Причины падения пакетов 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).