Sto lavorando su un codice Python modellato sul server prefork MPM di Apache. Sono più un programmatore di applicazioni che un programmatore di rete e sono passati 10 anni da quando ho letto Stevens, quindi sto cercando di capire quanto è veloce il codice.accept() con socket condivisi tra più processi (basato sulla preforking di Apache)
Ho trovato una breve descrizione di how Apache's prefork code works, by Sander Temme.
Il processo padre, che in genere viene eseguito come root, si collega a un socket (in genere porta 80 o 443). Genera figli, che ereditano il descrittore di file aperto per il socket e cambiano uid e gid per l'utente e il gruppo senza privilegi . I bambini costruiscono un sondaggio dei descrittori di file listener (se c'è più di un ascoltatore) e verificano l'attività su di essi. Se viene rilevata un'attività, il bambino chiama accept() sul socket attivo e gestisce la connessione. Quando è fatto con quello, torna a guardare il pollset (o il file listener descrittore).
Poiché più bambini sono attivi e hanno tutti ereditato lo stesso file descrittore di socket , essi guarderanno lo stesso pollset. Un mutex accettato consente a un solo bambino di guardare effettivamente il sondaggio, e una volta che ha trovato un socket attivo, sbloccherà il mutex in modo che il bambino successivo possa iniziare a guardare il sondaggio. Se è presente un solo listener , quello accetta il mutex non viene utilizzato e tutti i child si bloccheranno in accept().
Questo è il modo in cui funziona il codice che sto guardando, ma non capisco alcune cose.
1) Qual è la differenza tra un "bambino" e un "ascoltatore"? Ho pensato che ogni bambino fosse un ascoltatore, il che è vero per il codice che sto guardando, ma nella descrizione di Temme possono esserci "un singolo ascoltatore" e "bambini". Quando un bambino avrebbe più ascoltatori?
2) (correlato a 1) È un mutex per processo o un mutex di sistema? Del resto, perché avere un mutex? Non accetta (2) il proprio mutex su tutti gli ascoltatori? La mia ricerca dice che ho bisogno di un mutex e che il mutex deve attraversare l'intero sistema. (Gregge, semafori, ecc)
Temme continua a dire:
bambini registrare in un'area di memoria condivisa (il quadro di valutazione) quando durano servito una richiesta. I bambini inattivi possono essere essere ucciso dal processo padre a soddisfare MaxSpareServers. Se un numero troppo basso di bambini è inattivo, il genitore eseguirà la generazione dei figli per soddisfare MinSpareServers.
3) Esiste un buon codice di riferimento per questa implementazione (preferibilmente in Python)? Ho trovato Perl's Net::Server::Prefork, che utilizza pipe invece di memoria condivisa per il tabellone. Ho trovato un articolo di Randal Schwartz che fa solo il preforking ma non fa il quadro di valutazione.
Il pre-fork example from the Perl Cookbook non ha alcun tipo di blocco intorno a selezionare, e Chris Siebenmann's Python example dice che è basato su Apache, ma utilizza i socket appaiati per il quadro di valutazione, la memoria non condivisa, e utilizzare le prese per i controlli, includere il controllo per un dato bambino 'accettare. Questo non corrisponde affatto alla descrizione di Apache.
Stai usando qualcosa come 'mod_wsgi' come interfaccia tra Apache e Python? Se è così, dovrebbe gestire tutto questo per te. –
Questo è per un server WSGI di prefigurazione Python puro. Il mio cliente desidera una soluzione leggera per i luoghi che non desiderano Apache e mod_wsgi o equivalenti. L'unico server WSGI che ho trovato in Python era Spawning e richiede l'eventlet. ... Anche se ora ho scoperto che il flup ha un'implementazione come quella di Siebenmann che usa pipe per il tabellone segnaposto invece di memoria condivisa e con una licenza accettabile per il mio cliente. –