我正在编写一个程序,使用sendmsg和recvmsg通过域套接字在两个进程之间传递文件描述符.对于发送文件描述符,msghdr.msg_iov和msghdr.msg_iolen中包含其他数据.但是,我被告知类似于正常的读写系统调用,sendmsg和recvmsg也有部分读/写问题.在这种情况下,是否会为每个部分数据自动复制辅助字段中的数据?我问这个是因为我的实现需要非阻塞机制.让我用下面的例子来详细说明一下
发送者:发送msghdr数据,其中包含辅助字段中的fd和msg_iov中的K字节
接收器:(1)部分读取,K1字节(2)部分读取,K-K1字节
现在如上例所示,当所有数据到达时,我应该在步骤(2)之后实际处理数据.在这种情况下,我还能从辅助字段中正确提取fd吗?或者它只出现在第一次部分读取?
最佳答案 从内核源代码(
Linux,但见下文)的快速知识,我相信你需要确保辅助数据只被发送一次.也就是说,在非阻塞模式下,如果接收套接字中没有空间,您将返回EAGAIN / EWOULDBLOCK,并且不会发送数据或辅助数据.但是如果接收方有一些空间,那么将发送数据的初始部分,并且还将发送辅助数据.然后,您将收到指示部分发送的返回字节计数,但辅助数据将已发送.
当你尝试发送剩余的消息时,你需要注意这一点,因为内核没有内存,你以前发送过一个部分缓冲区,后续缓冲区在逻辑上是连续的(实际上它没办法 – 你可以为它所知道的所有人发送完全不同的数据.因此,如果您只为后续缓冲区部分提供相同的辅助数据,我相信内核将很乐意再次使用您的后续缓冲区部分提供辅助数据.这可能会导致接收方上的文件描述符重复(你可能会忽略它,因为你不会期望它们) – 如果你不避免它.
现在,如果您在发送端处于阻塞模式,并且传输被分成多个部分,则辅助数据将仅发送一次 – 使用第一个缓冲区部分,因为整个缓冲区的发送仍在内核控制之内.
因此,在接收方,如果尚未收到整个逻辑消息,则需要注意辅助数据伴随第一块接收数据.
我相信这种行为与@ Klas-Lindbäck(https://unix.stackexchange.com/questions/185011/what-happens-with-unix-stream-ancillary-data-on-partial-reads)给出的stackexchange参考中报告的一致. (但这个问题并没有涉及非阻塞模式.)
这个答案特定于linux.因此,结果在其他操作系统上的结果肯定会有所不同,尽管我很难看出它们如何显着不同并且仍然保持理智的语义.内核无法合理地维护先前发送的内存,并且sendmsg原型不允许它覆盖用户的msghdr以反映已经发送了msg_control部分.