J'essaie de comprendre les moindres détails des serveurs web. Je me demande si un serveur, par exemple Apache, est continuellement à la recherche de nouvelles requêtes ou s'il fonctionne par une sorte de système d'interruption. Si c'est un système d'interruption, qu'est-ce qui déclenche l'interruption, est-ce le pilote de la carte réseau ?
Réponses
Trop de publicités?La réponse courte est : s
-
Le serveur crée un socket d'écoute puis se bloque en attendant de nouvelles connexions. Pendant ce temps, le noyau place le processus dans un état d'attente. sommeil interruptible et exécute d'autres processus. Il s'agit d'un point important : le fait que le processus interroge continuellement le système gaspillerait le CPU. Le noyau est capable d'utiliser les ressources du système plus efficacement en bloquant le processus jusqu'à ce qu'il ait du travail à faire.
-
Lorsque de nouvelles données arrivent sur le réseau, la carte réseau émet une interruption.
-
Voyant qu'il y a une interruption de la carte réseau, le noyau, via le pilote de la carte réseau, lit les nouvelles données de la carte réseau et les stocke en mémoire. (
-
Le noyau traite les données nouvellement arrivées et les associe à une socket. Un processus qui bloque sur cette socket sera marqué runnable, ce qui signifie qu'il peut maintenant s'exécuter. Il ne s'exécute pas nécessairement immédiatement (le noyau peut décider d'exécuter encore d'autres processus).
-
À son gré, le noyau réveillera le processus du serveur web bloqué. (Puisqu'il est maintenant exécutable.)
-
Le processus du serveur web continue à s'exécuter comme si rien ne s'était passé. Son appel système bloquant revient et il traite toutes les nouvelles données. Ensuite... allez à l'étape 1.
Il y a beaucoup de détails "inférieurs".
Tout d'abord, considérons que le noyau possède une liste de processus, et qu'à tout moment, certains de ces processus sont en cours d'exécution, et d'autres non. Le noyau accorde à chaque processus en cours d'exécution une tranche de temps CPU, puis l'interrompt et passe au suivant. S'il n'y a pas de processus en cours d'exécution, le noyau émettra probablement une instruction du type HLT à l'UC qui suspend l'UC jusqu'à ce qu'il y ait une interruption matérielle.
Quelque part dans le serveur, il y a un appel système qui dit "donnez-moi quelque chose à faire". Il y a deux grandes catégories de façons de faire cela. Dans le cas d'Apache, cela appelle accept
sur un socket qu'Apache a précédemment ouvert, écoutant probablement sur le port 80. Le noyau maintient une file d'attente des tentatives de connexion, et ajoute à cette file d'attente chaque fois qu'une tentative de connexion est lancée. TCP SYN est reçu. La façon dont le noyau sait qu'un TCP SYN a été reçu dépend du pilote du périphérique ; pour de nombreuses cartes réseau, il y a probablement une interruption matérielle lorsque des données réseau sont reçues.
accept
demande au noyau de me renvoyer la prochaine initiation de connexion. Si la file d'attente n'était pas vide, alors accept
revient immédiatement. Si la file d'attente est vide, le processus (Apache) est supprimé de la liste des processus en cours d'exécution. Lorsqu'une connexion est établie ultérieurement, le processus est repris. Ce processus est appelé "bloquant", car il s'agit d'un appel au processus, accept()
ressemble à une fonction qui ne revient pas avant d'avoir obtenu un résultat, ce qui peut être dans un certain temps. Pendant ce temps, le processus ne peut rien faire d'autre.
Une fois accept
retourne, Apache sait que quelqu'un tente d'initier une connexion. Il appelle alors fourchette pour diviser le processus Apache en deux processus identiques. L'un de ces processus poursuit le traitement de la requête HTTP, l'autre appelle accept
à nouveau pour obtenir la connexion suivante. Ainsi, il y a toujours un processus maître qui ne fait rien d'autre que d'appeler accept
et génère des sous-processus, et ensuite il y a un sous-processus pour chaque demande.
Il s'agit d'une simplification : il est possible de faire cela avec des threads au lieu de processus, et il est également possible de fork
afin qu'il y ait un processus de travail prêt à fonctionner lorsqu'une requête est reçue, réduisant ainsi les frais de démarrage. Selon la façon dont Apache est configuré, il peut faire l'une ou l'autre de ces choses.
C'est la première grande catégorie de comment le faire, et elle s'appelle blocage IO parce que les appels système comme accept
y read
y write
qui opèrent sur des sockets vont suspendre le processus jusqu'à ce qu'ils aient quelque chose à retourner.
L'autre façon de procéder est appelée non-blocage ou basée sur des événements ou encore IO asynchrone . Ceci est mis en œuvre avec des appels système comme select
o epoll
. Ils font tous la même chose : vous leur donnez une liste de sockets (ou en général, de descripteurs de fichiers) et ce que vous voulez en faire, et le noyau bloque jusqu'à ce qu'il soit prêt à faire une de ces choses.
Avec ce modèle, vous pourriez dire au noyau (avec epoll
), "Dites-moi quand il y a une nouvelle connexion sur le port 80 ou de nouvelles données à lire sur l'une de ces 9471 autres connexions que j'ai ouvertes". epoll
bloque jusqu'à ce que l'une de ces choses soit prête, puis tu le fais. Puis vous répétez. Les appels système comme accept
y read
y write
ne bloquent jamais, en partie parce que lorsque vous les appelez, epoll
vient de vous dire qu'ils sont prêts, donc il n'y a aucune raison de bloquer, et aussi parce que lorsque vous ouvrez la socket ou le fichier, vous spécifiez que vous les voulez en mode non bloquant, donc ces appels échoueront avec EWOULDBLOCK
au lieu de bloquer.
L'avantage de ce modèle est que vous n'avez besoin que d'un seul processus. Cela signifie que vous n'avez pas besoin d'allouer une pile et des structures de noyau pour chaque demande. Nginx y HAProxy utilisent ce modèle, et c'est une des raisons pour lesquelles ils peuvent gérer beaucoup plus de connexions qu'Apache sur un matériel similaire.