调高 fs.file-max 可避免高并发 Socket 因 fd 耗尽导致 accept()/socket() 失败报 EMFILE。它设定了系统级 fd 总上限,需与 ulimit -n 和 nr_open 协同调整,推荐设为 2097152 并通过 sysctl.d 配置永久生效。

为什么改 fs.file-max 对高并发 Socket 有用?
Linux 内核用 fs.file-max 控制整个系统能打开的文件描述符(file descriptor, fd)总数,而每个 Socket 连接至少占用 1 个 fd。当你的 C++ 服务每秒新建数千连接(比如做代理、网关或长连接推送),fs.file-max 设得太低会导致 accept() 或 socket() 直接失败,报错 EMFILE(Too many open files)。这不是程序 bug,是内核拦下来的——所以必须提前调高。
注意:fs.file-max 是全局硬上限,不等于单进程能用的 fd 数;后者还受 ulimit -n 和 /proc/sys/fs/nr_open 约束,三者要协同调整。
怎么安全地修改 fs.file-max?
临时生效(重启后丢失)直接写 /proc:
echo 2097152 > /proc/sys/fs/file-max
永久生效需写入 /etc/sysctl.conf 或独立配置文件(推荐后者,避免污染主配置):
- 创建
/etc/sysctl.d/99-socket-fd.conf - 写入一行:
fs.file-max = 2097152 - 执行
sysctl --system重载(比sysctl -p更可靠,会加载所有.d/下的配置)
验证是否生效:cat /proc/sys/fs/file-max。别只信 sysctl fs.file-max,它可能缓存旧值。
C++ 程序启动前必须同步调高 ulimit -n
fs.file-max 调再高,单个进程仍被 ulimit -n 卡住。C++ 进程默认通常只有 1024,远不够用。
常见错误做法:在代码里用 setrlimit(RLIMIT_NOFILE, ...) 动态提升——这只能设到当前 ulimit 的软限制值,而软限不能超过硬限,硬限又默认很低。
正确做法:
- 启动服务前,在 systemd service 文件中加:
LimitNOFILE=1048576 - 或在 shell 启动脚本里先执行:
ulimit -n 1048576,再运行二进制 - 检查是否生效:
cat /proc/$(pidof your_app)/limits | grep "Max open files"
如果用 fork() 派生子进程,子进程继承父进程的 ulimit,不用重复设。
为什么 fs.file-max 不宜盲目设到几千万?
每个 fd 在内核里占约 1KB 内存(具体看内核版本),设成 1000 万就吃掉 10GB 内核内存,可能触发 OOM killer 杀掉关键进程。实际值应按真实负载估算:
- 估算峰值连接数 × 1.5(留余量)
- 加上日志文件、配置文件、共享库等其他 fd 开销
- 观察
cat /proc/sys/fs/file-nr:三个数字分别是已分配、未使用、最大值;中间值长期接近最大值才说明真不够
线上建议从 2097152(200 万)起步,压测时监控 /proc/sys/fs/file-nr 和 dmesg | grep -i "file limit",有警告再逐步上调。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











