如何避免坏 FD_SET 缓冲区溢出崩溃?
Posted
技术标签:
【中文标题】如何避免坏 FD_SET 缓冲区溢出崩溃?【英文标题】:How to avoid bad FD_SET buffer overflow crash? 【发布时间】:2014-04-03 03:50:26 【问题描述】:最近我被FD_SET
缓冲区溢出两次咬了。第一次是我们有太多的套接字(1024+)要添加到FD_SET
。这是一个测试用例,我们已禁用它,并添加assert
来检测此用例。
今天,当我们运行一个测试用例 1000 多次时,我们遇到了另一个相关问题。每次,测试用例都会以某种方式触发分配一个套接字,然后在测试用例完成之前释放它。当我们运行 1000+ 次时,此测试用例将遇到 FD_SET
缓冲区溢出。
我们找到了根本原因:
-
对于每一个pass,分配的socket id都会增加(+1),很长一段时间不会重复使用socket id。
Operating system
是MAC
,我认为避免使用已经发布的套接字而不发生错误是一个合理的设计。
FD_SET
只设置了fd_set
位数组以socket id 为索引,如果socket id 很大会溢出。我认为fd_set
是一个糟糕的设计。
我们认为 1000+ 是一个合理的数字。而且我们认为定义 MACRO 来设置 'fd_set' 巨大是不合理的,并且在等待时会浪费内存和 CPU。
我们不知道如何解决它,有什么建议吗?
-------------编辑1----
原来在其他地方有套接字泄漏,这违反了析构函数应该释放所有资源。这使得套接字 id 增加。
所以项目#1 是不正确的。操作系统将重用套接字 id。
但无论如何,讨论是有帮助的,FD_SET
是糟糕的设计,我们应该使用poll()
。
【问题讨论】:
如果它没有重用文件描述符(你称之为“socket id”),你就没有关闭文件描述符。尝试在您的测试程序运行一段时间后在其上运行lsof
。您可能会在输出中发现许多打开的套接字。
@JosephQuinsey,FD_SETSIZE 是 1024。
JosephQuinsey 和 Rob mayoff 如果你们中的任何一个人可以将您的 cmets 作为答案,我会将其掩盖为接受。
@robmayoff,我尝试在我的应用程序上运行 lsof,接缝套接字没有关闭,但我确定我已经从日志中调用了关闭。调用 close 函数非常简单,所以任何 cmets 为什么它不正确关闭?顺便说一句,lsof -g PID
在调试过程中不起作用,有什么原因吗?
"... 我认为避免使用已经发布的套接字而不发生错误是一个合理的设计。" 我会说这个假设是错误的。不要使用close()
ed file-/socket-descriptors!
【参考方案1】:
这个答案总结了 OP 以及 rob mayoff 和 Joseph Quinsey 找到的解决方案。
如果程序没有重用文件描述符(你称之为“socket 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 增加了。修复,操作系统会重用socket id。
但无论如何,讨论是有帮助的,
FD_SET
是糟糕的设计,我们应该使用poll()
。
请注意,类 Unix 系统总是(或通常)使用最小的可用文件描述符。例如,open(2)
的手册页状态;
成功调用返回的文件描述符将是当前未为进程打开的编号最小的文件描述符。
【讨论】:
编辑:答案Are there any platforms where using structure copy on an fd_set (for select() or pselect()) causes problems?在各种平台上有一些关于FD_SETSIZE
的信息。以上是关于如何避免坏 FD_SET 缓冲区溢出崩溃?的主要内容,如果未能解决你的问题,请参考以下文章