【问题标题】:How to tell, what "exceptional condition" caused select() to react to errorfds?如何判断是什么“异常情况”导致 select() 对 errorfds 做出反应?
【发布时间】:2026-02-02 14:40:01
【问题描述】:

根据其手册页,select() 系统调用提供对一个或多个文件描述符的三个不同方面的监控:它们是否准备好读取、准备写入,或者是否“错误”或“异常”条件”(语言不同)已经发生。其中哪一个应被监视,通过三个fd_set 参数指定,称为readfdswritefdserrorfds。虽然有很多关于正确使用 readfdswritefds 的优秀文档和示例,但我在 errorfds 上几乎找不到任何有用的内容。

无论如何,出于下面讨论的原因,我尝试使用 errorfds,确实发现了一些情况,其中我的 runloop 中的 select() 调用响应了其中一个上的“异常情况”文件描述符。例如,连接到 PTY 的 TTY,而后者正在从其主端关闭,则会引发这样的条件。

但是现在呢?我知道文件描述符上发生了一些“异常情况”,但是一般来说,我如何才能找出究竟是什么原因造成的呢?只看errno 肯定不会给出答案(那时它总是0)。是否有一些我应该注意的“神奇”ioctl

一些进一步的背景:我的一些程序(大部分是用 C 编写的)通过串行端口与外部硬件通信。为了测试,我还编写了一个简单的服务器,它创建了一个 PTY,它是我的其他程序可以连接到的对应的 TTY,就好像它是一个串行端口一样。虽然在基本层面上,所有这些都运行良好,但目前根本没有真正实现对错误或其他异常情况的处理,这有时会导致非常讨厌的行为。这需要改变!

我特别感兴趣的一个异常情况是连接是否已断开。例如,当一个端口消失时,我们会很高兴地注意到,例如,因为用户拔出了一个 USB 到串行适配器。正确处理读写错误似乎可以避免最严重的意外副作用,但我想知道我是否应该做更多(观看errorfds,或者可能是其他一些信号)。不幸的是,我对 UNIX 信号处理完全不熟悉。

【问题讨论】:

  • 我知道刚才指出的另一个问题,这确实与我的非常相似。但是,并不觉得它完全匹配。事实上,从那里给出的答案中,我想知道我的问题是否有任何答案,除了“不能在这个用例中使用errorfds”......但在继续之前想确定一下。

标签: c unix select low-level-io


【解决方案1】:

我知道文件描述符上发生了一些“异常情况”,但总的来说,我如何才能找出究竟是什么原因造成的呢?是否有一些我应该注意的“神奇”ioctl?

您应该尝试读取 0 个字节。至少在 linux 中,man 2 read 声明:

如果 count 为零,read() 可能会检测到下面描述的错误。

所以,在read(fd, NULL,0) 之后,您应该有一个errno,它可以告诉您更多信息,而无需实际阅读任何内容。手册页中的 weasel 词 may 意味着这可能不是很便携,但是(cf read(fd, NULL, 0); what does it do? is it well-defined?

【讨论】:

  • 为了更安全,您可以提供一个指向某些已分配内存的有效指针并请求 0 个字节:char c; if (read(fd, &c, 0) < 0) { …errno has some information in it… }。但是,在将 0 字节传递给函数(系统调用)时,您可能仍然会收到 EINVAL 错误。
  • @Jonathan:是的,这应该更可靠,但是,因为它是不可移植的,所以无论如何都是 YMMV。在 Steven 感兴趣的场景(断开连接)中,select() 无论如何都会被SIGHUP 中断,即使没有设置errorfds。我怀疑捕获SIGHUP 将是一种比read(fd,NULL,0) 技巧更便携、更可靠的方式来感知断开的连接