Websocket 为什么操作系统限制文件描述符?

Websocket 为什么操作系统限制文件描述符?,websocket,operating-system,message-queue,zeromq,file-descriptor,Websocket,Operating System,Message Queue,Zeromq,File Descriptor,我在尽力研究实现消息队列服务器的最佳方法之后提出了这个问题。为什么操作系统会限制进程和全局系统可以拥有的打开文件描述符的数量? 我当前的服务器实现使用zeromq,并为每个连接的websocket客户端打开订户套接字。显然,单个进程只能在fds的限制下处理客户端。 当我研究这个主题时,我发现了很多关于如何将系统限制提高到高达64k fds的级别的信息,但它从来没有提到它如何影响系统性能,以及为什么它一开始是1k或更低? 我目前的方法是尝试在自己的循环中使用一个协同路由,以及所有客户端及其订阅通道

我在尽力研究实现消息队列服务器的最佳方法之后提出了这个问题。为什么操作系统会限制进程和全局系统可以拥有的打开文件描述符的数量? 我当前的服务器实现使用zeromq,并为每个连接的websocket客户端打开订户套接字。显然,单个进程只能在fds的限制下处理客户端。 当我研究这个主题时,我发现了很多关于如何将系统限制提高到高达64k fds的级别的信息,但它从来没有提到它如何影响系统性能,以及为什么它一开始是1k或更低?
我目前的方法是尝试在自己的循环中使用一个协同路由,以及所有客户端及其订阅通道的映射,将消息发送给所有客户端。但我很想听到一个关于文件描述符限制的可靠答案,以及它们如何影响那些试图在每个客户端级别上使用它们并具有持久连接的应用程序?

当您有很多潜在的文件描述符时,某些操作会减慢速度。一个例子是操作“关闭除
stdin
stdout
stderr
之外的所有文件描述符”——唯一可移植的方法是尝试关闭除这三个描述符之外的所有可能的文件描述符,如果可能打开数百万个文件描述符,这可能会成为一个缓慢的操作

*:如果你愿意不携带,你可以查看
/proc/self/fd
——但这不是重点


这不是一个特别好的理由,但它是一个理由。另一个原因是为了防止有缺陷的程序(即“泄漏”文件描述符的程序)消耗过多的系统资源。

这可能是因为文件描述符值是文件描述符表的索引。因此,可能的文件描述符的数量将决定表的大小。一般用户不希望他们的ram的一半被一个文件描述符表占用,该表可以处理数百万个他们永远不需要的文件描述符。

为了提高性能,打开的文件表需要静态分配,因此其大小需要固定。文件描述符只是该表中的偏移量,因此所有条目都需要是连续的。您可以调整表的大小,但这需要停止进程中的所有线程,并为文件表分配新的内存块,然后将旧表中的所有条目复制到新表中。这不是您想要动态执行的操作,尤其是当您执行此操作的原因是因为旧表已满时

在unix系统上,进程创建fork()和fork()/exec()习惯用法要求迭代所有可能的进程文件描述符,试图关闭每个进程文件描述符,通常只保留少数文件描述符,如stdin、stdout、stderr未被触及或重定向到其他地方

由于此是启动进程的unix api,因此必须在创建新进程时执行,包括执行shell脚本中调用的每个非内置命令

其他考虑的因素是,虽然一些软件可以使用<代码> SysCONF(OpenSyMAX)动态地确定一个进程可能打开的文件的数量,<强>很多软件仍然使用C库的默认<代码> FDYStSimule,这通常是1024个描述符,因此,无论管理定义的上限如何,打开的文件都不能超过这么多。

Unix有一个基于文件描述符集的传统异步I/O机制,该文件描述符集使用位偏移量表示要等待的文件以及准备就绪或处于异常状态的文件。它不能很好地扩展数千个文件,因为每次运行循环时都需要设置和清除这些描述符集。较新的非标准API出现在主要unix变体上,包括*BSD上的
kqueue()
和Linux上的
epoll()
,以解决处理大量描述符时的性能缺陷

需要注意的是,
select()/poll()
仍然被许多软件使用,因为很长一段时间以来它一直是用于异步I/O的POSIX api。现代POSIX异步IO方法现在是
aio*
api,但它可能无法与
kqueue()
epoll()
api竞争。我没有在anger中使用aio,而且它肯定没有本地方法提供的性能和语义,因为它们可以聚合多个事件以获得更高的性能。*BSD上的kqueue()对于事件通知具有非常好的边缘触发语义,允许它替换select()/poll(),而无需对应用程序进行大的结构更改。LinuxEPoll()继承了*BSD kqueue()的先河,并对其进行了改进,而后者又继承了Sun/Solaris ePorts的先河

结果是,增加整个系统中允许打开的文件的数量会增加系统中每个进程的时间和空间开销,即使这些进程无法根据所使用的api使用这些描述符。对于允许打开的文件数量,还存在聚合系统限制。这提供了一些关于维持开放连接的开销的见解,但“仅”将10K连接视为珠穆朗玛峰


unix系统编程的最佳参考可能是W.Richard Stevens,这在unix平台上是正确的。在windows上使用文件句柄,默认情况下,一个进程可以分配1600万个句柄。句柄表是动态分配的,因此内存比句柄更可能耗尽。但是如果你的手柄用完了,奇怪的事情就会发生。例如,select()的FDs-FD掩码越多,也会占用更多的空间(可能还有时间)。我可以看出,提高限额并不是一件动态的事情。我可以