select(2)
当一个正在监视读取的文件描述符被另一个线程关闭时,该函数的行为是什么?
从一些粗略的测试,它确实立即返回.我怀疑结果要么是(a)它仍然继续等待数据,但如果你真的试图从它读取你会得到EBADF(可能 - 有一个潜在的竞赛)或(b)它假装好像文件描述符从未传入.如果后一种情况为真,传入一个没有超时的fd将导致死锁,如果它被关闭.
从一些额外的调查来看,似乎dwc和bothie都是正确的.
对这个问题的回答归结为:它是未定义的行为.这并不意味着它必然是不可预测的,但不同的操作系统会以不同的方式做到这一点.看起来像Solaris和HP-UX这样的系统select(2)
在这种情况下会回归,但Linux并没有基于这篇文章从2001年开始的linux-kernel邮件列表.
linux-kernel邮件列表中的参数基本上是它依赖的未定义(和破坏)行为.在Linux的情况下,调用close(2)
文件描述符有效地减少了对它的引用计数.由于还有一个select(2)
调用也引用了它,因此fd将保持打开并等待输入直到select(2)
返回.这基本上是dwc的答案.您将在文件描述符上获得一个事件,然后它将被关闭.尝试从中读取将导致EBADF,假设fd尚未被回收.(MarkR在他的回答中提出了一个问题,尽管我认为在大多数情况下可以通过适当的同步来避免这种情况.)
谢谢大家的帮助.
我希望它的行为就好像已经到达文件结尾一样,也就是说,它将返回文件描述符显示为就绪,但任何随后读取它的尝试都会返回"错误的文件描述符".
话虽如此,这样做是非常糟糕的做法,因为你总是有潜在的竞争条件,因为另一个文件描述符具有相同的数字可以由另一个线程在另一个第二个关闭它之后立即打开,然后选择线程将结束等待错误的一个.
一旦关闭文件,其编号就可以重用,并且可以在下次调用open(),socket()等时重用,即使是另一个线程也是如此.所以你真的,真的需要避免这种事情.
select系统调用是一种等待文件desctriptors改变状态的方法,而程序没有任何其他操作.主要用于服务器应用程序,它打开一堆文件描述符,然后等待它们做任何事情(接受新连接,读取请求或发送响应).这些文件描述符将以非阻塞io模式打开,这样服务器进程就不会在系统调用中挂起.
这另外意味着,不需要单独的线程,因为可以在线程中完成的所有工作也可以在选择调用之前完成.如果工作需要很长时间,而不是可以中断,则选择使用timeout = {0,0}进行调用,处理文件描述符,然后恢复工作.
现在,您在另一个线程中关闭文件描述符.为什么你有这个额外的线程,为什么它会关闭文件描述符?
POSIX标准没有提供任何提示,在这种情况下会发生什么,所以你正在做的是未定义的行为.预计不同操作系统之间的结果将会非常不同,甚至在相同操作系统的版本之间也会有所不同.
此致,Bodo