Как веб-серверы «прослушивают» IP-адреса, прерывания или опросы?

87

Я пытаюсь понять нижние детали веб-серверов. Мне интересно, если сервер, скажем, Apache, постоянно опрашивает новые запросы или работает ли он какой-то системой прерываний. Если это прерывание, что вызывает прерывание, это драйвер сетевой карты?

user2202911
источник
1
Ключевое слово, чтобы понять это «сервер» . В модели сервер-клиент (по сравнению с моделью главный-подчиненный) сервер ожидает запросов от клиентов. Эти запросы являются событиями, которые необходимо обслуживать. Веб-сервер - это прикладная программа. Ваш вопрос объединяет ПО приложения с терминологией HW (например, прерывание и NIC), а не сохраняет связанные понятия на одном уровне абстракции. Драйвер NIC может иногда использовать опрос, например, драйверы Linux NAPI возвращаются к опросу, когда происходит поток пакетов. Но это не имеет отношения к SW приложения обработки событий.
опилки
1
@sawdust Очень интересно. Вопрос действительно предназначен для понимания связи между процессами SW и HW
user2202911
1
Это очень похоже на то, как программы командной строки (и другие графические интерфейсы) слушают клавиатуру. Особенно в оконной системе, где у вас есть шаг, когда ядро ​​получает данные с клавиатуры и передает их оконному менеджеру, который идентифицирует окно, которое имеет фокус, и передает данные этому окну.
G-Man
@ G-Man: я теория, да. В действительности большинство машинисток не печатают со скоростью 1 Гбит / с, что оправдывает наличие двух разных архитектур. Один чистый, гибкий и медленный, другой неуклюжий, но скоростной.
MSalters

Ответы:

181

Короткий ответ: какая-то система прерываний. По сути, они используют блокирующий ввод / вывод, что означает, что они спят (блокируются), ожидая новых данных.

  1. Сервер создает сокет прослушивания, а затем блокирует при ожидании новых подключений. В течение этого времени ядро ​​переводит процесс в состояние прерывистого сна и запускает другие процессы. Это важный момент: непрерывный процессный опрос может привести к потере ресурсов процессора. Ядро может использовать системные ресурсы более эффективно, блокируя процесс, пока не будет выполнено работы.

  2. Когда новые данные поступают в сеть, сетевая карта выдает прерывание.

  3. Видя, что есть прерывание от сетевой карты, ядро ​​через драйвер сетевой карты считывает новые данные с сетевой карты и сохраняет их в памяти. (Это должно быть сделано быстро и обычно обрабатывается внутри обработчика прерываний.)

  4. Ядро обрабатывает вновь поступившие данные и связывает их с сокетом. Процесс, который блокирует этот сокет, будет помечен как работоспособный, что означает, что он теперь может быть запущен. Он не обязательно запускается немедленно (ядро может решить запустить другие процессы).

  5. На досуге ядро ​​разбудит заблокированный процесс веб-сервера. (Так как теперь он работает.)

  6. Процесс веб-сервера продолжает выполняться, как будто времени не прошло. Его системный вызов блокировки возвращается и обрабатывает любые новые данные. Тогда ... перейдите к шагу 1.

Грег Баузер
источник
18
+1 за четкое разграничение ядра и процесса веб-сервера.
Рассел Борогове
13
Я не могу поверить во что-то настолько сложное, как это можно резюмировать так просто и ясно, но вы сделали это. +1
Брэндон
8
+1 Отличный ответ. Кроме того, шаги между 2 и 3 могут стать немного более сложными с современными сетевыми картами, ОС и драйверами. Например, в NAPI в Linux пакеты фактически не принимаются в контексте прерывания. Вместо этого ядро ​​говорит: «Хорошо, NIC, я понимаю, что у вас есть данные. Перестаньте меня беспокоить (отключите источник прерываний), и я скоро вернусь, чтобы захватить этот пакет и любые последующие пакеты, которые могут прибыть раньше, чем я».
Джонатон Рейнхарт
8
Небольшой придира: блокировать не обязательно. Как только процесс сервера создаст сокет прослушивания, ядро ​​примет SYN на этом порту, даже если вы не заблокированы внутри accept. Они являются (к счастью, или это было бы совершенно отстой!) Независимыми, асинхронно выполняющимися задачами. Когда устанавливаются соединения, они помещаются в очередь, откуда acceptих тянет. Только если их нет, он блокирует.
Деймон
3
«читает новые данные с сетевой карты и сохраняет их в памяти. (Это должно быть сделано быстро и обычно обрабатывается в обработчике прерываний.)« Разве это не делается с прямым доступом к памяти?
Сиюань Рен
9

Здесь довольно много «нижних» деталей.

Во-первых, учтите, что в ядре есть список процессов, и в любой момент времени некоторые из этих процессов работают, а некоторые нет. Ядро позволяет каждому выполняющемуся процессу некоторое время процессора, затем прерывает его и переходит к следующему. Если нет запущенных процессов, то ядро, вероятно, выдаст команду типа HLT для процессора, которая приостанавливает работу процессора до тех пор, пока не произойдет аппаратное прерывание.

Где-то на сервере находится системный вызов, который говорит «дай мне что-нибудь сделать». Есть две широкие категории способов, которыми это может быть сделано. В случае Apache он вызывает acceptсокет, который Apache ранее открыл, вероятно, прослушивая порт 80. Ядро поддерживает очередь попыток подключения и добавляет в эту очередь каждый раз, когда получен TCP SYN . Как ядро ​​узнает, что получен TCP SYN, зависит от драйвера устройства; для многих сетевых карт возможно получение аппаратного прерывания при получении сетевых данных.

acceptпросит ядро ​​вернуть мне следующую инициализацию соединения. Если очередь не была пуста, то acceptсразу возвращается. Если очередь пуста, то процесс (Apache) удаляется из списка запущенных процессов. Когда позднее соединение инициируется, процесс возобновляется. Это называется «блокирование», потому что для вызывающего его процесса accept()выглядит как функция, которая не возвращает, пока не получит результат, который может пройти через некоторое время. За это время процесс больше ничего не может сделать.

После acceptвозвращения Apache знает, что кто-то пытается установить соединение. Затем он вызывает fork, чтобы разделить процесс Apache на два идентичных процесса. Один из этих процессов продолжает обрабатывать HTTP-запрос, другой вызывает acceptснова, чтобы установить следующее соединение. Таким образом, всегда есть главный процесс, который делает только подпроцессы вызова acceptи вызова , а затем есть один подпроцесс для каждого запроса.

Это упрощение: это можно сделать с потоками, а не с процессами, а также можно сделать это forkзаранее, чтобы рабочий процесс был готов к работе при получении запроса, что снижает накладные расходы при запуске. В зависимости от того, как настроен Apache, он может выполнять одно из следующих действий.

Это первая широкая категория , как это сделать, и это называется блокирование операций ввода - вывода , так как системные вызовы , как acceptи readи writeкоторые действуют на патрубках приостановит процесс , пока они не имеют что - то вернуть.

Другой широкий способ сделать это называется неблокирующим, основанным на событиях или асинхронным вводом-выводом . Это реализуется с помощью системных вызовов, таких как selectили epoll. Каждый из них делает одно и то же: вы даете им список сокетов (или вообще файловых дескрипторов) и того, что вы хотите с ними делать, и ядро ​​блокируется, пока не будет готово выполнить одну из этих вещей.

С этой моделью вы могли бы сказать ядру (с epoll): «Скажите мне, когда будет новое соединение на порту 80 или новые данные для чтения на любом из этих 9471 других соединений, которые у меня открыты». epollблоки, пока одна из этих вещей не будет готова, тогда вы делаете это. Тогда вы повторяете. Системные вызовы , как acceptи readи writeне блок, отчасти потому , что всякий раз , когда вы их называете, epollпросто сказали, что они готовы , так что не было бы никаких оснований для блокирования, а также потому , что при открытии сокета или файла вы указываете , что вы хотите их в неблокирующем режиме, поэтому эти вызовы не будут EWOULDBLOCKблокироваться вместо.

Преимущество этой модели в том, что вам нужен только один процесс. Это означает, что вам не нужно выделять стек и структуры ядра для каждого запроса. Nginx и HAProxy используют эту модель, и это большая причина, по которой они могут иметь дело с гораздо большим количеством соединений, чем Apache на аналогичном оборудовании.

Фил Фрост
источник