我应该在 select() EBADF 上断言失败吗?

Should I assert fail on select() EBADF?

我正在尝试修复调用 select() 的事件循环中的错误。当 select() returns EBADF 时,记录错误,然后重新初始化 fd 集并再次调用 select。这导致日志记录的无限硬循环,在几秒钟内生成千兆字节的日志。

如果我的程序连接到的其中一个 tcp 服务器发生不正常的断开连接(例如它出现段错误),则会发生此错误。在这种情况下,理想情况下我希望我的程序删除该 fd 并保留 运行(如果不可行则关闭)。

我的问题是,select() 应该返回 EBADF,还是表明我的程序有问题? IE。我应该在 EBADF 上断言失败,否则我应该如何处理它?我会循环遍历 fd 集以找到 "bad" 文件描述符吗?

是的,我认为 assert() 是个好主意。

来自 select(2) 手册页:

EBADF An invalid file descriptor was given in one of the sets. (Perhaps a file descriptor that was already closed, or one on which an error has occurred.)

这意味着您传递的文件描述符实际上与有效的打开文件不匹配。

不会导致 fd 使 select 失败并显示 EBADF 的场景示例:

  • 关闭连接的远程套接字端点。 (recv returns 0).
  • 拔下网络连接(甚至 USB 设备)。

查看Linux内核源码,我们看到 select 可以 return EBADF 如果确定您的集合中传递的其中一个fd确实不对应于您进程中打开的文件。这是在 fs/select.c

中的 max_select_fd 中检查的

在那次检查之后,do_select 只会 return 计数 "interesting" 个 fds。基础文件的 file_operations.poll 函数甚至不能使 do_select return 有任何不同。

现在看起来 完全 不可能 select return EBADF 对于 any 场景,除了通过关闭或从未打开的 fd 的编程。

My question is, should select() ever be returning EBADF, or is that an indication that my program is buggy?

select() returns EBADF 如果您在 fd_set 之一中传递无效的描述符。你不应该这样做,它表明你的程序中有一个错误 - 也许你正在关闭()某处的文件描述符但没有将它从 fd_set.

中删除

您的代码中有错误。修理它。在某个地方,您正在关闭一个套接字,而没有将其从选择器使用的 FD 集中删除。否则你刚刚创建了一个不是 FD 的 FD,并在 FD 集中使用它。

与此处的其他陈述相反,网络问题不会导致此错误。网络中断不会关闭套接字,这是它们变为无效的唯一方式。只有关闭它们才能做到这一点。如果您继续写入,连接不工作的套接字最终将导致 ECONNRESET。对端已断开连接的套接字将变为可读,并且其上的 recv() 将 return 为零。