Какая структура хранится в буферах отправки/получения сокета UDP?

Вопрос или проблема

Я заметил, что буферы отправки и получения UDP-сокетов имеют накладные расходы примерно в 600 байт. То есть, когда я записываю 10 байт данных в UDP-сокет, фактическая структура, хранящаяся в буферах отправки/получения, имеет размер 768 байт. Итак, вопросы следующие:

  1. Что на самом деле хранится в буферах отправки/получения?
  2. Существует ли способ получить количество накладных байт из кода C?
  3. Сколько пакетов размером X байт можно хранить в буфере получения, прежде чем он переполнится?

Вот небольшая демонстрация:

int fd = socket(AF_INET, SOCK_DGRAM, 0);
struct sockaddr_in addr{
    .sin_family = AF_INET,
    .sin_port = htons(8080),
    .sin_addr = INADDR_ANY,
    .sin_zero = {},
};
sendto(fd, "0123456789", 10, 0, (sockaddr*)(&addr), sizeof(addr));

Сокет, который был привязан к порту 8080, имеет заполненный буфер получения 768 байт данными:

> netstat -anu | grep 8080
udp      768      0 0.0.0.0:8080            0.0.0.0:*

Нет общего интерфейса C для этой информации; это деталь реализации операционной системы, а не “API” для разработчиков приложений.

Для Linux:

Вы можете просто написать минимальный модуль ядра, который printk("размер буфера пакетов ядра: %d\n", sizeof sk_buff); в его инициализации и прочитать dmesg, когда вы его загрузите, чтобы узнать размер структуры данных, которая хранит всю метаинформацию (не сами данные, а только указатель на них) любого сетевого пакета в ядре.

Структура ядра struct sock содержит указатели на очереди этих структур для очередей получения, передачи и ошибок для каждого сокета.

Ваши 768 байт очень вероятно вызваны гранулярностью выделения, а не фактическими накладными расходами метаданных.

Снова, вы, как разработчик программы пользовательского пространства, никогда не видите эти данные (за исключением взаимодействия с ними с помощью вызовов libc для работы с сокетом).

Также может быть не очень разумно сосредотачиваться на этом размере: Если производительность вашей системы ограничена использованием памяти сетевых буферов, есть вероятность, что вы делаете что-то архитектурно неоптимальное и должны использовать другой интерфейс, отличающийся от UNIX/BSD-сокетов, для взаимодействия с сетевым стеком Linux в целом.

Ответ или решение

Часто задаваемый вопрос среди разработчиков и системных администраторов заключается в понимании структуры данных, хранящихся в буферах отправки и получения UDP-сокетов. В этой статье мы подробно рассмотрим, что именно содержится в этих буферах, а также как можно узнать объем накладных расходов и сколько пакетов может храниться в буфере перед переполнением.

1. Что хранится в буферах отправки/получения?

При работе с UDP-сокетами, когда данные отправляются или получаются, они помещаются в специальные буферы. Эти буферы поддерживаются операционной системой и используются для временного хранения пакетов. Важно отметить, что на данные, которые вы сами передаете (например, 10 байт), накладывается дополнительная информация, которая значительно увеличивает фактический использованный объем.

Структуры, которые хранятся в этих буферах, обычно включают следующее:

  • Управляющая информация: включает метаданные о каждом пакете (например, размер пакета, временные метки, информация о следующем и предыдущем пакетах и т.д.). На уровне ядра Linux такая информация обычно хранится в структуре sk_buff.

  • Физические данные: сам передаваемый пакет, со всеми необходимыми заголовками. Учитывая специфику протоколов, заголовки UDP и IP также занимают память.

В результате, когда вы отправляете 10 байт данных, фактический объем, который вы наблюдаете в буфере (например, 768 байт), также включает накладные расходы, а также дополнительные данные, которые предоставляет операционная система для управления сетевыми соединениями.

2. Как получить информацию о накладных расходах из C-кода?

К сожалению, существует ограниченное количество стандартных интерфейсов C для получения подробной информации о накладных расходах связанных с сокетами. Эти детали больше относятся к реализации операционной системы, чем к API, доступному для разработчиков приложений. Однако, есть способ получить необходимую информацию через специализированные вызовы ядра.

Для операционной системы Linux вы можете создать минимальный модуль ядра и использовать команду printk для вывода размера структуры пакета. Например:

printk("Размер структуры пакета в ядре: %d\n", sizeof(struct sk_buff));

Затем вы можете заглянуть в dmesg, чтобы увидеть результаты. Через структуру struct sock можно также анализировать информацию о очередях для передачи и получения.

3. Сколько пакетов размера X байт может храниться в буфере передачи?

Чтобы определить, сколько пакетов определенного размера может храниться в буфере, вам нужно учитывать общий размер буфера, который предоставляет операционная система. В случае с вашей демонстрацией, вы упомянули, что размер буфера составляет 768 байт. С учетом того, что часть этого объема занимает накладные расходы, реальный размер доступного пространства для данных будет меньше.

Для оценки вы можете использовать формулу:

Количество пакетов = (Размер буфера - Накладные расходы) / Размер пакета

Например, если мы предположим, что накладные расходы составляют 600 байт и размер пакета равен 10 байтам:

Количество пакетов = (768 - 600) / 10 = 16 пакетов

Заключение

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

Также важно помнить, что если производительность вашей системы ограничена использованием памяти сетевых буферов, возможно, стоит рассмотреть альтернативные методы взаимодействия с сетевыми стекми, такими как использование других интерфейсов, отличных от сокетов UNIX/BSD.

Оцените материал
Добавить комментарий

Капча загружается...