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

Как известно sendmsg это заявление:

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

и непосредственно передаваемых параметров структура имеет такой вид:

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?

3 ответов


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

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

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

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

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

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

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


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


согласно http://opengroup.org/onlinepubs/007908799/xns/sendmsg.html ...

данные из каждой области хранения указанные msg_iov отправляется в свою очередь.

моя интерпретация такова sendmsg() не будет объединять данные сообщения, хранящиеся в iovec; каждый будет отправлен как отдельное сообщение.

[редактировать: моя интерпретация не была правильной; см. другие ответы для лучшего объяснение.]