Есть ли разница между блокировкой и неблокирующей отправкой
Если приложение может гарантировать, что в буфере отправки сокета всегда будет место, будет ли блокировка и неблокирующая отправка иметь одинаковую производительность? В этом случае есть ли какие-либо преимущества в обоих подходах по сравнению с другими?
3 ответа:
Единственная разница между блокировкой и неблокированием
Однако я сомневаюсь в вашем неявном предположении, что отправка не может быть заблокирована только потому, что буфер отправки имеет свободное пространство. Представьте себе неработающий сокет в системе, которая предъявляет большие требования к памяти. Я бы не обязательно ожидал, что ядро "закрепит" физические страницы для вашего буфера отправки; я бы ожидайте, что он использует эту память для чего-то полезного. А затем, когда вы попытаетесь отправить, ядро должно будет захватить свободную страницу для буфера отправки; и если таких страниц нет, оно может решить вернутьsend
заключается в том, переводит ли ядро ваш процесс в спящий режим или возвращаетEWOULDBLOCK
. Так что с точки зрения производительности разницы быть не должно.EWOULDBLOCK
вместо ожидания (скажем) замены. Теперь, это много "может быть "и" может быть", и кто-то более знакомый с внутренними органами ядра может сказать мне, что я ошибаюсь. Но даже если Linux не ведет себя так сегодня, это может произойти завтра; и вы на 100% уверены, что вы никогда не будете запускать свое приложение на чем-либо, кроме Linux, никогда? Поэтому я не стал бы писать свое заявление с таким хрупким предположением. Я предлагаю вам решить, имеет ли блокирующая или неблокирующая семантика больше смысла для вашего собственного кода, и не пытайтесь играть во внутренние игры ядра.[обновление]
Я надеялся, что мне не придется копаться в Linux internals, но слишком самоуверенный downvoter довел меня до этого.
Начните с net/ipv4/tcp.c на уровне метка "new_segment" :
new_segment: if (!sk_stream_memory_free(sk)) goto wait_for_sndbuf; skb = sk_stream_alloc_skb(sk, 0, sk->sk_allocation); if (!skb) goto wait_for_memory;
Смотрите, как "wait_for_sndbuf" отличается от"wait_for_memory"? Вот о чем я говорю.
В метке "wait_for_memory" есть вызов
sk_stream_wait_memory
со значениемtimeo
, которое зависит от того, является ли это неблокирующим отправлением. Эта функция, в свою очередь, либо переводит процесс в спящий режим, либо возвращаетEAGAIN
в зависимости отtimeo
.[обновление 2]
Просто чтобы было понятно, на какой вопрос я отвечаю...Я интерпретируйте этот вопрос следующим образом: "если я знаю, что буфер отправки моего сокета имеет достаточное свободное пространство, есть ли какая-либо разница-производительность или нет-между блокирующим и неблокирующим
Предположение, конечно, возможно, если, например, ваш протокол должен отправить одно сообщение, а затем только отправить новое сообщение после получения ответа на предыдущее сообщение. В этом случае вы знаете, что буфер отправки всегда пуст, когда выsend
на этом сокете?"send
. Путем получения и / или установки POSIX-стандартная опция сокетаSO_SNDBUF
, вы можете знать, что ваш сокет имеет достаточное свободное пространство. В этом случае может ли блокирующийsend
вести себя иначе, чем неблокирующийsend
?Мое прочтение спецификации POSIX говорит "Да" в принципе. Мое чтение исходного кода Linux говорит" Да " на практике. Я, конечно, могу ошибаться, но для демонстрации этого потребуется кто-то более сведущий в POSIX или Linux, и никто из них до сих пор не ответил на этот вопрос.
[финал (?) обновление]
Вот что, по моему мнению, POSIX позволяет / требует от вас предполагать.
Если в буфере отправки достаточно свободного места, то блокирующее
send
не может быть заблокировано навсегда. Это тот же самый смысл, в котором вызовcalloc
с адекватной свободной виртуальной памятью не может блокировать навсегда. В конечном счете система найдет ресурсы, необходимые для отправки ваших данных.(обратите внимание, что то же самое не верно, когда буфер отправки заполнен, и в этом случае блокировка
send
может быть заблокирована навсегда в зависимости от того, что происходит на приемном конце розетки.)Однако, даже если в буфере отправки достаточно места, неблокирующий
send
все равно может вернутьEWOULDBLOCK
. Поэтому, если вы используете неблокирующие сокеты, ваш код должен обрабатывать это независимо от того, что вы знаете о буфере отправки или о чем-либо еще.
Имейте в виду, что вы не всегда можете быть уверены, что ваша отправка будет выполнена быстро.
Например, если сокет на другой стороне не считывается с помощью recv, ваш буфер будет заполнен.
Конечно, если вы пишете обе стороны приложения и всегда читаете, не будет существенной разницы в производительности, я думаю.
Единственный способ "гарантировать, что в буфере отправки сокета всегда будет место" - это не отправлять, когда он заполнен.
Вы действительно можете определить, есть ли место в буфере отправки-на некоторых системах. Если нет места в режиме блокировки, вы можете выбрать() для возможности записи - на некоторых системах. В других системах вы не можете сказать, есть ли место и / или вы не можете выбрать() в режиме блокировки.
В таких системах у вас нет никаких хороших вариантов реализации, кроме как отправить и заблокировать, или же использовать режим блокировки.
В системах, где вы можете знать, но не выбрать (), вы можете замкнуть цикл и спать, но вы не можете знать, как долго спать, поэтому вы будете спать слишком долго и тратить время, в отличие от блокировки, которая будет блокировать ровно на нужный промежуток времени.