Что происходит после захвата пакета?

Я читал о том, что происходит после того, как пакеты захвачены сетевыми картами, и чем больше я читаю, тем больше я смущен.

Во-первых, я читал это традиционно, после того как пакет захвачен NIC, он копируется в блок памяти в пространстве ядра, а затем в пространство пользователя для любого приложения, которое затем работает с пакетными данными. Затем я прочитал о DMA, где NIC напрямую копирует пакет в память, минуя CPU. Так же сохраняется ли NIC → память ядра → поток памяти пространства пользователя? Кроме того, большинство NIC (например, Myricom) используют DMA для повышения скорости захвата пакетов?

Во-вторых, работает ли RSS (получение бокового масштабирования) аналогично в системах Windows и Linux? Я могу только найти подробные объяснения того, как работает RSS в статьях MSDN, где они рассказывают о том, как работает RSS (и MSI-X) в Windows Server 2008. Но та же концепция RSS и MSI-X по-прежнему должна применяться для Linux-систем, правильно

Спасибо.

С уважением, Rayne

Ответ 1

Как этот процесс воспроизводится, в основном зависит от автора драйвера и аппаратного обеспечения, но для драйверов, на которые я смотрел или писал, и аппаратного обеспечения, с которым я работал, обычно работает так:

  • При инициализации драйвера он выделяет некоторое количество буферов и передает их в сетевой адаптер.
  • Когда пакет принимается NIC, он вытаскивает следующий адрес из своего списка буферов, DMA передает данные непосредственно в него и уведомляет драйвер через прерывание.
  • Драйвер получает прерывание и может либо перевести буфер на ядро, либо будет выделять новый буфер ядра и скопировать данные. "Zero copy networking" является первой и, очевидно, требует поддержки со стороны операционной системы. (ниже).
  • Драйвер должен либо выделить новый буфер (в случае с нулевой копией), либо повторно использовать буфер. В любом случае буфер возвращается в сетевой адаптер для будущих пакетов.

Нулевое копирование в ядре не так уж плохо. Нулевое копирование до уровня пользователя намного сложнее. Userland получает данные, но сетевые пакеты состоят из обоих заголовков и данных. По крайней мере, истинная нуль-копия полностью в пользовательскую область требует поддержки от вашей сетевой платы, чтобы она могла DMA-пакеты в отдельные буферы заголовков/данных. Заголовки перерабатываются после того, как ядро ​​отправляет пакет в пункт назначения и проверяет контрольную сумму (для TCP, либо в аппаратном обеспечении, если NIC поддерживает его, либо в программном обеспечении, если нет, обратите внимание, что если ядро ​​должно вычислить контрольную сумму, могут также скопировать данные: просмотр данных приводит к сбоям в кэше, а копирование в другом месте может быть бесплатным с настроенным кодом).

Даже если все звезды совпадают, данные не находятся в вашем пользовательском буфере, когда они принимаются системой. Пока приложение не запросит данные, ядро ​​не знает, где это будет. Рассмотрим случай многопроцессного демона, такого как Apache. Существует много дочерних процессов, которые прослушиваются в одном и том же сокете. Вы также можете установить соединение fork(), и оба процесса могут recv() принимать входящие данные.

TCP-пакеты в Интернете обычно составляют 1460 байт полезной нагрузки (MTU 1500 = 20-байтовый IP-заголовок + 20-байтовый заголовок TCP + 1460 байт). 1460 не является силой 2 и не будет соответствовать размеру страницы в любой системе, которую вы найдете. Это создает проблемы для повторной сборки потока данных. Помните, что TCP ориентирован на поток. Не существует различий между отправителями, и две 1000-байтные записи, ожидающие в полученном, будут полностью поглощены чтением в 2000 байт.

Принимая это далее, рассмотрим пользовательские буферы. Они выделяются приложением. Чтобы использовать для нулевой копии до конца, буфер должен быть выровнен по страницам и не передавать эту страницу памяти ни с чем другим. В момент времени recv() ядро ​​теоретически может переназначить старую страницу с той, которая содержит данные, и "перевернуть" ее на место, но это осложняется проблемой повторной сборки выше, поскольку последовательные пакеты будут находиться на отдельных страницах. Ядро может ограничить данные, которые он передает, для каждой полезной нагрузки пакета, но это будет означать много дополнительных системных вызовов, переназначение страниц и, вероятно, более низкую пропускную способность в целом.

Я действительно только царапаю поверхность по этой теме. Я работал в нескольких компаниях в начале 2000-х годов, пытаясь расширить понятия нулевой копии в пользовательскую область. Мы даже реализовали стек TCP в пользовательской среде и полностью обошли ядро ​​для приложений, использующих стек, но это создало собственный набор проблем и никогда не было качеством производства. Это очень трудная проблема.