Я тестирую свой код на дешевом дроплете Digital Ocean (2 ГБ оперативной памяти, но я также пытался добавить 4 ГБ подкачки, и это ничего не изменило).
Часть моей программы имеет много клиентских сокетов DGRAM UDP, открытых для различных локальных портов (от/до 127.0.0.1).
Иногда без отправки безумного количества данных вызов send()
завершается с ошибкой No buffer space available
(ошибка 105).
Затем моя программа пытается использовать все остальные доступные сокеты (для других локальных портов), но все они терпят неудачу одновременно.
Я установил /proc/sys/net/core/wmem_max
(и /proc/sys/net/core/rmem_max
) на 16 МБ, а для каждого из этих сокетов setsockopt SOL_SOCKET, SO_SNDBUF
установлено значение 8 МБ. Ни один из них ничего не меняет.
Как send()
может потерпеть неудачу с No buffer space available
? Разве это не должно происходить только тогда, когда мы посылаем () слишком быстро? Но здесь это локально, и я не отправляю много данных.
Что меня больше всего озадачивает, так это то, что когда процесс повторяет попытку любого другого сокета, он получает ту же ошибку. Разве буферы сокетов не должны быть полностью независимыми?
Каковы возможные причины?
Ошибка No buffer space available
не означает, что буфер сокета заполнен. Это означает, что ядро Linux не может выделить память для буфера сокета. Для любого буфера сокета нет памяти, поэтому, когда возникает это условие, невозможно отправить какие-либо данные через любой сокет, пока не будет освобождена часть памяти. Увеличение wmem_max
и rmem_max
может еще больше усугубить проблему, поскольку они могут увеличить потребление памяти на сокет. Вы можете проверить общее потребление памяти и сколько памяти выделено для буферов сокета udp:
$ cat /proc/net/sockstat
sockets: used 315
TCP: inuse 8 orphan 0 tw 0 alloc 13 mem 1
UDP: inuse 3853 mem 240812
UDPLITE: inuse 0
RAW: inuse 0
FRAG: inuse 0 memory 0
Сокеты UDP в этом примере используют 240812 страниц на 4 КБ, что составляет ~ 940 МБ. Открыто 3853 сокета
$ free -h
total used free shared buff/cache available
Mem: 1.9G 1.8G 75M 1.2M 66M 28M
Swap: 0B 0B 0B
Система в этом примере имеет 2 ГБ физической памяти и очень мало свободной памяти. Высока вероятность того, что функция send
завершается с ошибкой No buffer space available
.
Если память фактически потребляется буферами сокетов, добавление подкачки вряд ли поможет, потому что, насколько мне известно, буферы не могут быть заменены.
Вы можете попробовать виртуальную машину с большим объемом памяти. Это может помочь или, по крайней мере, отсрочить проблему.
Вы также можете провести аудит приложения и проверить, как используются сокеты. Высокое потребление памяти буферами сокетов UDP может быть вызвано ошибкой в приложении или неправильным дизайном. Например, если приложение прослушивает сокет UDP и есть данные, но приложение ничего не читает, тогда ядро не освобождает память до тех пор, пока процесс не завершится или пока приложение не вызовет recv
. Много открытых сокетов без вызова recv
может использовать память.
Мне удалось воспроизвести при проверке как /proc/net/sockstat
, так и free -h
, и 180 сокетов в среднем никогда не занимают более 80 страниц (потому что большинство из них просто ждут, чтобы их использовали), но память без подкачки, тем не менее, достигает критически низкой точки, при которой проблема бывает. Спасибо! Теперь я знаю, что с самими сокетами все в порядке, просто они выходят из строя первыми, когда системе не хватает памяти.
Да, память для буфера выделяется по мере необходимости — обычно при получении данных или вызове приложения send
. Если буфер сокета заполнен (данные ожидают отправки или данные ожидают чтения), тогда ядро отбрасывает некоторые данные, но не сообщает об ошибке. Это разрешено для UDP. Но как вы написали - проблема в общем потреблении памяти. 80 страниц для буферов сокетов - это не очень много. Там наверное другой пожиратель памяти.
Другими словами, отдельные буферы сокетов не заполнены (SO_SNDBUF), но все они вместе занимают так много, что ядро не может найти для них больше (без подкачки) памяти? Что касается увеличения wmem_max/rmem_max и SO_SNDBUF, кто-то здесь упомянул, что память на самом деле не используется, пока не понадобится: stackoverflow.com/questions/64523733/… Вы согласны?