Red de conocimiento informático - Aprendizaje de programación - ¿Cuáles son el marco estructural y los principios de funcionamiento del controlador de Linux?

¿Cuáles son el marco estructural y los principios de funcionamiento del controlador de Linux?

1. El concepto de controlador de dispositivo Linux \x0d\\x0d\ La llamada al sistema es la interfaz entre el núcleo del sistema operativo y el programa de aplicación, y el controlador de dispositivo es la interfaz entre el núcleo del sistema operativo y el hardware de la máquina. El controlador del dispositivo enmascara los detalles del hardware del programa de aplicación. De esta manera, desde la perspectiva del programa de aplicación, el dispositivo de hardware es solo un archivo de dispositivo y el programa de aplicación puede operar el dispositivo de hardware como un archivo normal. El controlador del dispositivo es parte del kernel. Completa las siguientes funciones:\x0d\\x0d\ 1. Inicializa y libera el dispositivo \x0d\\x0d\ 2.Transfiere datos del kernel al hardware y lee datos del; hardware; \x0d\\x0d\ 3. Leer los datos enviados por la aplicación al archivo del dispositivo y devolver los datos solicitados por la aplicación \x0d\\x0d\ 4. Detectar y manejar errores en el dispositivo; \x0d\\x0d\ Hay tres tipos principales de archivos de dispositivo en el sistema operativo Linux: uno es un dispositivo de caracteres, el otro es un dispositivo de bloque y el tercero es un dispositivo de red. La principal diferencia entre un dispositivo de caracteres y un dispositivo de bloque es que cuando se realiza una solicitud de lectura/escritura a un dispositivo de caracteres, la E/S del hardware real generalmente ocurre inmediatamente. Este no es el caso con un dispositivo de bloque. memoria del sistema como búfer Cuando el usuario Si la solicitud del dispositivo del proceso puede cumplir con los requisitos del usuario, se devolverán los datos solicitados. De lo contrario, se llamará a la función de solicitud para realizar la operación de E/S real. Los dispositivos de bloque están diseñados principalmente para dispositivos lentos, como discos, para evitar perder demasiado tiempo de CPU esperando. \x0d\\x0d\ Como se mencionó, el proceso de usuario se ocupa del hardware real a través de archivos de dispositivo. Cada archivo de dispositivo tiene su atributo de archivo (c/b), que indica si es un dispositivo de caracteres o un dispositivo de bloque. Además, cada archivo tiene dos números de dispositivo, el primero es el número de dispositivo principal, que identifica al controlador, y el segundo. Es el número de dispositivo menor, que identifica diferentes dispositivos de hardware que usan el mismo controlador de dispositivo. Por ejemplo, si hay dos disquetes, puede usar el número de dispositivo menor para distinguirlos. El número de dispositivo principal del archivo del dispositivo debe ser coherente con el número de dispositivo principal solicitado por el controlador del dispositivo al registrarse; de ​​lo contrario, el proceso del usuario no podrá acceder al controlador. \x0d\\x0d\ Lo último que hay que mencionar es que cuando el proceso del usuario llama al controlador, el sistema ingresa al estado central y ya no es una programación preventiva. En otras palabras, el sistema debe regresar de la subfunción del conductor antes de poder realizar otro trabajo. Si su controlador se atasca en un bucle infinito, desafortunadamente todo lo que tiene que hacer es reiniciar la máquina y luego realizar un fsck prolongado. \x0d\\x0d\ 2. Análisis de ejemplo\x0d\\x0d\ Escribamos el controlador de dispositivo de caracteres más simple. Aunque no hace nada, le permite comprender cómo funcionan los controladores de dispositivos Linux. Ingrese el siguiente código C en su máquina y obtendrá un controlador de dispositivo real.

\x0d\\x0d\ Dado que el proceso de usuario maneja el hardware a través del archivo del dispositivo, el método de operación del archivo del dispositivo no es más que algunas llamadas al sistema, como abrir, leer, escribir, cerrar?, nota, no fopen, fread, pero ¿cómo asociar llamadas al sistema con controladores? Esto requiere comprender una estructura de datos muy clave:\x0d\\x0d\ STRuct file_operatiONs {\x0d\\x0d\ int (*seek) (struct inode *, struct file *, off_t , int);\x0d\\x0d\ int (*leer) (struct inode *, struct file *, char, int);\x0d\\x0d\ int (*write) (struct inode *, struct file *, off_t , int);\x0d\\x0d\ int (*readdir) (struct inode *, struct file *, struct dirent *, int);\x0d\\x0d\ int (*select) (struct inode *, struct file * , int , select_table *);\x0d\\x0d\ int (*ioctl) (struct inode *, struct file *, unsined int, unsigned long);\x0d\\x0d\ int (*mmap) (struct inode * , archivo de estructura *, estructura vm_area_struct *); int (*open) (inodo de estructura *, archivo de estructura *); int (*liberación) (inodo de estructura *, archivo de estructura *) ;\x0d\\x0d\ int (*fsync ) (struct inode *, struct file *);\x0d\\x0d\ int (*fasync) (struct inode *, struct file *, int);\x0d\\x0d \int (*check_media_change) (struct inode *, struct file *);\x0d\x0d\ int (*revalidate) (dev_t dev); \x0d\x0d\}\x0d\x0d\ Cada una de esta estructura El nombre de cada miembro corresponde a una llamada al sistema. Cuando el proceso de usuario utiliza llamadas al sistema para realizar operaciones como lectura/escritura en el archivo del dispositivo, la llamada al sistema encuentra el controlador de dispositivo correspondiente a través del número de dispositivo principal del archivo del dispositivo, luego lee el puntero de función correspondiente de esta estructura de datos y luego transfiere el control a esta función. Este es el principio básico de cómo funcionan los controladores de dispositivos Linux. Dado que este es el caso, el trabajo principal al escribir un controlador de dispositivo es escribir subfunciones y completar los diversos campos de file_operatives. \x0d\\x0d\ Comencemos a escribir la subrutina.

\x0d\\x0d\ #include definición de tipo básico\x0d\\x0d\ #include archivos de encabezado relacionados con el uso del sistema de archivos\x0d\\x0d\ #include \x0d\\x0d\ #include \x0d\\x0d\ # include \ x0d\\x0d\ unsigned int test_major = 0;\x0d\\x0d\ static int read_test(struct inode *inode, struct file *file, char *buf, int count)\x0d\\x0d\ {\x0d\ \x0d \ int left; Espacio de usuario y espacio del kernel\x0d\\x0d\ if (verify_area(VERIFY_WRITE, buf, count) == -EFAULT )\x0d\\x0d\ return -EFAULT;\x0d\\x0d\ for( left = contar; izquierda gt; 0; izquierda--)\x0d\\x0d\ {\x0d\\x0d\ __put_user(1,buf,1);\x0d\\x0d\ buf;\x0d\\x0d\ } \x0d \\x0d\ recuento de retornos;\x0d\\x0d\ }\x0d\\x0d\ Esta función está preparada para llamadas de lectura. Cuando se llama a read, se llama a read_test(), que escribe todos los unos en el búfer del usuario. buf es un parámetro de la llamada de lectura. Es una dirección en el espacio de proceso del usuario. Pero cuando se llama a read_test, el sistema ingresa al estado central. Por lo tanto, no se puede utilizar la dirección buf y se debe utilizar __put_user(). Esta es una función proporcionada por el kernel para transmitir datos al usuario. También hay muchas funciones con funciones similares. Tenga en cuenta que antes de copiar datos al espacio del usuario, debe verificar si buf está disponible. Esto utiliza la función verificar_area. Para verificar si se puede utilizar BUF. \x0d\\x0d\ static int write_test(struct inode *inode, struct file *file, const char *buf, int count)\x0d\\x0d\ {\x0d\\x0d\ return count;\x0d\\x0d\ }\x0d\\x0d\ static int open_test(struct inode *inode, struct file *file)\x0d\\x0d\ {\x0d\\x0d\ MOD_INC_USE_COUNT Se agrega el recuento de módulos, lo que indica que el kernel actual tiene un dispositivo; cargado en el kernel \x0d\\x0d\ Return 0;\x0d\\x0d\ }\x0d\\x0d\ static void release_test(struct inode *inode, struct file *file )\x0d\\x0d\ {\x0d\ \x0d\ ;\x0d\\x0d\ }\x0d\\x0d\ Todas estas funciones no son operativas. Las llamadas reales no hacen nada cuando ocurren, solo proporcionan punteros de función a las estructuras subyacentes.

\x0d\\x0d\ struct file_operations test_fops = {?\x0d\\x0d\ read_test,\x0d\\x0d\ write_test,\x0d\\x0d\ open_test,\x0d\\x0d\ release_test,\x0d\\x0d\ };\x0d\\x0d\ Se puede decir que el cuerpo principal del controlador del dispositivo está escrito. Ahora necesitamos integrar el controlador en el kernel. Los controladores se pueden compilar de dos maneras. Una es compilarlo en el kernel y la otra es compilarlo en módulos. Si lo compila en el kernel, aumentará el tamaño del kernel, cambiará el archivo fuente del kernel y no se podrá desinstalar dinámicamente. lo cual no favorece la depuración, por lo que se recomienda utilizar el método del módulo. \x0d\\x0d\ int init_module(void)\x0d\\x0d\ {\x0d\\x0d\ int resultado;\x0d\\x0d\ result = Register_chrdev(0, "test", amp; test_fops); La interfaz completa de la operación\x0d\\x0d\ if (resultado \x0d\\x0d\ #include \x0d\\x0d\ x0d\ {\x0d\\x0d\ int testdev;\x0d\\x0d\ int i; \x0d\\x0d\ char buf[10];\x0d\\x0d\ testdev = open("/dev/test", O_RDWR);\x0d\\x0d\ if ( testdev == -1 )\x0d\\ x0d\ {\x0d\\x0d\ printf("No se puede abrir el archivo \n");\x0d\\x0d\ exit (0);\x0d\\x0d\ }\x0d\\x0d\ read(testdev, buf,10);\x0d\\x0d\ for (i = 0; i