La implementación del kernel epoll del archivo LINUX IO

Introducción

Linux tiene tres modelos comunes de programación de E / S multiplexados: select / poll / epoll. Entre ellos, desde la incorporación del kernel 2.6, muchos servidores han implementado epoll como E / S de red debido a su alto rendimiento y alta concurrencia. Hoy miré el código fuente de Linux y compartí su proceso de implementación con todos.

1 descripción general de epoll

Como se muestra abajo. El modelo de datos de todo el epoll se divide principalmente en tres partes. En primer lugar, todo es la idea de un archivo. De manera similar, nuestro objeto epoll también es un archivo abstracto, por lo que hay una estructura de archivo ; los datos privados del archivo abstracto apuntan a una estructura eventpoll , que es el propio objeto epoll. , y el nivel de usuario es solo archivo Corresponde al ID de archivo del proceso; luego eventpoll agrega la estructura del epitem a través del árbol rbr rojo-negro y la lista enlazada rdlist . Esta estructura describe los eventos de interés para cada archivo asociado con el epoll y, por supuesto, también incluye la entidad del núcleo para la notificación de eventos.
Inserte la descripción de la imagen aquí

1.1 estructura de archivos

Atributos claves

  • private_data: datos privados. Señalar eventpoll;
  • f_op: Interfaz de operación de archivos, el lenguaje C simula la tabla de funciones virtuales C ++ para lograr características polimórficas. Cuando se usa como epoll, apunta a la variable eventpoll_fops.

1.2 estructura eventpoll

Atributos claves

  • poll_wait: cola de encuesta en espera. Dado que epoll también es un archivo, se puede asociar con otro epoll para monitorear eventos en él, y en este momento, se unirá a la cola de espera para monitorear;
  • wq: cola de espera normal. Cuando llamamos a la función epoll_wait, si no ocurre ningún evento, seremos agregados a la cola para esperar;
  • rbr: Un árbol rojo-negro que almacena los eventos de interés de los archivos asociados. Realmente apunte a la estructura del epitem, debido a que tiene el campo rbn de tipo struct rb_root, se puede combinar en un árbol rojo-negro;
  • rdllist: almacena la lista vinculada de eventos de E / S que se han producido. En realidad, apunta a la estructura del epitem, porque tiene un campo rdllink de tipo struct list_head que se puede combinar en una lista vinculada;
  • ovflist: también es una lista enlazada de eventos IO que han ocurrido. Porque cuando una tarea se despierta cuando ocurre un evento en la rdlist, el proceso de copiar el evento al modo de usuario de la tarea necesita bloquear la estructura de la epoll, por lo que cuando ocurre un evento en este momento, se agregará temporalmente a la lista. lista enlazada de seguridad sin candados. en.

1.3 estructura del epitem

Atributos claves

  • rbn: nodo de árbol rojo-negro. Utilice este epitem de campo como un nodo de árbol rojo-negro;
  • rdllink: nodo de lista enlazada. A través del epitem de campo como nodo de lista enlazada, no se elaborará la conversión entre el nodo de lista enlazada y el epitem El resumen simple consiste en calcular el desplazamiento entre el campo y la primera dirección de la estructura durante el tiempo de compilación;
  • ep: propietario del objeto eventpoll.
  • ffd: registra los archivos asociados.
  • evento: Grabe el evento correspondiente. Si está registrado en la rbr de epoll, este valor indica el evento de interés. Si está registrado en rdlist de epoll, el valor representa el evento IO que ocurrió;
  • fllink: inserta el elemento de la lista vinculada del archivo asociado. Este campo está conectado al campo f_ep_links del archivo de monitoreo ffd.file, y la notificación del evento se volverá a llamar cuando ocurra un evento.

2 tiempo de ejecución

2.1 inicialización de epoll

Durante el proceso de inicialización del kernel, se inicializará epoll. El proceso clave es crear dos cachés de objetos de losa. El proceso general es el siguiente:
Inserte la descripción de la imagen aquí

2.2 Crear un objeto epoll

Cuando llamamos a la función epoll_create1, el flujo del kernel es el siguiente:
Inserte la descripción de la imagen aquí

  • Primero, el proceso del modo de usuario llama a epoll_create1;
  • Luego, después del proceso de llamada al sistema, finalmente se ejecuta en la función de modo kernel SYSCALL_DEFINE1 (epoll_create1, int, flags);
  • Ejecute ep_alloc, y finalmente asigne un objeto eventpoll a través de slab;
  • Devuelve un número de archivo no utilizado a través de get_unused_fd_flags;
  • Cree un objeto de estructura de archivo struct a través de anon_inode_getfile y configúrelo en el objeto eventpoll;
  • Agregue el número de archivo y el objeto de estructura de archivo struct a la tabla de archivos abierta actualmente;
  • Finalmente, el número de archivo se devuelve al modo de usuario a través del modo kernel.

2.3 Asociar / modificar / cancelar eventos de E / S de archivos

Cuando llamamos a la función epoll_ctl, el flujo del kernel es el siguiente:
Inserte la descripción de la imagen aquí

  • Primero, el proceso del modo de usuario llama a epoll_ctl;
  • Luego, después del proceso de llamada al sistema, finalmente se ejecuta en la función de modo kernel SYSCALL_DEFINE4 (epoll_ctl, int, epfd, int, op, int, fd, struct epoll_event __user *, event);
  • Compruebe la serie de parámetros, por supuesto, incluida la copia del evento del modo de usuario al modo de kernel;
  • Llamar a ep_find para consultar el epitem existente es en realidad consultar en el árbol rojo-negro, y su complejidad de tiempo es O (㏒₂N);
  • Si op == EPOLL_CTL_ADD, ejecute la función ep_insert. En este momento, ep_find debe devolver un puntero nulo;
  • Si op == EPOLL_CTL_DEL, ejecute la función ep_remove;
  • Si op == EPOLL_CTL_MOD, ejecute la función ep_modify.

2.3.1 Eventos de E / S de archivos asociados

Para los eventos de E / S de archivos recién agregados de interés, el flujo del núcleo es el siguiente:
Inserte la descripción de la imagen aquí

  • Después de ingresar ep_insert, primero construya un objeto struct epitem epi a través de slab e inicialice epi;
  • Inicializar la estructura struct ep_pqueue epq;
  • Llame a la función list_add_tail_rcu para agregar epi a la lista enlazada tfile-> f_ep_links del archivo de monitoreo;
  • Dado que puede haber ocurrido un evento en el archivo que se va a insertar actualmente, es necesario llamar a la función ep_item_poll (epi, & epq.pt). Especialmente en el modo de disparo por flanco, si no se realiza el procesamiento de eventos subsiguientes y no hay disparador de eventos subsiguientes, nunca se procesará. De hecho, es el mismo problema si el no-flanco no realiza el procesamiento de eventos subsiguientes;
  • Agregue epi al árbol rojo-negro del objeto eventpoll a través de la función ep_rbtree_insert;
  • Si ocurre un evento antes de que se inserte ep_rbtree_insert, primero agregue el evento al rdllist de eventpoll y luego active la tarea en la cola de espera wq del objeto eventpoll. Además, si el epoll actual es supervisado por otro epoll, se despierta la tarea en la cola de espera poll_wait.

Nota: modificar / cancelar el evento de E / S del archivo combinado con la vista del código fuente, no se introducirá por separado. . .

2.4 Esperando el evento IO del archivo asociado

Cuando llamamos a la función epoll_wait, el flujo del kernel es el siguiente:
Inserte la descripción de la imagen aquí

  • Primero, el proceso del modo de usuario llama a epoll_wait;
  • Luego, después del proceso de llamada al sistema, finalmente se ejecuta en la función de modo kernel SYSCALL_DEFINE4 (epoll_wait, int, epfd, struct epoll_event __user *, events, int, maxevents, int, timeout);
  • Utilice la función fdget para convertir el objeto del archivo epoll en una estructura struc fd, que contiene la estructura del archivo struct, y lea el campo private_data y asígnelo a la variable ep;
  • Luego ingrese la función ep_poll;
  • En la función ep_poll, la primera es determinar si ha ocurrido un evento, si no ocurre ningún evento, se agregará a la wq de eventpoll para esperar a dormir. Después de ser despertado, cuando ep-> rdllist no está vacío, agotado o interrumpido por una señal, se realizarán operaciones posteriores, de lo contrario continuará durmiendo y esperando.
  • En este paso, el paso anterior determina que se ha producido un evento o se ha despertado para salir del bucle, y luego llama a la función ep_send_events. Luego llame a la función ep_scan_ready_list en ep_send_events;
  • En ep_scan_ready_list, primero copie ep-> rdllist a txtlist y bórrelo;
  • Luego llame a la función apuntada por el puntero de función sproc, sproc apunta a la función ep_send_events_proc, que copia los eventos de txtlist en el espacio del usuario, especialmente para el modo de disparo horizontal, necesita ser copiado a la lista enlazada rdlist de eventpoll;
  • Cuando el paso anterior está en progreso, es posible que el IO subyacente ya haya tenido un evento sucediendo nuevamente, y debido a que se ha bloqueado, es imposible acceder a la rdlist de la estructura eventpoll. El IO subyacente se agregará temporalmente a la lista vinculada ovflist de la estructura eventpoll, por lo que debe copiarse en rdlist en este momento;
  • Finalmente, si hay un evento en la estructura de eventpoll rdlist, se activarán otras tareas en la cola de espera de eventpoll wq. Además, si el epoll actual es monitoreado por otro epoll, la cola de espera en poll_wait debe despertarse.

3 interpretación del código fuente

El diagrama de secuencia de llamadas anterior es lo suficientemente detallado como para ser entendido haciendo referencia al código fuente, ¡no publicaré el código fuente para repetir la interpretación! Recordatorio especial, preste atención al mecanismo de sincronización de bloqueo de estado del kernel
Nota: La versión del código fuente del kernel de Linux a la que se hace referencia en este artículo es 4.0.

Supongo que te gusta

Origin blog.csdn.net/fs3296/article/details/104617708
Recomendado
Clasificación