【发布时间】:2016-10-19 03:51:27
【问题描述】:
考虑在非阻塞流模式套接字 (SOCK_STREAM) 上对 read() 的以下调用:
ssize_t n = read(socket_fd, buffer, size);
假设远程对等方不会关闭连接,也不会关闭连接的写入部分(从本地角度来看,读取部分)。
在 Linux 上,这种情况下的短读取 (n > 0 && n < size) 意味着内核级读取缓冲区已用尽,并且立即后续调用通常会失败并显示 EAGAIN/EWOULDBLOCK(它会除非新数据设法在两次调用之间到达)。
换句话说,在 Linux 上,只要size 足够大,对read() 的调用将始终消耗立即可用的所有内容。
与write() 类似,在Linux 上,短写入总是意味着内核级缓冲区已被填满,并且立即后续调用可能会失败并显示EAGAIN/EWOULDBLOCK。
问题 1:这在 macOS/OSX 上是否也有保证?
问题 2:这在 FreeBSD 上是否也有保证?
问题 3:这是 POSIX 要求/保证的吗?
我知道这在 Linux 上是正确的,因为 epoll 手册页中的以下注释(第 7 节):
对于面向流的文件(如管道、FIFO、流套接字),也可以通过检查目标文件读/写的数据量来检测读/写I/O空间耗尽的情况描述符。例如,如果您通过请求读取一定数量的数据来调用 read(2),而 read(2) 返回的字节数较少,则可以确定文件描述符的读取 I/O 空间已用尽。使用 write(2) 写入时也是如此。 (如果你不能保证被监控的文件描述符总是指向一个面向流的文件,请避免使用后一种技术。)
编辑:作为问题的动机,考虑这样一种情况,您希望同时处理多个套接字上的输入,无论出于何种原因,您都希望通过依次完全耗尽每个套接字的内核缓冲区来做到这一点(即“深度优先”而不是“广度优先”)。这显然可以通过在准备就绪的套接字上重复读取来完成,直到它以EAGAIN/EWOULDBLOCK 失败,但是如果之前的读取很短,最后一次调用将是多余的,我们知道短读取是保证用尽。
【问题讨论】:
-
问题 4:这在 Linux 上是否有保证?问题5:实际保证什么?你有什么问题?实际还会发生哪些其他行为?
-
正如我所说,我相信它在 Linux 上得到保证,并且这种保证来自
epoll手册页中提到的注释。如果你有理由相信我错了,请解释一下。 -
"believe" 是一个糟糕的编程基础。但是,我看不到会发生什么其他行为以及它会对应用程序逻辑产生什么变化。你想要数据,你轮询并阅读它。不能保证两个相邻的
reads 将在抢占式多任务系统上连续执行。您的应用程序应始终假定数据在read返回后立即到达。 -
@Olaf 我附加了一条说明,说明为什么我认为这个问题是相关的。你明白了吗?
-
@Olaf 我理解你的观点。在使用级别触发的轮询时,人们通常不会关心这一点,而只需在每次读取就绪通知时执行一次读取。然而,在边缘触发的情况下,必须耗尽内核缓冲区才能获得新的通知,并且您必须决定是否要以“深度优先”或“广度优先”的方式执行此操作。特别是对于“深度优先”的情况,尝试消除对
read()的一次调用是相关的。
标签: c macos sockets freebsd nonblocking