Como os servidores da Web “escutam” endereços IP, interrompem ou pesquisam?

87

Estou tentando entender os detalhes mais baixos dos servidores web. Gostaria de saber se um servidor, digamos o Apache, está pesquisando continuamente novas solicitações ou se funciona com algum tipo de sistema de interrupção. Se for uma interrupção, o que está causando a interrupção, é o driver da placa de rede?

user2202911
fonte
1
A palavra-chave a entender é "servidor" . No modelo servidor-cliente (versus modelo mestre-escravo), o servidor aguarda solicitações dos clientes. Essas solicitações são eventos que precisam ser atendidos. Um servidor da web é um programa de aplicativo. Sua pergunta combina o SW do aplicativo com a terminologia HW (por exemplo, interrupção e NIC), em vez de manter os conceitos relacionados na mesma camada de abstração. Às vezes, o driver da NIC pode usar a pesquisa, por exemplo, os drivers NAPI do Linux regredem para a pesquisa quando há uma enxurrada de pacotes. Mas isso é irrelevante para o aplicativo de processamento de eventos SW.
sawdust
1
@sawdust Muito interessante. A questão é realmente significou para entender a conexão entre os processos de SW e HW
user2202911
1
É muito parecido com o modo como os programas de linha de comando (e outras GUI) ouvem o teclado. Especialmente em um sistema de janelas, em que você tem a etapa do kernel recebendo os dados do dispositivo de teclado e entregando-os ao gerenciador de janelas, que identifica a janela que está focada e fornece os dados para essa janela.
G-Man
@ G-Man: Eu teoria, sim. Na realidade, a maioria dos datilógrafos 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:

181

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

  1. O servidor cria um soquete de escuta e bloqueia enquanto aguarda novas conexões. Durante esse período, o kernel coloca o processo em um estado de suspensão interrompível e executa outros processos. Este é um ponto importante: fazer a pesquisa de processo continuamente desperdiçaria CPU. O kernel é capaz de usar os recursos do sistema com mais eficiência, bloqueando o processo até que haja trabalho para ele fazer.

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

  3. Vendo que há uma interrupção na 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 bloqueado nesse soquete será marcado como executável, o que significa que agora está qualificado para execução. Não é necessariamente executado imediatamente (o kernel pode decidir executar outros processos ainda).

  5. Quando quiser, o kernel ativará o processo bloqueado do servidor da web. (Como agora é executável.)

  6. O processo do servidor da web continua executando como se não houvesse tempo. Sua chamada do sistema de bloqueio retorna e processa quaisquer novos dados. Então ... vá para o passo 1.

Greg Bowser
fonte
18
+1 para delinear claramente o kernel versus o processo do servidor da web.
Russell Borogove
13
Não posso acreditar em algo tão complexo, pois isso pode ser resumido de forma tão clara e simples, mas você conseguiu. +1
Brandon
8
+1 ótima resposta. Além disso, as etapas entre 2 e 3 podem ficar um pouco mais complexas com as placas de rede, SOs e drivers modernos. Por exemplo, com a NAPI no Linux, os pacotes não são realmente recebidos no contexto de interrupção. Em vez disso, o kernel diz "Ok, NIC, entendo que você tenha dados. Pare de me incomodar (desative a fonte de interrupção) e voltarei em breve para pegar esse pacote e quaisquer pacotes subsequentes que possam chegar antes de mim".
Jonathon Reinhart 10/11
8
Ligeiro nitpick: Não é realmente necessário bloquear. Assim que o processo do servidor criar um soquete de escuta, o kernel aceitará SYNs nessa porta, mesmo enquanto você não estiver bloqueado por dentro accept. São (felizmente, ou seria totalmente péssimo!) Tarefas independentes, executadas de forma assíncrona. À medida que as conexões entram, elas são colocadas em uma fila de onde as acceptpuxam. Somente se não houver, ele bloqueia.
Damon
3
"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ções.)" "Isso não é feito com acesso direto à memória?
Siyuan Ren
9

Existem muitos detalhes "inferiores".

Primeiro, considere que o kernel possui 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 o interrompe e passa para o próximo. Se não houver processos executáveis, o kernel provavelmente emitirá uma instrução como HLT para a CPU que suspenderá a CPU até que haja uma interrupção de hardware.

Em algum lugar do servidor, há uma chamada do sistema que diz "me dê algo para fazer". Existem duas grandes categorias de maneiras pelas quais isso pode ser feito. No caso do Apache, ele chama acceptum 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 muitas placas de rede, provavelmente há uma interrupção de hardware quando os dados da rede são recebidos.

acceptpede que o kernel retorne para mim a próxima iniciação de conexão. Se a fila não estava vazia, acceptapenas retorna imediatamente. Se a fila estiver vazia, o processo (Apache) será removido da lista de processos em execução. Quando uma conexão é iniciada posteriormente, o processo é retomado. Isso é chamado de "bloqueio", porque, para o processo que a chama, accept()parece uma função que não retorna até ter um resultado, que pode levar algum tempo a partir de agora. Durante esse período, o processo não pode fazer mais nada.

Uma vez acceptretornado, o Apache sabe que alguém está tentando iniciar uma conexão. Em seguida, chama o fork para dividir o processo Apache em dois processos idênticos. Um desses processos continua a processar a solicitação HTTP, o outro chama acceptnovamente para obter a próxima conexão. Portanto, sempre há um processo mestre que não faz nada além de chamar accepte gerar subprocessos e, em seguida, há um subprocesso para cada solicitação.

Isso é uma simplificação: é possível fazer isso com threads em vez de processos, e também é possível com forkantecedência, para que haja um processo de trabalho pronto quando uma solicitação for recebida, reduzindo assim a sobrecarga da inicialização. Dependendo de como o Apache estiver configurado, ele poderá executar uma dessas ações.

Essa é a primeira categoria ampla de como fazê-lo, e é chamado de bloqueio IO porque o sistema chama como accepte reade writeque operam em sockets irá suspender o processo até que eles têm algo a voltar.

A outra maneira ampla de fazer isso é chamada de E / S não-bloqueadora ou baseada em eventos ou assíncrona . Isso é implementado com chamadas do sistema como selectou epoll. Cada um deles faz a mesma coisa: você fornece a eles uma lista de soquetes (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 que eu abri". epollbloqueia até que uma dessas coisas esteja pronta, então você faz. Então você repete. Chama sistema como accepte reade writenunca mais bloco, em parte porque sempre que você chamá-los, epollapenas lhe disse que eles estão prontos para que não haveria nenhuma razão para bloquear, e também porque quando você abre o soquete ou o arquivo que você especificar que você quer que eles no modo sem bloqueio, para que essas chamadas falhem em EWOULDBLOCKvez de bloquear.

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

Phil Frost
fonte