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