locking - accept() con sockets compartidos entre mĂșltiples procesos(basados en el entrenamiento previo de Apache)
multiprocessing preforking (1)
Estoy trabajando en un código de Python modelado en el servidor MPM Prefork de Apache. Soy más un programador de aplicaciones que un programador de red y han pasado 10 años desde que leí a Stevens, así que estoy tratando de ponerme al día para entender el código.
Encontré una breve descripción de cómo funciona el código de Prefork de Apache, por Sander Temme .
El proceso principal, que normalmente se ejecuta como root, se enlaza a un socket (generalmente el puerto 80 o 443). Genera hijos, que heredan el descriptor de archivo abierto para el socket, y cambian uid y gid al grupo y usuario sin privilegios. Los niños construyen un sondeo de los descriptores de archivos del oyente (si hay más de un oyente) y observan la actividad en ellos. Si se encuentra actividad, el hijo llama a accept () en el socket activo y maneja la conexión. Cuando haya terminado con eso, vuelve a ver el pollset (o el descriptor de archivo del oyente).
Dado que varios hijos están activos y todos heredaron el mismo descriptor (es) de archivo de socket, estarán observando el mismo pollset. Un mutex aceptado permite que solo un solo niño vea el sondeo, y una vez que haya encontrado un zócalo activo, desbloqueará el mutex para que el próximo niño pueda comenzar a observar el sondeo. Si solo hay un único oyente, esa aceptación mutex no se usa y todos los hijos se bloquearán en aceptar ().
Esta es prácticamente la forma en que funciona el código que estoy viendo, pero no entiendo algunas cosas.
1) ¿Cuál es la diferencia entre un "niño" y un "oyente"? Pensé que cada niño es un oyente, lo cual es cierto para el código que estoy viendo, pero en la descripción de Temme puede haber "un solo oyente" y "niños". ¿Cuándo tendría un niño múltiples oyentes?
2) (relacionado con 1) ¿Es esto una exclusión mutua por proceso o una exclusión mutua del sistema? Para el caso, ¿por qué tener un mutex? ¿No acepta (2) hacer su propio mutex en todos los oyentes? Mi investigación dice que sí necesito un mutex y que el mutex debe estar en todo el sistema. (rebaño, semáforo, etc.)
Temme continúa diciendo:
Los niños registran en un área de memoria compartida (el tablero de indicadores) la última vez que atendieron una solicitud. Los niños inactivos pueden ser asesinados por el proceso padre para satisfacer a MaxSpareServers. Si hay muy pocos niños inactivos, el padre engendrará hijos para satisfacer los servidores de MinSpareServers.
3) ¿Existe un buen código de referencia para esta implementación (preferiblemente en Python)? Encontré Net::Server::Prefork , que usa tuberías en lugar de memoria compartida para el marcador. Encontré un artículo de Randal Schwartz que solo hace el preformado pero no el marcador.
El ejemplo anterior a la bifurcación del libro de cocina de Perl no tiene ningún tipo de bloqueo alrededor de la selección, y el ejemplo de Chris Siebenmann en Python dice que está basado en Apache pero usa sockets pareados para el marcador, no memoria compartida, y usa los sockets para los controles, incluye el control para que un niño dado ''acepte''. Esto no coincide con la descripción de Apache en absoluto.
Con respecto a (1), el oyente es simplemente una referencia a la existencia de un socket para aceptar conexiones. Como Apache puede aceptar conexiones en múltiples sockets al mismo tiempo, por ejemplo, 80/443, entonces hay múltiples sockets de oyentes. Cada proceso secundario tendría que escuchar en todas estas tomas cuando llegue el momento. Como accept () solo se puede hacer en un socket a la vez, está precedido por el sondeo / selección, por lo que se sabe en qué socket del oyente se debe realizar la aceptación.
Con respecto a (2), es un mutex global o de proceso cruzado. Es decir, un proceso que lo bloquea bloqueará otros procesos que intenten adquirir el mismo bloqueo. Aunque accept () técnicamente serializará los procesos, la presencia de múltiples sockets de escucha significa que no puede confiar en eso, ya que no sabe de antemano en qué socket se debe aceptar. Incluso cuando se trata de un único socket de escucha, el motivo de la exclusión mutua de aceptación es que si hay un gran número de solicitudes de manejo de procesos, entonces podría ser bastante costoso si el sistema operativo activa todos los procesos para ver cuál de ellos tiene () que retorne. Dado que Apache en el modo prefork puede tener más de 100 procesos, eso podría causar un problema.
Por lo tanto, si solo tiene un único conector de escucha y sabe que solo tiene algunos procesos que desean realizar la llamada de aceptación (), posiblemente pueda eliminar el proceso mutuo de aceptación mutua.