2009-02-12 55 views
24

select(2)函数在其正在监视的文件描述符被另一个线程关闭时的行为是什么?如果关闭(2)单独线程中的文件描述符,select(2)会做什么?

从一些粗略的测试,它并立即返回。我怀疑结果是(a)它仍然在等待数据,但如果你真的试图读取它,你会得到EBADF(可能 - 有潜在的种族)或(b)它假装像文件描述符从来没有被传入。如果后一种情况是真的,传递一个没有超时的fd会导致死锁,如果它被关闭。

+0

[从套接字选择突破]的可能重复(http://stackoverflow.com/questions/2486727/breaking-out-from-socket-select) – iammilind 2016-05-04 13:22:41

+0

我认为它是不同的,虽然稍有关系。另一个问题是明确地询问如何从另一个线程中抛出一个`select()`(而`pipe()`是个很好的答案),而我的更多的是关于`select()`的行为, )`ed socket。在下面的答案中,你会看到答案是,“这取决于”。 – 2016-05-10 15:15:45

+0

我继续发现的一个更神秘的bug追捕原来只是由于这个问题:线程A在socket #x上选择了线程B关闭。此后不久,线程C创建了一个新的套接字,这也恰好是套接字#x(因为网络堆栈选择重新使用新套接字的数字x)。此时,线程A(仍然尝试使用套接字#x)开始在线程C的套接字上选择/读取/写入数据,尽管它们之间完全没有逻辑连接。这是一个追查的完全痛苦。 – 2017-01-20 20:53:13

回答

21

从一些额外的调查,似乎都DWC和bothie是正确的。

bothie's answer问题归结为:它是未定义的行为。这并不意味着它是不可预测的,但不同的操作系统会以不同的方式进行操作。在这种情况下,似乎Solaris和HP-UX系统从select(2)返回,但Linux不基于2001年的this post to the linux-kernel mailing list.

linux-kernel邮件列表上的参数基本上是未定义的(并且已损坏)行为依赖。在Linux的情况下,在文件描述符上调用close(2)会有效地减少引用计数。由于有一个select(2)调用也参考它,该fd将保持打开并等待输入,直到select(2)返回。这基本上是dwc's answer。你将在文件描述符上得到一个事件,然后它将被关闭。试图读取它将导致一个EBADF,假设fd没有被回收。 (关注MarkR在his answer中所做的,尽管我认为在大多数情况下可以通过适当的同步来避免)

所以,非常感谢大家的帮助。

6

我希望它会表现得好像档案结尾已经达成,这就是说,它会以显示为准备文件描述符,但任何尝试读取它随后将返回“错误的文件描述符返回”。

话虽如此,这样做是非常不好的做法,反正你总是有潜在的竞争条件与相同数量的另一文件描述符可以通过另一个线程后,立即另2日关闭了它打开,那么选择线程将最终等待错误的线程。

只要你关闭一个文件,它的号码变成可再利用,并可以通过下一次调用得到重用开(),插座()等,即使被另一个线程。因此,你真的需要避免这种事情。

+0

我以为它可能会随时准备就绪,但这并不完全正确:描述符实际上并未处于就绪状态 - 它已关闭。正如你所提到的,到你使用它的时候,它可能会被重新分配给别的东西。 – 2009-02-12 22:07:30

2

这是一个有点混乱,你问...

选择()在一个“有趣”的变化应该返回。如果close()仅仅减少了引用计数并且文件仍然处于打开状态以便写入某处,那么没有理由使select()唤醒。

如果其他线程上唯一的开放描述那样接近(),然后它变得更有趣,但我需要看到代码的简单版本,看看是否有什么地方真的错了。

5

select系统调用是一种等待文件desctriptors改变状态,而程序没有任何其他的事情。主要用于服务器应用程序,这些应用程序打开一堆文件描述符,然后等待它们执行任何操作(接受新连接,读取请求或发送响应)。这些文件描述符将以非阻塞io模式打开,以便服务器进程在任何时候都不会挂在系统调用中。

这也意味着,不需要单独的线程,因为所有可以在线程中完成的工作都可以在选择调用之前完成。如果工作需要很长时间,而不是可以中断,选择使用timeout = {0,0}调用,文件描述符被处理,之后工作正在恢复。

现在,你在另一个线程关闭文件描述符。为什么你有这个额外的线程,为什么它会关闭文件描述符?

POSIX标准没有提供任何提示,在这种情况下会发生什么,所以你在做什么是未定义的行为。预计在不同的操作系统之间,甚至是同一操作系统的版本之间,结果会有很大的不同。

问候,博多