如何避免FD_SET缓冲区溢出崩溃?

3

最近我两次遭受了FD_SET缓冲区溢出的困扰。第一次是因为我们有太多的套接字(1024+)要添加到FD_SET中。这是一个测试用例,我们已经禁用它,并添加了assert来检测这种情况。

今天我们运行了一个1000+次的测试用例,遇到了另一个相关问题。每次,测试用例都会以某种方式触发分配套接字,然后在测试用例完成之前释放它。当我们运行1000+次时,这个测试用例将导致FD_SET缓冲区溢出。

我们已经找到了根本原因:

  1. 对于每一次通过,分配的套接字ID将增加(+1),它不会在很长一段时间内重复使用套接字ID。操作系统是MAC,我认为这是一个合理的设计,可以避免使用已经释放的套接字而不发生错误。
  2. FD_SET只使用套接字ID作为索引设置fd_set位数组,如果套接字ID很大,它将溢出。我认为fd_set是一个糟糕的设计。

我们认为1000+是一个合理的数字。我们不认为定义宏来设置“fd_set”巨大是合理的,并且会在等待时浪费内存和CPU。

我们不知道如何解决它,所以有什么建议吗?

-------------编辑1----------------

事实证明,在其他地方存在套接字泄漏,违反了析构函数应该释放所有资源的规定。这使得套接字ID增加。 因此,项目#1不正确。操作系统将重复使用套接字ID。 但无论如何,讨论是有帮助的,FD_SET是糟糕的设计,我们应该使用poll()


2
如果它没有重用文件描述符(你所谓的“套接字ID”),那么你就没有关闭文件描述符。尝试在测试程序运行一段时间后运行lsof命令。你可能会在输出中发现许多打开的套接字。 - rob mayoff
JosephQuinsey和Rob mayoff,如果你们中任何一位能将你的评论作为答案,我会将其标记为接受的答案。 - ZijingWu
@robmayoff,我尝试在我的应用程序上运行lsof,似乎套接字没有关闭,但我确定已经从日志中调用了close。调用close函数非常简单,所以有什么评论为什么它不能正确关闭?顺便说一下,lsof -g PID在调试过的进程上不起作用,有什么原因吗? - ZijingWu
我认为避免使用已经释放的套接字是一个合理的设计,这个假设是错误的。请不要使用已关闭的文件/套接字描述符! - alk
可能是增加FD_SETSIZE和select的限制的重复问题。 - mpromonet
显示剩余2条评论
1个回答

1
这篇答案总结了OP找到的解决方案,以及rob mayoff和Joseph Quinsey的评论。
如果程序没有重用文件描述符(你所说的“套接字ID”),它就没有关闭文件描述符。尝试在测试程序运行一段时间后运行lsof。你可能会在输出中发现许多打开的套接字。(但OP说lsof -g PID在调试过的进程上似乎不起作用)。
另外,尝试使用netstat -a -p --inet | grep process-name-or-pid
在某些系统上,有时仅使用close(fd)关闭套接字是不够的。如果你的套接字文件描述符不断增加,则答案close() is not closing socket properly可能会有所帮助。
为避免FD_SETSIZE问题,例如Increasing limit of FD_SETSIZE and select中的一些作者建议使用poll而不是select
最后,OP解决了这个问题:
“事实证明,在另一个地方有套接字泄漏,违反析构函数应该释放所有资源。这使得套接字ID增加。修复后,操作系统将重新使用套接字ID。”
“但无论如何,这次讨论很有帮助,FD_SET是糟糕的设计,我们应该使用poll()。”
请注意,类Unix系统始终(或通常)使用可用文件描述符中的最小值。例如,open(2)的man页面指出:
“成功调用返回的文件描述符将是进程当前未打开的编号最低的文件描述符。”

“编辑”:答案在哪些平台上,在fd_set(用于select()或pselect())上使用结构复制会导致问题?提供了有关各种平台上的FD_SETSIZE的信息。 - Joseph Quinsey

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接