在Ubuntu上部署Nginx、Redis、数据库或者高并发的网络服务时,经常会在日志里看到"Too many open files"这样的报错。这个错误的根源是进程打开的文件描述符数量达到了系统上限。Linux把每个打开的文件、网络套接字、管道都视为一个文件描述符,默认的1024个限制对于现代服务来说往往不够用。本文详细介绍如何在Ubuntu中查看并调整这个限制,覆盖临时修改和永久配置两种场景。

一、文件描述符是什么,如何查看当前限制
文件描述符(File Descriptor,简称fd)是Linux内核为每个打开的文件、套接字、管道等资源分配的一个非负整数。进程每打开一个文件或建立一条TCP连接,就会消耗一个fd。可以用一个简单的比喻理解:fd就像餐厅的桌号,桌号数量固定,客人多了就得加桌子,否则后来的客人只能被拒绝。
Ubuntu中查看限制最常用的是ulimit -n命令,-n参数专门显示当前shell环境下打开文件数量的软限制。软限制是内核实际执行的限制值,与之对应的还有硬限制,用ulimit -Hn查看。硬限制是软限制能调整到的天花板,普通用户只能把软限制调低或调高到硬限制以内,只有root用户才能提高硬限制。
除了ulimit,还可以直接查看内核级限制和某个进程的实际使用情况:
# 查看当前软限制和硬限制 ulimit -Sn ulimit -Hn # 查看系统级的fd总数上限 cat /proc/sys/fs/file-max # 查看指定进程的fd限制(以PID为1234为例) cat /proc/1234/limits | grep "open files" # 查看系统当前已分配的fd数量 cat /proc/sys/fs/file-nr
其中/proc/sys/fs/file-nr输出三列数值,分别表示已分配的fd数、已分配但未使用的fd数、以及系统最大值。如果第一列逼近第三列,说明整个系统的fd资源已经吃紧,这时候只调整单进程限制是不够的,还需要提高系统级上限。
二、使用ulimit临时调整限制
最快速的调整方式是在当前shell会话中执行ulimit命令。比如想把软限制提升到65535,直接执行:
# 普通用户可在硬限制范围内调高软限制 ulimit -n 65535 # root用户可以同时提高硬限制(unlimited表示不限制) ulimit -Hn unlimited ulimit -n 65535 # 修改后验证 ulimit -n
需要注意,这种方式只对当前shell以及由它启动的子进程生效。关掉终端或重启系统后,设置就会失效,回到默认值。对于临时调试或者手动启动一个服务来说够用,但不适合生产环境的长期部署。
另一个常见的坑是把ulimit命令写进脚本却没生效。原因是ulimit是shell内置命令,影响的是执行它的那个shell进程。如果你在脚本里设置ulimit后用su或sudo切换用户再启动服务,新起的进程可能继承不到这个设置。正确的做法是把ulimit语句放在启动服务的同一个shell脚本里,且在启动命令之前执行。
三、修改limits.conf实现永久生效
要让限制在用户登录后持久生效,标准做法是编辑/etc/security/limits.conf文件。这个文件由PAM模块读取,格式为每行四列:域(用户名或组名)、类型(soft或hard)、项目和值。
# 编辑配置文件 sudo vim /etc/security/limits.conf # 在文件末尾追加以下内容 # 给所有用户设置软限制和硬限制 * soft nofile 65535 * hard nofile 65535 # 也可以只针对特定用户,比如www-data www-data soft nofile 65535 www-data hard nofile 65535 # 通配符*不包含root,root需要单独写一行 root soft nofile 65535 root hard nofile 65535
修改保存后,重新登录(SSH需要断开重连,不能只执行source)即可生效,再用ulimit -n验证。这里有个容易忽略的细节:通配符*并不包含root用户,所以root要单独配置一行,否则以root身份登录时仍然是默认值。
limits.conf依赖PAM认证流程,因此它只对通过登录会话启动的进程有效。由systemd管理的服务(Ubuntu 16.04之后绝大多数服务都由systemd接管)并不会读取这个文件,这就引出了下一节的配置方式。
四、为systemd服务单独配置LimitNOFILE
systemd托管的服务有自己独立的资源限制体系,需要在服务的unit文件中设置。以Nginx为例,先执行systemctl cat nginx找到它的service文件路径,然后创建一个override配置,避免直接修改原文件导致升级时被覆盖:
# 创建override配置(目录会自动创建) sudo systemctl edit nginx # 在打开的编辑器中写入以下内容,保存退出 [Service] LimitNOFILE=65535 # 重新加载配置并重启服务 sudo systemctl daemon-reload sudo systemctl restart nginx # 验证:先找到nginx worker进程的PID ps -ef | grep nginx cat /proc/上面的PID/limits | grep "open files"
执行systemctl edit后,实际生成的文件位于/etc/systemd/system/nginx.service.d/override.conf,这个机制的好处是升级软件包时你的自定义配置不会被冲掉。如果想给所有systemd服务设置全局默认值,可以编辑/etc/systemd/system.conf和/etc/systemd/user.conf,把其中的DefaultLimitNOFILE取消注释并设为想要的值,然后重启系统。
验证服务是否真的拿到了新限制,不要只看配置文件,一定要通过/proc/进程PID/limits查看实际运行值。有些服务自身还有内部的fd上限(比如Nginx的worker_rlimit_nofile指令、Redis的maxclients参数),系统限制调大之后别忘了同步检查应用层的配置,两边取较小值才是最终生效的限制。
五、调整系统级上限与注意事项
除了单进程限制,内核层面的fs.file-max决定了整个系统能打开的fd总数。在非常高的并发场景下需要一并调整:
# 临时修改,立即生效 sudo sysctl -w fs.file-max=2097152 # 永久生效:写入sysctl配置 echo "fs.file-max = 2097152" | sudo tee -a /etc/sysctl.conf sudo sysctl -p
设置时建议遵循一个原则:file-max应远大于所有进程的nofile限制之和乘以一个合理系数,因为系统中每个进程的基础fd开销也要占用全局额度。数值并不是越大越好,每个fd都会占用内核内存,盲目调到几十亿没有任何收益,反而浪费资源。
最后梳理一下排查思路:遇到fd耗尽问题时,先用ls /proc/进程PID/fd | wc -l统计进程实际打开了多少fd,判断是资源泄漏还是确实需要更高配额。如果是代码忘记关闭文件或连接,调大限制只是延缓问题爆发,根治还得从代码层面入手。确认确实需要提升后,再按照临时ulimit、永久limits.conf、systemd服务LimitNOFILE这三个层次依次配置,并用/proc下的limits文件逐项验证,确保配置真正落地。
Ubuntu文件描述符ulimit配置Linux系统优化修改时间:2026-09-09 23:36:56