Questão Como os servidores da web “escutam” os endereços IP, interrompem ou pesquisam?


Estou tentando entender os detalhes mais baixos dos servidores da web. Eu estou querendo saber se um servidor, digamos Apache, está continuamente pesquisando novos pedidos ou se funciona por algum tipo de sistema de interrupção. Se é uma interrupção, o que está provocando a interrupção, é o driver da placa de rede?


86


origem


A palavra-chave para entender é "servidor". No modelo servidor-cliente (versus modelo mestre-escravo) o servidor espera para solicitações de clientes. Esses pedidos são eventos que precisam ser atendidos. Um servidor da web é um programa aplicativo. Sua pergunta combina a SW do aplicativo com a terminologia de HW (por exemplo, interrupção e NIC), em vez de manter os conceitos relacionados na mesma camada de abstração. O driver da NIC pode, na verdade, usar sondagem às vezes, por ex. Os drivers NAPI do Linux retornam ao polling quando há uma avalanche de pacotes. Mas isso é irrelevante para o SW do aplicativo de processamento de eventos. - sawdust
@sawdust Muito interessante. A questão é realmente entender a conexão entre os processos SW e HW - user2202911
É muito semelhante à maneira como os programas de linha de comando (e outras GUIs) ouvem o teclado. Especialmente em um sistema de janelas, onde você tem a etapa do kernel de receber os dados do dispositivo de teclado e entregá-los ao gerenciador de janelas, que identifica a janela que tem foco e fornece os dados para essa janela. - G-Man
@ G-Man: Eu teoria, sim. Na realidade, a maioria dos digitadores não digita a 1 Gbit / s, o que justifica ter duas arquiteturas diferentes. Um limpo, flexível e lento, um desajeitado mas de alta velocidade. - MSalters


Respostas:


A resposta curta é: algum tipo de sistema de interrupção. Essencialmente, eles usam bloqueio de E / S, o que significa que eles dormem (bloqueiam) enquanto esperam por novos dados.

  1. O servidor cria um soquete de escuta e bloqueia enquanto aguarda novas conexões. Durante esse tempo, o kernel coloca o processo em um sono interrompido estado e executa outros processos. Este é um ponto importante: ter a pesquisa do processo continuamente desperdiçaria a CPU. O kernel é capaz de usar os recursos do sistema de forma mais eficiente, bloqueando o processo até que haja trabalho a ser feito.

  2. Quando novos dados chegam na rede, a placa de rede emite uma interrupção.

  3. Vendo que há uma interrupção da placa de rede, o kernel, através do driver da placa de rede, lê os novos dados da placa de rede e os armazena na memória. (Isso deve ser feito rapidamente e geralmente é tratado dentro do manipulador de interrupção.)

  4. O kernel processa os dados recém-chegados e os associa a um soquete. Um processo que está bloqueando nesse soquete será marcado como executável, o que significa que agora ele está qualificado para ser executado. Ele não necessariamente é executado imediatamente (o kernel pode decidir executar outros processos ainda).

  5. A seu gosto, o kernel irá ativar o processo do servidor web bloqueado. (Desde que agora é executável.)

  6. O processo do servidor da Web continua executando como se nenhum tempo tivesse passado. Sua chamada de sistema de bloqueio retorna e processa novos dados. Então ... vá para o passo 1.


181



+1 para um delineamento claro do kernel versus o processo do servidor web. - Russell Borogove
Eu não posso acreditar que algo tão complexo como este pode ser resumido de forma tão clara e simples, mas você fez isso. +1 - Brandon
+1 ótima resposta. Além disso, as etapas entre 2 e 3 podem ficar um pouco mais complexas com NICs, SOs e drivers modernos. Por exemplo, com NAPI no Linux, os pacotes não são realmente recebidos em contexto de interrupção. Em vez disso, o kernel diz "OK NIC, eu entendo que você tem dados. Pare de me incomodar (desative a fonte de interrupção), e voltarei em breve para pegar este pacote e quaisquer pacotes subseqüentes que possam chegar antes de eu fazer. " - Jonathon Reinhart
Ligeiro nitpick: Não é realmente necessário bloquear. Assim que o processo do servidor tiver criado um soquete de escuta, o kernel aceitará SYNs nessa porta, mesmo quando você não estiver bloqueado accept. Eles são (felizmente, ou seria totalmente uma droga!) Tarefas independentes e de execução assíncrona. Conforme as conexões entram, elas são colocadas em uma fila onde accept puxa-os de. Apenas se não houver, bloqueia. - Damon
"lê os novos dados da placa de rede e armazena-os na memória. (Isso deve ser feito rapidamente e geralmente é tratado dentro do manipulador de interrupções.)" Isso não é feito com acesso direto à memória? - Siyuan Ren


Existem muitos detalhes "inferiores".

Primeiro, considere que o kernel tem uma lista de processos e, a qualquer momento, alguns desses processos estão em execução e outros não. O kernel permite a cada processo em execução uma fatia do tempo da CPU, depois a interrompe e passa para a próxima. Se não houver processos executáveis, o kernel provavelmente emitirá uma instrução como HLT para a CPU que suspende a CPU até que haja uma interrupção de hardware.

Em algum lugar no servidor é um chamada de sistema que diz "me dê algo para fazer". Existem duas grandes categorias de maneiras que isso pode ser feito. No caso do Apache, ele chama accept em um soquete que o Apache abriu anteriormente, provavelmente escutando na porta 80. O kernel mantém uma fila de tentativas de conexão, e adiciona a essa fila toda vez que um TCP SYN é recebido. Como o kernel sabe que um TCP SYN foi recebido depende do driver do dispositivo; para muitos NICs, provavelmente há uma interrupção de hardware quando os dados da rede são recebidos.

accept pede que o kernel retorne para mim o próximo início de conexão. Se a fila não estava vazia, então accept apenas retorna imediatamente. Se a fila estiver vazia, o processo (Apache) será removido da lista de processos em execução. Quando uma conexão é iniciada mais tarde, o processo é retomado. Isso é chamado de "bloqueio", porque para o processo que o chama, accept()parece uma função que não retorna até que tenha um resultado, que pode demorar algum tempo a partir de agora. Durante esse tempo, o processo não pode fazer mais nada.

Uma vez accept retorna, o Apache sabe que alguém está tentando iniciar uma conexão. Em seguida, chama garfo dividir o processo do Apache em dois processos idênticos. Um desses processos passa a processar a solicitação HTTP, as outras chamadas accept novamente para obter a próxima conexão. Assim, há sempre um processo mestre que não faz nada além de chamar accept e gerar subprocessos e, em seguida, há um subprocesso para cada solicitação.

Isso é uma simplificação: é possível fazer isso com encadeamentos em vez de processos, e também é possível fork de antemão, então há um processo de trabalho pronto para ser usado quando uma solicitação é recebida, reduzindo assim a sobrecarga de inicialização. Dependendo de como o Apache está configurado, pode fazer qualquer uma dessas coisas.

Essa é a primeira grande categoria de como fazer isso, e é chamado bloqueio de IO porque o sistema chama como accept e read e write que operam em soquetes suspenderão o processo até que tenham algo a devolver.

A outra maneira ampla de fazer isso é chamada de não bloqueio ou baseada em eventos ou IO assíncrono. Isso é implementado com chamadas de sistema como select ou epoll. Cada um faz a mesma coisa: você dá a eles uma lista de sockets (ou, em geral, descritores de arquivos) e o que você quer fazer com eles, e o kernel bloqueia até que esteja pronto para fazer uma dessas coisas.

Com este modelo, você pode dizer ao kernel (com epoll), "Diga-me quando houver uma nova conexão na porta 80 ou novos dados para ler em qualquer uma dessas 9471 outras conexões abertas". epoll blocos até que uma dessas coisas esteja pronta, então você faz. Então você repete. Chamadas do sistema como accept e read e write nunca bloqueie, em parte porque sempre que você ligar, epoll Acabei de dizer que eles estão prontos para que não haja motivo para bloquear, e também porque quando você abrir o soquete ou o arquivo que você especificar que você quer que eles no modo de não-bloqueio, para que essas chamadas irão falhar com EWOULDBLOCK em vez de bloquear.

A vantagem deste modelo é que você precisa apenas de um processo. Isso significa que você não precisa alocar uma pilha e estruturas de kernel para cada solicitação. Nginx e HAProxy use este modelo, e é uma grande razão pela qual eles podem lidar com muito mais conexões do que o Apache em um hardware similar.


7