Código fuente del sistema de monitoreo de ubicación
Los eventos pueden ser una colección de las siguientes macros:
Ventajas de epoll sobre selección/sondeo:
El código del núcleo relacionado con epoll está en fs/eventpoll archivo .c. Aquí analizamos la implementación de las tres funciones epoll_create, epoll_ctl y epoll_wait en el kernel respectivamente. El código fuente del kernel de Linux utilizado en el análisis es la versión 4.1.2.
Epoll_create se usa para crear un identificador para Epoll. Se implementa en el sistema del kernel de la siguiente manera:
sys_epoll_create:
Se puede ver que cuando lo hacemos. Llame a epoll_create, el parámetro de tamaño entrante solo se usa para determinar si es menor o igual a 0 y no tiene otro uso.
La función completa solo tiene tres líneas de código y el trabajo real aún se ubica en la función sys_epoll_create1.
sys_epoll_create-& gt;sys_epoll_create1:
El proceso funcional de sys_epoll_create1 es el siguiente:
sys_epoll_create-& gt;sys_epoll _ create 1->ep_alloc :
sys_epoll_create->sys_epoll_create 1->EP_alloc->get_unused_fd_flags:
En el kernel de Linux, current es una macro que devuelve una variable de la estructura task_struct (llamamos (es un descriptor de proceso). Esta variable representa el proceso actual. Los recursos de archivo abiertos por el proceso se almacenan en el miembro de archivos del descriptor de proceso, por lo que actual->;archivo devuelve los recursos de archivo abiertos por el proceso actual. La función rlimit(RLIMIT_NOFILE) obtiene el número máximo de descriptores de archivos que puede abrir el proceso actual. Este valor se puede configurar y el valor predeterminado es 1024.
Recomendaciones de videos relacionados:
Epoll, la piedra angular subyacente que respalda io de mil millones de niveles, revela la verdad en el combate real.
Principios de red tcp/udp, programación de red epoll/reactor, "ensayo de ocho partes" serio durante la entrevista.
Dirección de aprendizaje: Desarrollo de servidores Linux C/C++/arquitecto backend educación de voz cero-video tutorial de aprendizaje-Tencent Classroom
Necesitamos más arquitectos de servidores Linux C/C++ para aprender Grupo de información 812855908 ( La información incluye C/C++, Linux, tecnología golang, Nginx, ZeroMQ, MySQL, Redis, fastdfs, MongoDB, ZK, streaming media, CDN, P2P, K8S, Docker, TCP/IP, etc.
< El trabajo de p>__alloc_fd es asignar un descriptor de archivo disponible al proceso entre [inicio, fin] (nota: aquí el inicio es 0, el final es el número máximo de descriptores de archivo que el proceso puede abrir), no entraré en detalles aquí. .El código es el siguiente:
sys_epoll_create->sys_epoll_create->EP_alloc->get_unused_FD_flags->__alloc_fd:
Luego, epoll_create1 llamará a anon_inode_getfile para crear la estructura del archivo como sigue:
sys_epoll_create->sys_epoll_create 1->Nodo de información anónima Obtenga el archivo:
La función anon_inode_getfile primero asignará una estructura de archivo y una estructura de dentry, y luego conectará el Estructura de archivos con un nodo de inodo anónimo anon_inode_inode. Cabe señalar que al llamar a la función anon_inode_getfile para solicitar una estructura de archivo, la variable ep de la estructura eventpoll aplicada anteriormente, el archivo aplicado -->Private_data apuntará a esta variable ep, y al mismo tiempo después de la función anon_inode_getfile devuelve, EP->Archivo apuntará a la variable de estructura de archivo a la que se aplica esta función.
Hablemos brevemente sobre file/dentry/inode. Cuando un proceso abre un archivo, el kernel asignará una estructura de archivo al proceso, lo que indica que el archivo abierto está en el contexto del proceso, y luego la aplicación pasará un descriptor de archivo int para acceder a esta estructura. De hecho, el proceso del kernel mantiene una matriz de estructuras de archivos y el descriptor de archivo es el subíndice de la estructura de archivo correspondiente en la matriz.
La estructura Dentry (llamada "entrada de directorio") registra varios atributos del archivo, como el nombre del archivo, los permisos de acceso, etc. Cada archivo tiene solo una estructura de dentry, y luego un proceso puede abrir un archivo varias veces y varios procesos también pueden abrir el mismo archivo. En estos casos, el kernel asignará múltiples estructuras de archivos y establecerá múltiples contextos de archivos. Sin embargo, para el mismo archivo, no importa cuántas veces se abra, el kernel solo asignará un dentry al archivo. Por lo tanto, existe una relación de muchos a uno entre la estructura del archivo y la estructura de entrada.
Al mismo tiempo, cada archivo no solo tiene una estructura de entrada del directorio dentry, sino también una estructura de inodo del índice. nodo, que registra la ubicación del archivo en el medio de almacenamiento. Debido a la ubicación y distribución, solo se asigna un inodo por archivo en el kernel. Dentry e inode describen diferentes objetivos. Un archivo puede tener varios nombres de archivo (como archivos de enlace) y el acceso al mismo archivo a través de diferentes nombres de archivo puede ser diferente. Los archivos Dentry representan archivos en un sentido lógico y registran sus atributos lógicos, mientras que las estructuras de inodo representan archivos en un sentido físico y registran sus atributos físicos. Existe una relación de muchos a uno entre las estructuras dentry y de inodo
sys_epoll_create->sys_epoll_create 1->fd_installation:
Resumen Veamos el papel de la función epoll_create: Después de llamar a epoll_create, asigne una estructura eventpoll y una estructura de archivo que represente el archivo epoll en el kernel, asocie las dos estructuras y devuelva un descriptor de archivo epoll fd que también esté asociado con la estructura del archivo. Cuando una aplicación opera epoll, necesita pasar un descriptor de archivo epoll fd. De acuerdo con este fd, el kernel puede encontrar la estructura de archivos de epoll y luego usar este archivo para obtener la variable de estructura de eventpoll solicitada por epoll_create. Toda la información importante relacionada con epoll se almacena en esta estructura. A continuación, todas las operaciones de la función de interfaz epoll se realizan en la variable de estructura eventpoll.
Entonces, la función de epoll_create es establecer un canal para el proceso desde el descriptor del archivo epoll hasta la variable de estructura eventpoll en el kernel.
La interfaz Epoll_ctl se utiliza para agregar/modificar/eliminar eventos de escucha de archivos.
El código del kernel es el siguiente:
sys_epoll_ctl:
Según la introducción de la interfaz epoll_ctl, op es la acción de la operación epoll (agregar/modificar/eliminar evento), ep_op_has_event( op) determina si no es una operación de eliminación. Si OP! = EPOLL_CTL_DEL es verdadero, entonces necesita llamar a la función copy_from_user para copiar el evento desde el espacio del usuario a la variable epds del kernel. Porque, solo para operaciones de eliminación, el kernel no necesita usar el evento pasado por el proceso.
Luego llame a fdget dos veces seguidas para obtener las variables de estructura de archivo del archivo epoll y el archivo monitoreado (en lo sucesivo, el archivo de destino) (nota: esta función devuelve la variable de estructura fd y la variable de estructura fd La estructura contiene la estructura del archivo).
El siguiente paso es comprobar los parámetros. En las siguientes situaciones, se puede considerar que hay un problema con los parámetros pasados y se devolverá un error directamente:
Por supuesto, si se trata de una operación de suma, todavía hay algún juicio sobre la acción operativa. No lo explicaré aquí, es relativamente simple y puedes verlo tú mismo.
En ep se mantiene un árbol rojo-negro. Cada vez que se agrega un evento registrado, se usa una variable con una estructura de epitem para representar el detector de eventos y luego se inserta en el árbol rojo-negro de ep. En epoll_ctl, se llama a la función ep_find para encontrar el elemento de monitoreo representado por el archivo de destino en el árbol rojo-negro de ep. El elemento de monitoreo devuelto puede estar vacío.
A continuación, el código en el área de cambio es el núcleo de toda la función epoll_ctl. Hay tres situaciones para cambiar de operación: agregar (EPOLL_CTL_ADD), eliminar (EPOLL_CTL_DEL) y modificar (EPOLL_CTL_MOD). Aquí tomaré la suma como ejemplo para ilustrar. Las otras dos situaciones son similares. Sé cómo agregar eventos de escucha, también se pueden extrapolar otros eventos de eliminación y modificación de escucha.
Al agregar eventos de monitoreo a un archivo de destino, primero debe asegurarse de que el archivo de destino no esté monitoreado en el ep actual. Si está presente (epi no está vacío), se devolverá el error -EEXIST. De lo contrario, los parámetros son normales, y luego los eventos de monitoreo POLLERR y POLLHUP del archivo de destino se configuran de manera predeterminada, y luego se llama a la función ep_insert para insertar los eventos de monitoreo del archivo de destino en el árbol rojo-negro mantenido por ep:
sys_epoll_CTL-& gt;ep_insert:
Como se mencionó anteriormente, el monitoreo del archivo de destino se mantiene mediante una variable de escucha con una estructura de epitem, por lo que en ep_insert función, primero se llama a la función kmem_cache_alloc para obtener el archivo de destino del asignador de losa. Asigne un escucha de estructura de epiitem y luego inicialice la estructura. No hay mucho que decir aquí. A continuación, veamos la llamada a la función ep_item_poll:
sys_epoll_CTL->EP_insert->ep_item_poll:
En la función ep_item_poll, llame a la función de encuesta del archivo de destino y apunte a diferentes funciones para diferentes archivos de destino. Si el archivo de destino es un socket, el sondeo apunta a sock_poll, y si el archivo de destino es un socket tcp, el sondeo es la función tcp_poll. Aunque las funciones señaladas por la encuesta pueden ser diferentes, sus funciones son las mismas, que es obtener los bits de evento generados actualmente por el archivo de destino y vincular los elementos de escucha al gancho de encuesta del archivo de destino (lo más importante es registrar la función de devolución de llamada de encuesta EP _PTABLE_QUEEN_PROC). Una vez completada esta operación, se llamará a la función de devolución de llamada EP_PTABLE_QUEEN_PROC cuando el archivo de destino genere un evento en el futuro.
A continuación, llame a list_add_tail_rcu para agregar el elemento de escucha actual a la lista vinculada f_ep_links del archivo de destino, que es la lista vinculada del gancho epoll del archivo de destino a la que se agregarán todos los elementos de escucha que monitorean el archivo de destino. la lista enlazada.
Luego llame a ep_rbtree_insert para agregar el oyente epi al árbol rojo-negro mantenido por ep.
No se da ninguna explicación aquí, el código es el siguiente:
sys_epoll_CTL->EP_insert->ep_rbtree_insert:
Como se mencionó anteriormente, ep_insert llama a ep_item_poll para obtener bits de evento generados por archivo de destino. Durante un período de tiempo antes de que se llame a epoll_ctl, puede haber eventos que los procesos relacionados deban monitorear. Si hay eventos monitoreados (Revents &:event-& gt; Events es verdadero) y el elemento de escucha relacionado con el archivo de destino no está vinculado a la lista de preparación del ep rdlist, el elemento de monitoreo se agrega a la lista de preparación del ep rdlist , rdlist Vincula los elementos de escucha para todos los archivos de destino listos monitoreados por el descriptor epoll. Además, si hay tareas esperando para generar eventos, llame a la función wake_up_locked para activar todas las tareas en espera y procesar los eventos correspondientes. Cuando un proceso llama a epoll_wait, aparece en la cola de espera wq de ep. A continuación, se explica la función epoll_wait.
Para resumir la función epoll_ctl: esta función se aplica a los elementos de monitoreo para el archivo de destino en función de los eventos monitoreados y cuelga los elementos de monitoreo en el árbol rojo-negro de la estructura eventpoll.
Epoll_wait espera eventos, el código del kernel es el siguiente:
sys_epoll_wait:
El primero es verificar algunos parámetros pasados por el proceso: p>
Después de verificar todos los parámetros, llame a la función ep_poll para el procesamiento real:
sys_epoll_wait->ep_poll:
Lo primero que hace ep_poll es procesar el tiempo de espera. Tiempo de espera El tiempo de espera está en milisegundos. Si el tiempo de espera es mayor que 0, significa que el período de tiempo de espera ha expirado. Si el tiempo de espera es igual a 0, la función no se bloqueará ni regresará directamente. Si es menor que 0, se bloqueará para siempre y no volverá hasta que ocurra un evento.
Cuando no se genera ningún evento ((!Ep_events_available(ep)) es verdadero), llame a la función __add_wait_queue_exclusive para agregar el proceso actual a EP->; Wq espera en la cola y luego en un infinito for; bucle, primero llame a set_current_state(task_interruptible) para configurar el proceso actual en el estado de suspensión interrumpible, y luego el proceso actual abandona la CPU y entra en suspensión. No se ejecutará hasta que otro proceso llame a wake_up o entre una señal de interrupción para despertar. el proceso. Siguiente código.
Si el proceso se activa, primero verifique si hay un evento, si se agota el tiempo de espera o si se activa mediante otras señales. Si ocurren estas situaciones, salga del bucle, elimine el proceso actual de EP->Wp de la cola de espera y establezca el proceso actual en el estado listo TASK_RUNNING.
Si hay un evento, llame a la función ep_send_events para transferir el evento al espacio del usuario.
sys_epoll_wait->EP_poll->ep_send_events:
Ep_send_events no funciona, pero el verdadero trabajo está en la función ep_scan_ready_list:
sys_epoll_wait->EP_poll- >EP_send_events->ep_scan_ready_list:
Ep_scan_ready_list primero vincula los datos en la lista lista de Ep a una lista tx global, luego borra la lista lista de Ep y también establece la lista ovflist del Ep en NULL. ovflist es una lista enlazada individualmente, que es una lista enlazada de respaldo que acepta eventos listos. Cuando el proceso del kernel copia eventos del kernel al espacio del usuario, el archivo de destino puede generar nuevos eventos durante este tiempo. En este momento, debe colocar la nueva cadena de tiempo en ovlist.
Luego, llame a la función de devolución de llamada del procedimiento almacenado (aquí se llamará a la función ep_send_events_proc) para copiar los datos del evento desde el kernel al espacio del usuario.
sys_epoll_wait->EP_poll->EP_send_events->EP_scan_ready_list->ep_send_events_proc:
< La función de devolución de llamada p>ep_send_events_proc se repite para obtener los datos del evento del elemento monitoreado. Para cada elemento monitoreado, llama a ep_item_poll para obtener eventos para el archivo de destino monitoreado. Si se obtiene el evento, llama a la función __put_user para copiar los datos al espacio del usuario.Volviendo a la función ep_scan_ready_list, se mencionó anteriormente que durante la ejecución de la función de devolución de llamada del procedimiento almacenado, el archivo de destino puede generar nuevos eventos vinculados a ovlist, por lo que después de la devolución de llamada, los eventos en ovlist es necesario agregarlo nuevamente. Agregado a la lista de eventos listos para rdllist.
Al mismo tiempo, al final, si rdlist no está vacío (lo que indica si hay un evento listo) y el proceso está esperando el evento, se llama a wake_up_locked para reactivar el proceso del kernel nuevamente. para manejar la llegada del evento (el proceso es el mismo que antes, es decir, el evento se copia al espacio del usuario).
En este punto, el proceso epoll_wait ha finalizado, pero hay un problema, es decir, el proceso mencionado anteriormente se suspenderá después de llamar a epoll_wait, pero ¿cuándo se despertará este proceso? Al llamar a epoll_ctl para registrar un elemento de escucha para el archivo de destino, registre una función de devolución de llamada EP_PTABLE_QUEEN_PROC para el elemento de escucha del archivo de destino. La función de devolución de llamada EP_PTABLE_QUEEN_PROC agrega el proceso a la lista de activación del archivo de destino y registra una función de devolución de llamada ep_poll_callbak. Cuando el archivo de destino genera un evento, la devolución de llamada ep_poll_callbak activará el proceso en la cola de espera.
Para resumir la función epoll: la función epoll_wait pondrá el proceso que la llama en estado de suspensión (excepto cuando el tiempo de espera es 0). Si hay un evento monitoreado, el proceso se activará y el evento se copiará del kernel al espacio del usuario y de regreso al proceso.