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

时间:2009-05-19 01:15:01

标签: c sockets

我正在开展一个学校项目,我必须编写一个多线程服务器,现在我通过对它运行一些测试来将它与apache进行比较。我正在使用autobench来帮助解决这个问题,但是在我运行一些测试之后,或者如果我给它提供太高的速率(大约600+)来建立连接,我会收到“太多打开文件”的错误。

完成处理请求后,我总是在套接字上执行close()。我也尝试过使用shutdown()函数,但似乎没有任何帮助。有什么方法吗?

13 个答案:

答案 0 :(得分:52)

Linux可以对允许打开的文件描述符数量进行限制。

您可以查看以下内容:

cat /proc/sys/fs/file-max

这将为您提供系统范围的文件描述符限制。

在shell级别,这将告诉您个人限制:

ulimit -n

这可以在/etc/security/limits.conf中更改 - 这是nofile参数。

但是,如果你正确地关闭了套接字,除非你打开很多类似的连接,否则你不应该接收它。听起来有些东西阻止你的插座被正确关闭。我会验证它们是否正确处理。

答案 1 :(得分:24)

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

ulimit -n 4096

解释如下 - 每个服务器连接都是文件描述符。在CentOS,Redhat和Fedora,可能是其他人,文件用户限制是1024 - 不知道为什么。键入时可以很容易地看到: ulimit -n

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

在我的情况下,这是Redis的问题,所以我做了:

ulimit -n 4096
redis-server -c xxxx

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

答案 2 :(得分:14)

TCP具有一个名为“TIME_WAIT”的功能,可确保连接干净地关闭。它需要连接的一端在套接字关闭后保持监听一段时间。

在高性能服务器中,重要的是进入TIME_WAIT的客户端而不是服务器。客户端可以负责打开端口,而繁忙的服务器可以快速耗尽端口或拥有太多开放的FD。

要实现这一点,服务器不应该首先关闭连接 - 它应该总是等待客户端关闭它。

答案 3 :(得分:9)

使用lsof -u `whoami` | wc -l查找用户拥有的打开文件数

答案 4 :(得分:7)

这意味着同时打开文件的最大数量。

<强>解决:

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

* soft nofile 16384
* hard nofile 16384

在root的当前控制台中(sudo不起作用):

ulimit -n 16384

虽然这是可选的,但是如果可以重新启动服务器。

/etc/nginx/nginx.conf文件中注册新值worker_connections,等于16384除以值worker_processes

如果没有ulimit -n 16384,则需要重新启动,然后问题就会消退。

<强> PS:

如果在日志error accept() failed (24: Too many open files)中显示修复后:

在nginx配置中,propevia(例如):

worker_processes 2;

worker_rlimit_nofile 16384;

events {
  worker_connections 8192;
}

答案 5 :(得分: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,每次fork或者生成时,都会在子进程中克隆所有打开的文件句柄。

关闭网络套接字也很容易 - 例如当远程方断开连接时放弃它们。这会像疯了一样泄漏手柄。

答案 6 :(得分:4)

在关闭的套接字真正释放之前可能需要一些时间

lsof列出打开的文件

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

答案 7 :(得分:2)

关于CentOS的另一个信息。 在这种情况下,当使用&#34; systemctl&#34;启动过程。 你必须修改系统文件==&gt; /usr/lib/systemd/system/processName.service 。在文件中有这一行:

Timeline.prototype.onTaskDoubleClick

只需重新加载系统内容:

LimitNOFILE=50000

答案 8 :(得分:1)

当你的程序有比打开文件更多的开放描述符ulimit(ulimit -a会列出这个)时,内核将拒绝再打开任何文件描述符。确保没有任何文件描述符泄漏 - 例如,运行它一段时间,然后停止并查看是否有任何额外的fds在空闲时仍然打开 - 如果它仍然是一个问题,请更改你的nofile ulimit用户在/etc/security/limits.conf

答案 9 :(得分:1)

我遇到了同样的问题,我没有费心去检查close()调用的返回值。当我开始检查返回值时,问题神秘地消失了。

我只能假设编译器的优化故障(在我的情况下为gcc),假设close()调用没有副作用,并且如果不使用它们的返回值则可以省略。

答案 10 :(得分:0)

在MacOS上,显示限制:

launchctl limit maxfiles

结果类似:maxfiles 256 1000

如果数字(软限制和硬限制)太低,则必须设置上限:

sudo launchctl limit maxfiles 65536 200000

答案 11 :(得分:0)

为了将来参考,我遇到了类似的问题。我通过创建太多的文件和套接字创建了太多的文件描述符(FD)(在Unix OS上,所有内容都是FD)。我的解决方案是在运行时使用setrlimit()增加FD。

首先,我获得了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的来源with this code

您还可以在gettrlimit()setrlimit() herehere上找到更多信息。

答案 12 :(得分:0)

vSphere上的Ubuntu 18的类似问题。 原因-配置文件nginx.conf包含过多的日志文件和套接字。套接字在Linux中被视为文件。 当nginx -s重新加载或sudo服务nginx启动/重新启动时,error.log中出现“打开文件过多”错误。

NGINX工作进程由NGINX用户启动。 Nginx用户的Ulimit(软和硬)为65536。 ulimit和设置limits.conf无效。

nginx.conf中的rlimit设置也无济于事: worker_rlimit_nofile 65536;

有效的解决方案是:

$ mkdir -p /etc/systemd/system/nginx.service.d
$ nano /etc/systemd/system/nginx.service.d/nginx.conf
    [Service]
    LimitNOFILE=30000
$ systemctl daemon-reload
$ systemctl restart nginx.service