【问题标题】:Will read() ever block after select()?在 select() 之后 read() 会阻塞吗?
【发布时间】:2011-07-18 03:26:17
【问题描述】:

我正在通过 TCP/IP 套接字读取数据流。流负载非常不均匀。有时每秒有大量数据到达,有时一个小时没有数据。在长时间不活动的情况下(远程服务器没有数据,但连接仍然在线)我的程序应该采取一些措施。

我正在使用 select() 实现超时。它告诉我是否有数据准备好,但我不知道在不导致 read() 阻塞的情况下我能读多少。阻塞是不可接受的,因为它的持续时间可能比我需要的超时时间长得多。

为了提高效率,流被读入大缓冲区,read() 调用提供了该缓冲区大小。

如果要填充的缓冲区大于套接字中当前可用的数据量,read() 是否会在 select() 之后阻塞?

【问题讨论】:

  • 我正在发生这种情况。 Select() 在从串行端口读取时返回然后 read() 块。我知道这是 2011 年的,但偶然发现了它。

标签: linux sockets


【解决方案1】:

实际上它不应该阻塞(这就是 select() 的用途!),但事实上,它可能,非常例外。通常, read() 应该返回您指定的最大字节数,其中可能包括零字节(这实际上是一个有效的事情发生!),但它不应该在之前报告就绪后阻塞。

不过,请参阅 Linux select 手册页:

在 Linux 下,select() 可能会报告一个 套接字文件描述符为“准备好 阅读”,尽管如此 后续读取块。这可以 例如,当数据有 到了但检查有错 校验和并被丢弃。也许有 是文件的其他情况 描述符被虚假报告为 准备好。因此使用起来可能更安全 O_NONBLOCK 在不应该的套接字上 块。

【讨论】:

  • 完全正确。继续使用select(),但将套接字设置为非阻塞,当select() 表示可读时,调用read() 直到它返回EAGAIN
  • 不,零字节不是发生的有效事情(EOF除外);请参阅"RETURN VALUE" 下的 recv() 的 POSIX 规范。 Linux select() 手册页记录的行为也违反了POSIX。这就是至少从 1985 年以来每个 Unix 的工作方式;如果你的 Unix 没有,那么你的 Unix 就坏了。
  • @Nemo:你想说什么? (1) 人们可以假设 EOF 和有序关闭是发生的有效事情,并且是程序应该预期并准备处理的条件。 (2) 这个问题是针对 Linux 的,只要回答指出 Linux 的特殊行为就足够了。 (3)虽然不相关,但这种行为并不像您所说的那样严格违反 POSIX,因为 POSIX 允许条件(OOB 数据的到达就是一个例子),即使随后的recv 将套接字标记为就绪会 阻塞。诚然,这是一个罕见的事件,但可能且合法。
  • 顺便问一下,我在linux-kernel list 上询问了 Linux select() 行为,并得到了 Alan Cox 的良好回应。
  • @DavidSchwartz:它隐含在“状态”和“O/S”的概念中。如果你 write 一个文件然后 read 它回来,你会得到你写的相同的字节。当然,如果其他人在此期间覆盖了该文件,那是不正确的……但否则它正确的。 O/S 不能随意地神奇地使你的套接字不可读,就像神奇地破坏你的文件一样。我有点惊讶有人会尝试以其他方式争论。 (甚至 Linux 开发人员也同意这违反了 POSIX;他们只是说这是故意违反速度。)再一次:在 Linux 之前没有 Unix 有过这样的行为。
【解决方案2】:

O_NONBLOCK 可以通过fcntl/F_SETFL 设置并且应该导致非阻塞read

【讨论】:

  • 你如何实现有效的超时?循环不是一种选择。睡眠周期对其响应时间不利。
  • 哦,我明白了。您的意思是选择和非阻塞读取的组合。不太方便,但如果没有更好的选择就可以了。
  • 会在非阻塞描述符上选择块吗?手册说如果下一个读取调用不会阻塞,它将返回。非阻塞描述符不会阻塞。
  • @Basilevs:我很确定select() 将等待非阻塞描述符上包含数据(或文件结尾)。
  • @Basilevs,我的手册说“readfds 中列出的那些将被监视以查看字符是否可供读取(更准确地说,查看读取是否不会阻塞;特别是文件描述符在文件结束时也准备好了)”。我想这是在read 即使没有可用字符时也会立即返回的情况下修复原始句子的方法。顺便说一句,不要忘记select 可能会在描述符上的任何事件之前被信号中断,否则会发生超时。
【解决方案3】:

一个阻塞的文件描述符会阻塞在 read() 上,直到有东西要读取——可能是一个字节或你的整个请求。如果没有可读取的内容,非阻塞描述符不会在 read() 上阻塞。 Select() 不是 read()。它基本上使进程进入睡眠状态并监视文件描述符,包括非阻塞描述符。当其中一个描述符有活动(或超时期限到期)时,选择返回,您可以读取数据,或者在超时的情况下执行其他操作。

所以你有两个不同的问题。 (1) 在没有数据的情况下,你想“采取一些行动”。那是选择超时。 (2) 一旦有了数据(通过选择通知),您就不想阻止读取。这就是非阻塞模式。当您在非阻塞读取上获得 EAGAIN 时,您将循环回选择和/或“采取一些行动”并循环回选择。

【讨论】:

    【解决方案4】:

    不,read() 将读取到指定大小并返回实际读取的字节数,可能会更少。

    【讨论】:

    • read 将阻塞如果根本没有可用的字节,并且如果描述符处于阻塞模式,它还不是文件结尾,我记得。
    【解决方案5】:

    你可以使用默认不阻塞的recv()(如果没有指定标志MSG_WAITALL)

    【讨论】:

    • 我希望代码尽可能与未命名的管道兼容,但我不确定 recv 是否可以与它们一起使用。
    • -1 手册说:如果套接字上没有可用的消息,则接收调用会等待消息到达,除非套接字是非阻塞的(参见 fcntl(2)),在这种情况下值 -返回 1 并将外部变量 errno 设置为 EAGAIN。
    • @Basilevs:MKo 肯定是指 MSG_DONTWAIT
    猜你喜欢
    • 1970-01-01
    • 2012-02-11
    • 2013-10-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多