O_NONBLOCK неожиданное поведение

#c #linux #sockets #unix #networking

#c #linux #сокеты #unix #создание сетей

Вопрос:

Я использую сокет и устанавливаю O_NONBLOCK с помощью fcntl, но приложение по-прежнему потребляет 100% процессора. Даже я использую select со временем ожидания 1 мс.

Для чтения я использую recv.

Я попробовал это без O_NONBLOCK, и в этом случае он не потребляет 100% процессора, но recv занимает много времени. Я попробовал recv с опцией O_NONBLOCK, но все равно recv занимает много (где-то 200 мс) времени для чтения. Мы используем select, тогда почему recv занимает много времени?

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

Любой может предложить свои мнения. Это клиентское приложение.

Комментарии:

1. «Для целей чтения с использованием recv и после того, как не установлен O_NONBLOCK, он не потребляет 100% ЦП, и если в этом случае recv с опцией O_NONBLOCK занимает много времени для чтения значения». <— Я не понимаю, что это значит

Ответ №1:

функция select() оптимизирована для перехода в эффективное состояние ожидания (не потребляет много ЦП) при использовании более длительного тайм-аута. Попробуйте примерно через 30 секунд. Он немедленно вернется, когда произойдет действие в одном из просмотренных файловых дескрипторов, так что вы можете выполнять свою работу.

Попробуйте просмотреть свой процесс в strace -v -ttt или tcpdump, чтобы найти подсказки о высокой задержке, или показать соответствующий код.

Также имейте в виду, что select может изменить структуру временных значений, поэтому не забывайте сбрасывать значения между вызовами select().

Комментарии:

1. 30 секунд — это многовато. Для действия, запрошенного пользователем, достаточно около 1,5 — 2 секунд, чтобы дать им возможность отменить, когда доступ к сети не работает (например, неправильный одноранговый адрес или сеть не работает)

2. Время действительно зависит от того, какие другие события за пределами выбора вам нужно обслуживать. Тридцати секунд может быть достаточно для неинтерактивного демона, который отключается только один раз за голубую луну. Может потребоваться одна миллисекунда, если в вашем процессе запущен другой поток rm -rf / 🙂

3. Согласованные 30 секунд могут быть чрезмерными, но select() также может быть добровольно прерван сигналом. В любом случае это должно быть настраиваемым :). Я подозреваю, что операционная система входит в цикл занятости recv() и / или select() — strace должен показать это.

4. Если вам нужно выйти из цикла выбора, чтобы что-то опросить, ваше приложение плохо спроектировано. Обработчики сигналов в любом случае не могут выполнять какую-либо значительную работу, поэтому достаточно просто заставить их записать канал sentinel, который отслеживает select. Аналогично для пользовательского ввода — подключите сокет X или стандартный интерфейс к select. etc. Если у вас на самом деле нет события, которое должно произойти в определенное время, практически нет причин для какого-либо тайм-аута для выбора вообще.

Ответ №2:

Я не думаю, что это неожиданно. Если вы установите неблокирующий режим, recv он не будет блокировать ожидание чего-либо. Другими словами, он вернет независимо от того, доступны данные или нет, и это, скорее всего, перегрузит ваш процессор.

Я бы также подумал о том, чтобы немного увеличить время select ожидания. Нет никакой реальной причины иметь его таким маленьким, поскольку это также может привести к перегрузке процессора.

Я склонен использовать такие значения, как секунда для select тайм-аута, поскольку это задержка, которая мне нужна для других вещей (например, своевременной обработки сигналов CTRL-BREAK).

Это дает процессу много времени, не требующего больших затрат процессора, в ожидании, пока данные станут доступными. Если он станет доступен менее чем за секунду, то вы узнаете об этом раньше ( select вернется).

Комментарии:

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

2. @ggiroux: на самом деле это не имеет значения, но select срабатывает по уровню, а не по краю.

3. Да, извините, я смущен, забудьте об этом, пакс также разъяснил свой ответ, поскольку я прокомментировал.