sendmsg/recvmsg 中的部分 read/write 问题

Partial read/write issue in sendmsg/recvmsg

我正在编写一个程序,通过域套接字使用 sendmsgrecvmsg 在两个进程之间传递文件描述符。为了发送文件描述符,附加数据包含在 msghdr.msg_iovmsghdr.msg_iolen 中。然而,我被告知类似于正常的 readwrite 系统调用,sendmsgrecvmsg 也有部分 read/write 问题。在这种情况下,是否会为每个部分数据自动复制辅助字段中的数据?我问这个是因为我的实现需要非阻塞机制。让我用下面的例子来详细说明一下

发件人:发送 msghdr 数据,其中包含辅助字段中的 fdmsg_iov

中的 K 字节

接收器:(1) 部分读取,K1 字节 (2) 部分读取,K-K1 字节

现在像上面的例子,其实我应该是在所有数据都到达的时候,对步骤(2)之后的数据进行处理。在这种情况下,我还能从辅助字段中正确提取 fd 吗?还是只出现在第一次部分阅读中?

通过快速查看内核源代码(linux,但请参阅下文),我相信您需要确保仅发送一次辅助数据。也就是说,在非阻塞模式下,如果接收套接字没有空间,你将返回EAGAIN/EWOULDBLOCK,并且不会发送数据和辅助数据。但是如果接收方有一些space,那么会发送数据的初始部分,同时发送辅助数据。然后您会收到一个 return 字节计数,指示部分发送,但辅助数据将已发送。

当您尝试发送消息的其余部分时,您需要注意这一点,因为内核不会维护您之前发送的部分缓冲区的内存,后续缓冲区在逻辑上是连续的(真的没有办法它可以——你可以发送它所知道的完全不同的数据)。因此,如果您只是为后续缓冲区部分提供相同的辅助数据,我相信内核会很乐意为您的后续缓冲区部分再次提供辅助数据。这可能会导致接收方出现重复的文件描述符(您可能会忽略关闭,因为您不会期望它们)——如果您不避免的话。

现在,如果您在发送端处于阻塞模式,并且传输被分成多个部分,则辅助数据将仅发送一次——与第一个缓冲区部分一起发送,因为发送整个缓冲区仍然在内核控制范围内。

因此,在接收端,如果您没有收到完整的逻辑消息,则需要注意辅助数据伴随着接收到的数据的第一块。

我相信此行为与@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 部分已经被发送已发送。