Атомарная запись в сокет unix?

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

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

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

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

Заранее спасибо.


person Gui13    schedule 12.01.2011    source источник
comment
Я удалил свой ответ, так как я просто не знаком с семейством сокетов AF_UNIX. Когда я ответил, я не думал, что вы имели в виду Unix Socket == AF_UNIX. На странице руководства действительно говорится, что дейтаграммы с сокетами Unix полностью надежны. Так что это может быть вариант для вас. Но так как я ими не пользовался, оставлю это кому-нибудь более осведомленному.   -  person Mark Wilkins    schedule 12.01.2011


Ответы (2)


Трубы

Да, неблокирующая емкость обычно составляет 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

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

person JB.    schedule 12.01.2011
comment
Что касается неблокирующей емкости, обратите внимание, что неблокирующая и атомарная (то, что задал OP) - ортогональные вещи. Атомарная запись не подразумевает и не делает ее неблокирующей, а неблокирующая запись может быть неатомарной, что означает, что не все данные были записаны за один вызов write (), но этот вызов не заблокировал поток. - person Maxim Egorushkin; 12.01.2011
comment
Хотел бы я включить какую-нибудь практическую демонстрацию размера блока, как я сделал в stackoverflow.com/questions/2552402/cat-file-vs-file/, но pv не работает с сокетами, и если я пропущу через него, узкое место перейдет в трубы. Ну что ж. Последний абзац пока остается непроверенным. - person JB.; 12.01.2011
comment
@Maxim, вы правы насчет различения неблокирующего / атомарного (хотя я чувствую, что мы имеем в виду несколько разные интерпретации атомарности). Я вспоминал последнее погружение в эту часть источников Linux, что они были более коррелированы, чем я первоначально думал. Но поскольку ОП на самом деле не сказал, что ему нужно, или какой вариант Unix он использовал, я думаю, что отредактирую для ясности и оставлю все как есть, пока он не прокомментирует. - person JB.; 12.01.2011
comment
Из просмотра pubs.opengroup.org/onlinepubs/9699919799/functions/send.html и pubs.opengroup.org/onlinepubs/9699919799/functions/write .html похоже, что POSIX технически не гарантирует, что запись в сокеты DGRAM / SEQPACKET (unix) будет атомарной, но любая реализация, не обеспечивающая атомарность для этих двух типов, такова. фундаментально сломан, что я даже не буду пытаться найти обходной путь и просто запанику, если обнаружу это. - person PSkocik; 17.09.2019

Скажем, я использую сокет 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 из-за недостаточного размера буфера канала.

person Maxim Egorushkin    schedule 12.01.2011