Как работает sendmsg?

Как вы знаете, sendmsg имеет следующее объявление:

int sendmsg(int s, const struct msghdr *msg, int flags);

и структура msghdr имеет следующий вид:

struct msghdr {
    void         * msg_name;     /* optional address */
    socklen_t    msg_namelen;    /* size of address */
    struct iovec * msg_iov;      /* scatter/gather array */
    size_t       msg_iovlen;     /* # elements in msg_iov */
    void         * msg_control;  /* ancillary data, see below */
    socklen_t    msg_controllen; /* ancillary data buffer len */
    int          msg_flags;      /* flags on received message */
};

Как вы видите, msghdr имеет массив буферов, iovec и счетчик буферов msg_iovlen. Что мне интересно, так это то, как sendmsg отправляет эти буферы. Он объединяет все буферы и отправляет или отправляет в цикле for?


person whoi    schedule 23.11.2010    source источник
comment
Сразу замечание: если это ради любопытства, то круто. Если вы пытаетесь написать что-то, что зависит от этих знаний, то вы почти наверняка делаете это неправильно и напрашиваетесь на неприятности.   -  person San Jacinto    schedule 23.11.2010
comment
@SanJacinto было бы полезно, если бы вы уточнили, почему написание чего-то, что зависит от этих знаний, вызывает проблемы. Вы можете уточнить, пожалуйста?   -  person Steve Lorimer    schedule 09.07.2012
comment
@lori Потому что документация предоставляет вам набор интерфейсов кода и говорит вам, чего от них ожидать. Интерфейсы очень медленно меняются. Базовый код не имеет такой гарантии. Если вы собираете знания о внутренностях и пишете свой код на основе этих знаний, то вам не следует удивляться, если вы обновите свое ядро ​​или какой-либо драйвер в сетевом стеке, и ваш код, который его вызывает, больше не работает. Ты сделал плохой выбор, если сделал это.   -  person San Jacinto    schedule 09.07.2012


Ответы (3)


На странице руководства говорится о сообщении (в единственном числе) и нескольких элементах (во множественном числе):

Для send() и sendto() сообщение находится в buf и имеет длину len. Для sendmsg() на сообщение указывают элементы массива msg.msg_iov. Вызов sendmsg() также позволяет отправлять вспомогательные данные (также известные как управляющая информация).

Для потокового сокета это не имеет значения в любом случае. Любые данные, которые вы отправляете, просто закончатся как один длинный поток данных на другой стороне.

Для сокетов дейтаграмм или сообщений я понимаю, почему было бы полезно немного больше ясности. Но оказывается, что вы отправляете только одну дейтаграмму или сообщение с одним вызовом sndmsg; не по одному на элемент буфера.

На самом деле я начал копаться в исходном коде Linux из любопытства и чтобы лучше понять этот ответ. Похоже, что send, а sendto — это всего лишь оболочки для sendmsg в Linux, которые создают для вас struct msghdr. Фактически, реализация UDP sendmsg освобождает место для одного заголовка UDP на вызов sendmsg.

Если производительность — это то, о чем вы беспокоитесь, не похоже, что вы выиграете от sendmsg, если передадите только один iovec. Однако, если вы объединяете буферы в пользовательском пространстве, это потенциально может принести вам пользу.

Он немного похож на writev, но с дополнительным преимуществом, заключающимся в том, что вы можете указать адрес назначения для использования с сокетами без установления соединения, такими как UDP. Вы также можете добавить вспомогательные данные, если вам это нравится. (Обычно используется для отправки файловых дескрипторов через сокеты домена UNIX.)

person Stéphan Kochen    schedule 23.11.2010
comment
Если я правильно прочитал код SCTP (SCTP поддерживает сокеты SOCK_SEQPACKET), это также 1 сообщение SCTP на вызов sendmsg. То же самое и для сокетов AF_UNIX SEQPACKET. - person ninjalj; 23.11.2010
comment
Если сообщение слишком длинное для атомарной передачи через базовый протокол, возвращается ошибка EMSGSIZE, и сообщение не передается. (linux.die.net/man/2/sendmsg) - разве это не точную спецификацию того, что linux делает в сокетах UDP/packet (атомарно!)? Или, наоборот, если бы Linux делал что-то другое, разве это не соответствовало бы спецификации? - person Aconcagua; 28.01.2016
comment
@Aconcagua Я думаю, вы смотрите на что-то другое. Материал iovec заключается в объединении нескольких буферов и отправке их в одно сообщение. EMSGSIZE — это то, что может произойти после этой обработки, если общее сообщение превысит некоторый предел протокола. - person Stéphan Kochen; 02.02.2016
comment
@Shtééf Ты так думаешь? С помощью «копания» кода вы узнали, что linux резервирует один заголовок UDP для каждого вызова sendmsg, и я указал причину, по которой это делается, потому что это требуется стандартом... - person Aconcagua; 05.02.2016
comment
С другой стороны, если вопрос действительно касается внутренних компонентов ОС, ответ ninjalj, возможно, больше подходит для этого конкретного вопроса... - person Aconcagua; 05.02.2016

Это зависит от вашего стека TCP/IP. Встроенные стеки TCP/IP потенциально могут отправлять различные iovecs непосредственно на сетевую карту. Но в обычных стеках TCP/IP уже должна быть копия из памяти пространства пользователя в память пространства ядра, поэтому выигрыша здесь нет, и iovecs концептуально копируются в один большой кусок памяти (это могут быть отдельные страницы памяти, если Драйвер поддерживает ввод-вывод scather/gather, но здесь важно то, что границы iovec не сохраняются).

person ninjalj    schedule 23.11.2010