【问题标题】:Socket accept - "Too many open files"套接字接受 - “打开的文件太多”
【发布时间】:2010-10-27 04:54:09
【问题描述】:

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

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

【问题讨论】:

    标签: c sockets


    【解决方案1】:

    Linux 可以在多个地方限制允许打开的文件描述符的数量。

    您可以检查以下内容:

    cat /proc/sys/fs/file-max
    

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

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

    ulimit -n
    

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

    但是,如果您正确关闭了套接字,则除非您打开大量同时连接,否则您不应该收到此消息。听起来好像有什么东西阻止了你的套接字被适当地关闭。我会验证它们是否得到妥善处理。

    【讨论】:

    • 用户名 hard nofile 20000
    【解决方案2】:

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

    ulimit -n 4096
    

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

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

    就我而言,这是 Redis 的问题,所以我这样做了:

    ulimit -n 4096
    redis-server -c xxxx
    

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

    【讨论】:

    • 而内存泄漏的答案是……购买更多内存?没有修复文件泄漏。
    • 看来你没有理解问题(或者你把评论放在错误的答案下?。它与文件描述符限制有关,与内存或内存泄漏无关。
    • 文件限制为 1024,否则你会遇到fundamental problem with select()
    • @RafaelBaptista 在某些情况下实际上需要大量并发连接,例如高性能聊天服务器。这不一定是关于泄漏 FD。
    • @RafaelBaptista:如果你的服务器可以处理超过 512 个并行连接,你需要更多的打开文件。现代服务器可以处理数百万个并行连接,因此限制低至 1024 确实没有任何意义。临时用户的默认限制可能没问题,但处理并行客户端连接的服务器软件则不然。
    【解决方案3】:

    TCP 有一个称为“TIME_WAIT”的功能,可以确保干净地关闭连接。它要求连接的一端在套接字关闭后继续监听一段时间。

    在高性能服务器中,进入 TIME_WAIT 的是客户端,而不是服务器,这一点很重要。客户端可以负担得起打开端口,而繁忙的服务器可能会迅速耗尽端口或打开太多的 FD。

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

    【讨论】:

    • 没有。 TCP TIME_WAIT 将在操作系统级别保持套接字打开,并最终导致服务器拒绝传入的连接。当您关闭文件句柄时,它会关闭。 stackoverflow.com/questions/1803566/…
    • 文件句柄确实会立即关闭,但我说错了。但我的主要观点仍然存在,因为即使 FD 被释放,TCP 端口仍然在 TIME_WAIT 期间分配,并且繁忙的服务器可能会耗尽 TCP 端口,或者花费过多的内核内存来跟踪它们。
    【解决方案4】:

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

    【讨论】:

      【解决方案5】:

      这表示同时打开文件的最大数量。

      已解决:

      在文件/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;
      }
      

      【讨论】:

        【解决方案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 或 spawn 时,所有打开的文件句柄都会克隆到子进程中。

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

        【讨论】:

          【解决方案7】:

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

          lsof 列出打开的文件

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

          【讨论】:

            【解决方案8】:

            只是关于 CentOS 的另一个信息。 在这种情况下,当使用“systemctl”启动进程时。 您必须修改系统文件==> /usr/lib/systemd/system/processName.service .文件中有这一行:

            LimitNOFILE=50000
            

            然后重新加载你的系统配置文件:

            systemctl daemon-reload
            

            【讨论】:

              【解决方案9】:

              在 MacOS 上,显示限制:

              launchctl limit maxfiles
              

              结果如:maxfiles 256 1000

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

              sudo launchctl limit maxfiles 65536 200000
              

              【讨论】:

                【解决方案10】:

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

                【讨论】:

                  【解决方案11】:

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

                  我只能假设编译器的优化故障(在我的例子中是 gcc),假设 close() 调用没有副作用,如果不使用它们的返回值可以省略。

                  【讨论】:

                  • 很抱歉,这根本不合理。如果您的代码中的微小更改使错误“消失”,那么您的代码中很可能存在更改隐藏的严重错误。使用valgrind 或其他类似工具来追踪它。优化掉 close 调用的编译器将是灾难性的。
                  • 我同意。但是,检查任何系统调用的返回值很重要,因为您可以从许多情况下得到EGAIN,如果您忽略它,所有的赌注都将失败。
                  【解决方案12】:

                  为了将来参考,我遇到了类似的问题;我通过创建太多文件和套接字(在 Unix 操作系统上,一切都是 FD)创建了太多的文件描述符 (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 的更多信息。

                  【讨论】:

                    【解决方案13】:

                    vsphere 上的 Ubuntu 18 上的类似问题。 原因 - 配置文件 nginx.conf 包含太多的日志文件和套接字。套接字在 Linux 中被视为文件。 nginx -s reload或sudo service nginx start/restart时,error.log中出现Too many open files错误。

                    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
                    

                    【讨论】:

                      猜你喜欢
                      • 1970-01-01
                      • 1970-01-01
                      • 2010-12-03
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 2014-01-12
                      相关资源
                      最近更新 更多