Implementaci贸n de Epoll, parte 4

Este es el 煤ltimo de una serie de cuatro art铆culos ( Parte 1 , Parte 2 , Parte 3 ) sobre implementaci贸n epoll. Aqu铆 hablaremos sobre c贸mo epolltransfiere eventos desde el espacio del kernel al espacio del usuario, y c贸mo se implementan los modos de disparo de borde y nivel. Este art铆culo fue escrito m谩s tarde que los dem谩s. Cuando comenc茅 a trabajar en el primer material, el kernel de Linux estable m谩s reciente era 3.16.1. Y en el momento de escribir este art铆culo, esta ya es la versi贸n 4.1. Este art铆culo se basa en el c贸digo de esta versi贸n del kernel. Sin embargo, el c贸digo no ha cambiado mucho, por lo que los lectores de los art铆culos anteriores no deber铆an preocuparse por el hecho de que algo en la implementaci贸n haya cambiado mucho.







epoll



Interactuar con el espacio del usuario



En los art铆culos anteriores, dediqu茅 bastante tiempo a explicar c贸mo funciona el sistema de manejo de eventos en el kernel. Pero, como sabe, el kernel necesita pasar informaci贸n sobre eventos a un programa que se ejecuta en el espacio de usuario para que el programa utilice esta informaci贸n. Esto se hace principalmente con la llamada al sistema epoll_wait (2) .



El c贸digo para esta funci贸n se puede encontrar en la l铆nea 1961 del archivo fs/eventpoll.c. La funci贸n en s铆 es muy simple. Despu茅s de verificaciones bastante normales, simplemente obtiene el puntero eventpolldesde el descriptor de archivo y llama a la siguiente funci贸n:



error = ep_poll(ep, events, maxevents, timeout);


Funci贸n ep_poll ()



La funci贸n se ep_poll()declara en la l铆nea 1585 del mismo archivo. Comienza verificando si el usuario ha establecido un valor timeout. Si es as铆, la funci贸n inicializa la cola de espera y establece el tiempo de espera en el valor especificado por el usuario. Si el usuario no quiere esperar, es decir , timeout = 0, la funci贸n pasa inmediatamente al bloque de c贸digo con una etiqueta check_events:, que se encarga de copiar el evento.



Si el usuario ha especificado un valor timeout, y no hay eventos que se le puedan informar (su presencia se determina mediante una llamada ep_events_available(ep)), la funci贸n se ep_poll()agrega a la cola de espera ep->wq(recuerde lo que hablamos en el tercer art铆culo de esta serie). All铆 mencionamos que ep_poll_callback()en el proceso, activa cualquier proceso que est茅 esperando en la cola.ep->wq...



Luego, la funci贸n entra en espera llamando schedule_hrtimeout_range(). Estas son las circunstancias bajo las cuales un proceso "durmiente" puede "despertar":



  1. El tiempo de espera ha expirado.
  2. El proceso recibi贸 una se帽al.
  3. Ha surgido un nuevo evento.
  4. No pas贸 nada y el planificador simplemente decidi贸 activar el proceso.


En los escenarios 1, 2 y 3, la funci贸n establece los indicadores apropiados y sale del ciclo de espera. En el 煤ltimo caso, la funci贸n simplemente vuelve a entrar en modo de espera.



Una vez realizada esta parte del trabajo, ep_poll()contin煤a ejecutando el c贸digo de bloque check_events:.



En este bloque, primero se verifica la presencia de eventos, y luego se realiza la siguiente llamada, donde ocurre lo m谩s interesante.



ep_send_events(ep, events, maxevents)


Funci贸n ep_send_events()declarada en la l铆nea 1546. Es, despu茅s de la llamada, llama a la funci贸n ep_scan_ready_list(), pasando de una devoluci贸n de llamada, ep_send_events_proc(). La funci贸n ep_scan_ready_list()recorre la lista de descriptores de archivos listos y llama ep_send_events_proc()a cada evento listo que encuentra. A continuaci贸n, quedar谩 claro que se necesita un mecanismo que implique el uso de una devoluci贸n de llamada para garantizar la seguridad y la reutilizaci贸n del c贸digo.



La funci贸n ep_send_events()primero coloca datos de la lista de descriptores de archivo listos para usar de la estructura eventpoolen su variable local. Luego establece el campo de ovflistestructura eventpoolen NULL(y su valor predeterminado es EP_UNACTIVE_PTR).



驴Por qu茅 los autores epollutilizanovflist? 隆Esto se hace para garantizar una alta eficiencia epoll! Puede notar que despu茅s de que la lista de descriptores de archivos listos se ha tomado de la estructura eventpool, se ep_scan_ready_list()establece ovflisten NULL. Esto da como resultado que ep_poll_callback()no intente adjuntar el evento que se est谩 pasando al espacio de usuario ep->rdllist, lo que puede generar grandes problemas. Al usar la ovflistfunci贸n, no es ep_scan_ready_list()necesario mantener un candado ep->lockmientras se copian eventos en el espacio del usuario. Como resultado, se mejora el rendimiento general de la soluci贸n.



Despu茅s de eso, ep_send_events_proc()omitir谩 la lista de descriptores de archivos listos que tiene y volver谩 a llamar a sus m茅todos.poll()para asegurarse de que el evento realmente sucedi贸. 驴Por qu茅 epollvolver a consultar los eventos aqu铆? Esto se hace para asegurarse de que el evento (o eventos) registrado por el usuario todav铆a est茅 disponible. Considere una situaci贸n en la que se agreg贸 un descriptor de archivo a la lista de descriptores listos para archivo por evento EPOLLOUTmientras el programa de usuario est谩 escribiendo en ese descriptor. Una vez que el programa termina de escribir, es posible que ya no se pueda escribir en el descriptor de archivo. Epollnecesita manejar situaciones como esta correctamente. De lo contrario, el usuario recibir谩 EPOLLOUTen el momento en que se bloquee la operaci贸n de escritura.



Aqu铆, sin embargo, vale la pena mencionar un detalle. Funci贸nep_send_events_proc()hace todo lo posible para garantizar que los programas de espacio de usuario reciban notificaciones de eventos precisas. Es posible, aunque poco probable, que la disponibilidad de un conjunto de eventos cambie despu茅s del ep_send_events_proc()desencadenante poll(). En este caso, un programa de espacio de usuario puede recibir una notificaci贸n de un evento que ya no existe. Es por eso que se considera correcto usar siempre enchufes sin bloqueo cuando se aplica epoll. Esto evita que su aplicaci贸n se bloquee inesperadamente.



Despu茅s de verificar la m谩scara del evento, ep_send_events_proc()simplemente copia la estructura del evento en el b煤fer proporcionado por el programa de espacio de usuario.



Activado por flanco y activado por nivel



Ahora finalmente podemos discutir la diferencia entre Edge Triggering (ET) y Level Triggering (LT) en t茅rminos de su implementaci贸n.



else if (!(epi->event.events & EPOLLET)) {
    list_add_tail(&epi->rdllink, &ep->rdllist);
}


隆Es muy f谩cil! La funci贸n ep_send_events_proc()vuelve a agregar el evento a la lista de descriptores de archivos listos. Como resultado, en la pr贸xima llamada, se ep_poll()volver谩 a comprobar el mismo descriptor de archivo. Dado que ep_send_events_proc()siempre llama a un archivo poll()antes de devolverlo a la aplicaci贸n de espacio de usuario, esto aumenta ligeramente la sobrecarga del sistema (en comparaci贸n con ET) si el descriptor de archivo ya no est谩 disponible. Pero el objetivo de todo esto es, como se mencion贸 anteriormente, no informar eventos que ya no est谩n disponibles.



Una vez que ep_send_events_proc()termina de copiar eventos, la funci贸n devuelve el n煤mero de eventos copiados, manteniendo la aplicaci贸n de espacio de usuario actualizada.



Cuando la funci贸n ha ep_send_events_proc()terminado, las funcionesep_scan_ready_list()Necesito limpiar un poco. Primero, regresa a la lista de descriptores de archivos listos los eventos que la funci贸n dej贸 sin procesar ep_send_events_proc(). Esto puede suceder si el n煤mero de eventos disponibles excede el tama帽o del b煤fer proporcionado por el programa de usuario. Tambi茅n ep_send_events_proc()adjunta r谩pidamente todos los eventos ovflist, si los hay, a la lista de descriptores de archivos listos. Adem谩s, se ovflistregistra de nuevo EP_UNACTIVE_PTR. Como resultado, los nuevos eventos se adjuntar谩n a la lista de espera principal ( rdllist). La funci贸n sale activando cualquier otro proceso "durmiente" en caso de que haya otros eventos disponibles.



Salir



Con esto concluye el cuarto y 煤ltimo art铆culo de la serie de implementaci贸n epoll. Mientras escribo estos art铆culos, me ha impresionado el tremendo trabajo mental que han realizado los autores del c贸digo del kernel de Linux para lograr la m谩xima eficiencia y escalabilidad. Y agradezco a todos los autores de c贸digo Linux por compartir su conocimiento con todos los que lo necesitan al compartir los resultados de su trabajo.



驴Qu茅 opina del software de c贸digo abierto?










All Articles