Есть ли разница между блокировкой и неблокирующей отправкой


Если приложение может гарантировать, что в буфере отправки сокета всегда будет место, будет ли блокировка и неблокирующая отправка иметь одинаковую производительность? В этом случае есть ли какие-либо преимущества в обоих подходах по сравнению с другими?

3 8

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, ваш буфер будет заполнен.

Конечно, если вы пишете обе стороны приложения и всегда читаете, не будет существенной разницы в производительности, я думаю.

Единственный способ "гарантировать, что в буфере отправки сокета всегда будет место" - это не отправлять, когда он заполнен.

Вы действительно можете определить, есть ли место в буфере отправки-на некоторых системах. Если нет места в режиме блокировки, вы можете выбрать() для возможности записи - на некоторых системах. В других системах вы не можете сказать, есть ли место и / или вы не можете выбрать() в режиме блокировки.

В таких системах у вас нет никаких хороших вариантов реализации, кроме как отправить и заблокировать, или же использовать режим блокировки.

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