Причины падения пакетов UDP Linux

У меня есть приложение на Linux С++, которое получает последовательные UDP-пакеты. Из-за последовательности, я могу легко определить, когда пакет потерян или переупорядочен, т.е. Когда встречается "пробел". Система имеет механизм восстановления для устранения пробелов, однако лучше всего избегать пробелов в первую очередь. Используя простой пакетный сниффер на основе libpcap, я определил, что в данных на аппаратном уровне нет пробелов. Тем не менее, я вижу много пробелов в своем приложении. Это говорит о том, что ядро ​​отбрасывает пакеты; это подтверждается просмотром файла /proc/net/snmp. Когда мое приложение встречает пробел, счетчик Udp InErrors увеличивается.

На системном уровне мы увеличили максимальный буфер приема:

# sysctl net.core.rmem_max
net.core.rmem_max = 33554432

На уровне приложения мы увеличили размер буфера приема:

int sockbufsize = 33554432
int ret = setsockopt(my_socket_fd, SOL_SOCKET, SO_RCVBUF,
        (char *)&sockbufsize,  (int)sizeof(sockbufsize));
// check return code
sockbufsize = 0;
ret = getsockopt(my_socket_fd, SOL_SOCKET, SO_RCVBUF, 
        (char*)&sockbufsize, &size);
// print sockbufsize

После вызова метода getsockopt() печатное значение всегда равно 2x, на которое оно установлено (67108864 в приведенном выше примере), но я считаю, что этого следует ожидать.

Я знаю, что неспособность быстро потреблять данные может привести к потере пакетов. Тем не менее, все это приложение выполняет проверку последовательности, а затем выталкивает данные в очередь; фактическая обработка выполняется в другом потоке. Кроме того, машина современная (Dual Xeon X5560, 8 ГБ оперативной памяти) и очень легко загружена. У нас буквально десятки идентичных приложений, получающих данные с гораздо более высокой скоростью, которые не испытывают этой проблемы.

Помимо слишком медленного использования приложений, существуют ли другие причины, по которым ядро ​​Linux может отказаться от пакетов UDP?

FWIW, это на CentOS 4, с ядром 2.6.9-89.0.25.ELlargesmp.

Ответы

Ответ 1

У меня была аналогичная проблема с моей программой. Его задача - получить udp-пакеты в одном потоке и, используя блокирующую очередь, записать их в базу данных с помощью другого потока.

Я заметил (используя vmstat 1), что, когда система испытывала тяжелые операции ожидания ввода-вывода (чтение), мое приложение не получало пакеты, но система получала их.

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

Я разрешил его, играя с точностью (ionice) моего процесса и процесса базы данных. Помогло изменение класса расписания ввода-вывода в Best Effort. Удивительно, но теперь я не могу воспроизвести эту проблему, даже с отличной поведением ввода-вывода. Мое ядро ​​- 2.6.32-71.el6.x86_64.

Я все еще разрабатываю это приложение, поэтому я попытаюсь обновить свой пост, как только узнаю больше.

Ответ 2

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

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

Ответ 3

int ret = setsockopt(my_socket_fd, SOL_SOCKET, SO_RCVBUF, (char *)&sockbufsize, (int)sizeof(sockbufsize));

Прежде всего, setsockopt принимает (int, int, int, void *, socklen_t), поэтому нет необходимости выполнять броски.

Используя простой пакетный сниффер на основе libpcap, я определил, что в данных на аппаратном уровне нет пробелов. Тем не менее, я вижу много пробелов в своем приложении. Это говорит о том, что ядро ​​отбрасывает пакеты,

Это говорит о том, что ваша среда не достаточно быстра. Известно, что захват пакетов интенсивно обрабатывается, и вы заметите, что глобальная скорость передачи на интерфейсе будет уменьшаться, когда вы начнете захватывать такие программы, как iptraf-ng или tcpdump.

Ответ 4

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

Чтобы решить эту проблему, я добавил в принимающий поток меньший локальный буфер, и если бы он только вставлял данные в буфер, то он не был заблокирован (с использованием std:: mutex:: try_lock).