Атомная запись на сокет Unix? - PullRequest
9 голосов
/ 12 января 2011

Я пытаюсь выбрать между pipe и unix-сокетами для механизма IPC.
Оба поддерживают функции select() и epoll(), что замечательно.

Теперь каналы имеют «атомарную» запись 4 КБ (на сегодняшний день), что гарантируется ядром Linux.
Существует ли такая возможность в случае сокетов Unix?Я не смог найти ни одного документа, в котором бы указывалось это явно.

Скажем, я использую сокет UNIX и записываю x байтов данных с моего клиента.Я уверен, что эти x байтов будут записаны на стороне сокета сервера, когда мой сервер взломает select()?

По той же теме, будет ли использование SOCK_DGRAM гарантировать, что запись является атомарной (если такая гарантиявозможно), поскольку предполагается, что датаграммы будут единичными четко определенными сообщениями?
В чем тогда будет разница при использовании SOCK_STREAM в качестве режима передачи?

Заранее благодарен.

Ответы [ 2 ]

11 голосов
/ 12 января 2011

Трубы

Да, неблокирующая емкость обычно составляет 4 КБ, но для максимальной переносимости вам, вероятно, лучше использовать константу PIPE_BUF.Альтернативой является использование неблокирующего ввода-вывода.

Больше информации, чем вы хотите знать, в man 7 pipe.

Unix-дейтаграмм-сокетах

Запись с использованием send семейство функций на сокетах дейтаграмм действительно гарантированно будет атомарным .В случае с Linux они также надежны и сохраняют порядок.(что делает недавнее введение SOCK_SEQPACKET немного смущающим меня) Много информации об этом в man 7 unix.

Максимальный размер датаграммы зависит от сокета.Доступ к нему осуществляется с помощью getsockopt/setsockopt на SO_SNDBUF.В системах Linux он колеблется между 2048 и wmem_max, по умолчанию wmem_default.Например, в моей системе wmem_default = wmem_max = 112640.(вы можете прочитать их из /proc/sys/net/core). Наиболее релевантная документация по этому вопросу находится в man 7 socket вокруг опции SO_SNDBUF.Я рекомендую вам прочитать его самостоятельно, так как описываемое им поведение удвоения емкости может сначала немного запутать.

Практические различия между потоком и датаграммой

Потоковые сокеты работают только при подключении.Это в основном означает, что они могут общаться только с одним пэром одновременно.Как потоки, они не гарантированно сохраняют «границы сообщений».

Дейтаграммные сокеты отключены.Они могут (теоретически) общаться с несколькими пэрами одновременно.Они сохраняют границы сообщений.

[Я полагаю, что новый SOCK_SEQPACKET находится между: подключен и сохраняет границы.]

В Linux оба надежны и сохраняют порядок сообщений.Если вы используете их для передачи потоковых данных, они, как правило, работают аналогично.Так что просто используйте тот, который соответствует вашему потоку, и позвольте ядру обрабатывать буферизацию для вас.

Необработанный тест сравнения потоков, дейтаграмм и каналов:

# unix stream 0:05.67
socat UNIX-LISTEN:u OPEN:/dev/null &
until [[ -S u ]]; do :;done
time socat OPEN:large-file UNIX-CONNECT:u

# unix datagram 0:05.12
socat UNIX-RECV:u OPEN:/dev/null &
until [[ -S u ]]; do :;done
time socat OPEN:large-file UNIX-SENDTO:u

# pipe 0:05.44
socat PIPE:p,rdonly=1 OPEN:/dev/null &
until [[ -p p ]]; do :;done
time socat OPEN:large-file PIPE:p

Ничего статистически значимого здесь.Мое узкое место, вероятно, читает большой файл.

2 голосов
/ 12 января 2011

Скажем, я использую сокет UNIX и записываю x байтов данных с моего клиента.Уверен ли я, что эти x байтов будут записаны на стороне сокета сервера при взломе select() моего сервера?

Если вы используете сокет AF_UNIX SOCK_STREAM, такого нетгарантия, то есть данные, записанные в одном write/send(), могут потребовать более одного read/recv() вызова на принимающей стороне.

По тому же вопросу использование SOCK_DGRAM гарантирует, что записи являются атомарными (еслитакая гарантия возможна), поскольку дейтаграммы должны быть отдельными четко определенными сообщениями?

С другой стороны, сокеты AF_UNIX SOCK_DGRAM необходимы для сохранения границ дейтаграммы и обеспечения надежности,Вы должны получить ошибку EMSGSIZE, если send() не может передавать дейтаграмму атомарно.Не уверен, что произойдет с write(), поскольку на странице руководства не сказано, что она может сообщить EMSGSIZE (хотя на страницах руководства иногда не отображаются все возвращенные ошибки).Я попытался бы переполнить буфер получателя дейтаграммами большого размера, чтобы увидеть, какие именно ошибки сообщают send/write().

Одним из преимуществ использования сокетов UNIX над каналами является больший размер буфера.Я точно не помню, каков предел буфера ядра канала, но я помню, что его недостаточно и я не могу его увеличить (это жестко закодированная константа ядра).fast_producer_process | slow_consumer_process было на несколько порядков медленнее, чем fast_producer_process > file && file > slow_consumer_process из-за недостаточного размера конвейерного буфера.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...