套接字接受 - “打开的文件太多”

90
我正在做一个学校项目,需要编写多线程服务器,现在我正在运行一些测试来将其与Apache进行比较。我使用autobench来帮助进行测试,但是当我运行了几个测试或者给它太高的速率(大约600以上)来建立连接时,就会出现“打开文件过多”错误。
在处理请求后,我总是对套接字执行close()操作。我已经尝试使用shutdown()函数,但似乎没有任何帮助。有什么解决方法吗?
13个回答

88

在Linux中,有多个地方可以限制您可以打开的文件描述符数量。

您可以检查以下内容:

cat /proc/sys/fs/file-max

这将给出文件描述符的系统级限制。

在Shell级别上,这将告诉你个人限制:

ulimit -n

这可以在 /etc/security/limits.conf 文件中更改,它是 nofile 参数。

然而,如果您正确地关闭套接字,您不应该收到此消息,除非您正在打开大量的并发连接。听起来像是有些东西阻止了您的套接字被适当地关闭。我建议您验证它们是否被正确地处理。


1
用户名 硬限制 文件打开数限制 20000 - linjunhalida

55

我有类似的问题。 快速解决方案是:

ulimit -n 4096

解释如下 - 每个服务器连接都是一个文件描述符。在 CentOS、Redhat 和 Fedora(可能还有其他系统中),文件用户限制为 1024 - 不知道为什么会这样设置。可以通过输入命令 ulimit -n 简单地查看此限制。

请注意,这与系统最大文件数 (/proc/sys/fs/file-max) 没有多少关系。

在我的情况下,这是 Redis 的问题,所以我执行了以下操作:

ulimit -n 4096
redis-server -c xxxx

在你的情况下,不需要使用redis,而是需要启动你的服务器。


8
解释:内存泄漏的解决方法不是购买更多的内存,而是修复文件泄漏。翻译:解决内存泄漏问题的方法不是买更多内存,而是修复文件泄漏。 - Rafael Baptista
6
似乎你没有理解问题(或者你把评论放在了错误的答案下面?)。这与文件描述符限制有关,与内存或内存泄漏无关。 - Nick
2
文件限制为1024,否则您将遇到与select()根本问题 - fluffy
3
在某些情况下确实需要大量的并发连接,例如高性能聊天服务器。这不一定涉及泄漏 FD(文件描述符)。 - Antwan van Houdt
1
@RafaelBaptista:如果您有一台可以处理超过512个并行连接的服务器,则需要更多的打开文件。现代服务器可以处理数百万个并行连接,因此将限制设置得低至1024真的没有任何意义。这可能对于普通用户的默认限制来说还可以,但对于处理并行客户端连接的服务器软件来说则不行。 - Mikko Rantalainen
显示剩余2条评论

20
使用lsof -u `whoami` | wc -l命令可以查找用户打开了多少个文件。

17

TCP有一个名为“TIME_WAIT”的特性,确保连接被干净地关闭。它要求连接的一端在套接字关闭后要继续保持监听一段时间。

在高性能服务器中,重要的是客户端进入TIME_WAIT状态,而不是服务器。客户端可以承受有一个端口处于打开状态,而繁忙的服务器可能会迅速耗尽端口或打开过多的FD。

为了实现这一点,服务器不应该首先关闭连接,而应始终等待客户端关闭它。


2
TCP TIME_WAIT会在操作系统级别保持套接字打开,并最终导致服务器拒绝传入连接。当您关闭文件句柄时,它就关闭了。https://dev59.com/kXI-5IYBdhLWcg3wiY7a - Rafael Baptista
确实,文件句柄会立即关闭,我说错了。但我的主要观点仍然成立,因为即使FD被释放,TCP端口仍然在TIME_WAIT期间保留分配,并且繁忙的服务器可能会耗尽TCP端口,或者花费太多内核内存来跟踪它们。 - Ed4

16

这意味着同时打开的文件数量上限。

解决方案:

在文件/etc/security/limits.conf 的末尾添加以下行:

* soft nofile 16384
* hard nofile 16384

在当前控制台中,从根目录执行以下操作(sudo无效):

ulimit -n 16384

虽然这是可选的,但如果可以重新启动服务器,则需要在 /etc/nginx/nginx.conf 文件中注册新值 worker_connections 等于 16384 除以值 worker_processes

如果没有执行 ulimit -n 16384,则需要重新启动,然后问题将消失。

附言:

如果修复后日志中仍然出现 error accept() failed (24: Too many open files)

在 nginx 配置中,找到适当位置进行设置(例如):

worker_processes 2;

worker_rlimit_nofile 16384;

events {
  worker_connections 8192;
}

6

我也遇到了这个问题。你有一个文件句柄泄露的情况。你可以通过打印出所有打开的文件句柄列表(在POSIX系统上)来进行调试:

void showFDInfo()
{
   s32 numHandles = getdtablesize();

   for ( s32 i = 0; i < numHandles; i++ )
   {
      s32 fd_flags = fcntl( i, F_GETFD ); 
      if ( fd_flags == -1 ) continue;


      showFDInfo( i );
   }
}

void showFDInfo( s32 fd )
{
   char buf[256];

   s32 fd_flags = fcntl( fd, F_GETFD ); 
   if ( fd_flags == -1 ) return;

   s32 fl_flags = fcntl( fd, F_GETFL ); 
   if ( fl_flags == -1 ) return;

   char path[256];
   sprintf( path, "/proc/self/fd/%d", fd );

   memset( &buf[0], 0, 256 );
   ssize_t s = readlink( path, &buf[0], 256 );
   if ( s == -1 )
   {
        cerr << " (" << path << "): " << "not available";
        return;
   }
   cerr << fd << " (" << buf << "): ";

   if ( fd_flags & FD_CLOEXEC )  cerr << "cloexec ";

   // file status
   if ( fl_flags & O_APPEND   )  cerr << "append ";
   if ( fl_flags & O_NONBLOCK )  cerr << "nonblock ";

   // acc mode
   if ( fl_flags & O_RDONLY   )  cerr << "read-only ";
   if ( fl_flags & O_RDWR     )  cerr << "read-write ";
   if ( fl_flags & O_WRONLY   )  cerr << "write-only ";

   if ( fl_flags & O_DSYNC    )  cerr << "dsync ";
   if ( fl_flags & O_RSYNC    )  cerr << "rsync ";
   if ( fl_flags & O_SYNC     )  cerr << "sync ";

   struct flock fl;
   fl.l_type = F_WRLCK;
   fl.l_whence = 0;
   fl.l_start = 0;
   fl.l_len = 0;
   fcntl( fd, F_GETLK, &fl );
   if ( fl.l_type != F_UNLCK )
   {
      if ( fl.l_type == F_WRLCK )
         cerr << "write-locked";
      else
         cerr << "read-locked";
      cerr << "(pid:" << fl.l_pid << ") ";
   }
}

通过转储所有打开的文件,您很快就能找出文件句柄泄漏的位置。
如果您的服务器生成子进程,例如“fork”样式的服务器,或者您正在生成其他进程(例如通过cgi),则必须确保使用“cloexec”创建文件句柄 - 无论是真实文件还是套接字。
没有cloexec,每次分叉或生成时,所有打开的文件句柄都会在子进程中克隆。
还很容易未能关闭网络套接字 - 例如,当远程方断开连接时仅放弃它们。这将导致文件句柄泄漏。

5

在 MacOS 上,显示限制:

launchctl limit maxfiles

结果如下:maxfiles 256 1000

如果数字(软限制和硬限制)太低,您需要进行上调:

sudo launchctl limit maxfiles 65536 200000

4

已关闭的套接字需要一些时间才能真正释放。

使用 lsof 列出打开的文件。

使用 cat /proc/sys/fs/file-max 查看系统限制是否存在。


3

为了以后参考,我遇到了类似的问题;我创建了太多的文件和套接字(在Unix操作系统上,所有东西都是FD),导致创建了太多的文件描述符(FDs)。我的解决方案是使用setrlimit()在运行时增加FDs。

首先,我使用以下代码获取FD限制:

// This goes somewhere in your code
struct rlimit rlim;

if (getrlimit(RLIMIT_NOFILE, &rlim) == 0) {
    std::cout << "Soft limit: " << rlim.rlim_cur << std::endl;
    std::cout << "Hard limit: " << rlim.rlim_max << std::endl;
} else {
    std::cout << "Unable to get file descriptor limits" << std::endl;
}

在运行getrlimit()后,我确认在我的系统上,软限制是256个FD,硬限制是无限个FD(这取决于您的发行版和规格)。由于我在文件和套接字之间创建了> 300个FD,所以我的代码崩溃了。在我的情况下,我无法减少FD的数量,因此我决定使用以下代码增加FD软限制:
// This goes somewhere in your code
struct rlimit rlim;

rlim.rlim_cur = NEW_SOFT_LIMIT;
rlim.rlim_max = NEW_HARD_LIMIT;

if (setrlimit(RLIMIT_NOFILE, &rlim) == -1) {
    std::cout << "Unable to set file descriptor limits" << std::endl;
}

请注意,您还可以使用此代码获取正在使用的FD数以及这些FD的来源。
此外,您可以在此处此处找到有关gettrlimit()setrlimit()的更多信息。

2

关于CentOS的另一条信息。 在这种情况下,当使用“systemctl”启动进程时, 你需要修改系统文件 ==> /usr/lib/systemd/system/processName.service 文件中应该有以下行:

LimitNOFILE=50000

只需重新加载您的系统配置:

systemctl daemon-reload

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