2009-10-01 92 views
5

我有一个异步应用程序执行多个线程,在套接字上执行操作,然后异步执行操作。避免在多线程套接字应用程序中重复使用相同的fd号

我试图避免一旦调度一个套接字的读取操作时,套接字被关闭并在第一次操作开始执行之前重新打开(可能是另一个操作中的另一个对象),这将最终读取正确的文件描述符,但错误的对等体。

问题出在因为(accept(); close(); accept())在accept()中返回相同的fd会导致上述情况。

我看不到一种避免它的方法。

任何提示?

+0

为什么插入一个线程的线程被另一个线程关闭? – 2009-10-01 14:51:29

+0

这就是异步系统的性质,一个操作读取,另一个写入,另一个可能关闭,并且它们全部可以在不同的线程中执行。 – 2009-10-01 14:53:12

+2

重新思考你的方法。通常有一个接受连接的线程,并产生线程来处理它们。那些线程然后负责关闭连接,并且其他线程无法访问该连接。 – 2009-10-01 14:58:57

回答

3

好的,找到答案。

这里最好的方法是调用accept()并获得最低的fd可用,使用您已知的数字(如dup2(6,1000)和close(6))复制它,您现在可以控制fd范围你用。

接下来再接受6或类似的,我们将dup2(6,999);并继续降低,如果太低,则重新设置。

由于接受在同一个线程和DUP2一直在做和关闭不贵相比,接受它总是做有它非常适合我的需求。

2

如何管理套接字?这听起来像你有多个线程的任何一个都可以:

  1. accept传入的连接
  2. close现有连接
  3. 作出新的传出连接

这听起来像你需要一种方法调解进入周围浮动的各种插座。您是否考虑过将每个套接字与互斥锁相关联,以防止在套接字仍在使用时关闭套接字,或者可能将每个套接字描述符放入一个带有原子引用计数的结构中,以防止其他线程在所有线程完成使用之前关闭它?

+0

我可以这样做,但在这个几乎无锁的系统中添加互斥体是我试图避免的。 最好的事情是保证接受不会顺序给予fd,但我看不到发生这种情况。 – 2009-10-01 15:17:25

+0

@Arkaitz - 那么我提到的第二个解决方案可能对你有好处。将套接字保存在带有引用计数器的结构中,并且不要让任何线程关闭具有大于零的引用计数的套接字。没有锁,并且在所有线程都完成之前,套接字不会关闭。 – 2009-10-01 17:46:54

1

套接字是一个5元组{local-addr,local-port,remote-addr,remote-port,proto},所以如果你能够使用这些属性而不是fd来处理事件/处理程序路由,你可以避免fd冲突。

另一种选择是序列化所有的close()/接受()操作(重点是什么?),使他们不能混用

+1

我仍然需要互斥体来保护这些结构,这会使系统更加缓慢,这是我试图避免锁定的边缘情况。 – 2009-10-01 15:18:36

0

保持挂起操作的计数(读/写,等等),每个插座以及套接字上是否有未决的关闭请求。在您要求关闭之前,请先检查是否有任何挂起的操作。如果有,请调用关机代替,然后只在挂起操作计数达到0时调用close。

1

好问题!我甚至没有意识到可能会出现这样的问题。

我能想到的唯一答案是你没有使用close()来指示套接字被终止。一种解决方案是使用shutdown()来终止连接。然后可以通过引用计数来安全地关闭()插槽。

+0

'close'函数递减底层操作系统中的套接字引用计数,'shutdown'强制关闭并向对等方发送EOF/FIN。看到这个SO帖子的更多细节:http://stackoverflow.com/questions/409783/socket-shutdown-vs-socket-close/598759#598759 – 2009-10-01 17:50:41

+0

正如您发布的链接中提到的,shutdown()不会关闭套接字,只是终止沟通。 Shutdown()会导致任何未决(或未来)的I/O操作失败,但不会释放FD,因此不会发生所描述的问题。在操作失败的情况下,执行该操作的线程将递减引用计数(当线程接收到套接字时,此引用计数已增加),如果该值为零则关闭套接字。显然这个refcount是在程序中实现的,而不是操作系统的引用。 – TrayMan 2009-10-02 04:45:01

+0

你甚至不需要担心引用计数,如果读操作失败就关闭套接字。 – atomice 2009-10-02 15:40:25

1

我仍然会小心使用dup2()到一个众所周知的fd值。请记住,dup2()将在复制之前在目标上执行关闭;如果你开始打开1000个文件,这可能会与一些不相关I/O的无关线程发生冲突。

如果我是你,给你坚持在制约,我会用DUP()互斥内(不()DUP2)。 (如果你关心它,也许是per-fd mutexes)。

+0

东西是accept&dup2,所有东西都会在同一个线程上执行。所以我可以从MAX_FD跳到10左右,然后重新开始,当MAX_FD刚刚关闭时,dup2()将fd设置为MAX_FD的可能性,因为我们再次开始并不真正被考虑。 – 2009-10-02 06:38:05

+0

但是,你能证明你没有在做无关的文件吗?或者也许某个图书馆代表你做了什么?你能证明自然发生的FD的数量最终不会达到1000吗?也许你可以保证这些事情,但这样的事情会让我对你的方法感到紧张。 – asveikau 2009-10-05 18:46:22