Linux怎么配置系统的内核信号处理限制
Linux内核信号处理限制通过用户空间资源限制(ulimit)和systemd单元配置实现,核心控制RLIMIT_SIGPENDING(信号挂起限制)及信号栈大小限制。信号队列限制按用户汇总,同一用户所有进程共用上限,sigqueue()调用失败时返回EAGAIN(资源暂时不可用)而非ENOMEM(内存不足)。
Linux 内核的信号处理机制看似底层,实际工作中经常卡在几个配置点上。很多人以为调信号限制得改内核参数或挂载 sysctl,其实真正需要动手的地方在用户空间的资源限制层——ulimit 和 systemd 的单元配置,以及你代码里怎么用备用栈。

内核信号处理限制不能通过内核模块或sysctl直接配置,必须从用户空间资源限制(ulimit)和内核编译/启动参数两层入手,核心是控制RLIMIT_SIGPENDING和信号栈大小。
这么说可能有点抽象,我们先把关键概念拆开看。
查看当前进程的信号队列限制
每个进程到底能排多少信号在队列里等着处理?这个数字由 RLIMIT_SIGPENDING 控制,对应 ulimit -i 的输出。
ulimit -i显示当前 shell 会话下进程可以挂起的信号数量上限——也就是RLIMIT_SIGPENDING的软限制。ulimit -Hi看硬限制,硬限制只能 root 改。- 注意:这个限制是按用户ID汇总统计的,不是单进程独立计算。同一个用户的所有进程共用这个上限,别以为只是给一个进程设的。
- 如果应用频繁调用
sigqueue()发送带数据的实时信号,而ulimit -i设得太低,你就会看到errno = EAGAIN(信号队列满了)。很多人误以为是内存不足,其实排查方向完全错了。
临时修改信号队列上限
调试时或临时扩容,可以在启动服务前用 ulimit 调一把。不过要注意作用域——只影响当前 shell 及其子进程。
- 提升软限制(不能超过硬限制):
ulimit -i 2048 - 提升硬限制(需要 root 权限):
sudo ulimit -Hi 4096 - 另外别指望 systemd 管理的服务能自动继承你 shell 的设置,它们默认走系统值。
systemd 服务的持久化配置
对于长期跑的守护进程,必须显式地在单元文件里声明限制,否则系统默认值(通常是 122880)会一直生效。
- 编辑服务单元文件,例如
/etc/systemd/system/myapp.service - 在
[Service]段添加一行:LimitSIGPENDING=8192 - 然后重载并重启:
sudo systemctl daemon-reload && sudo systemctl restart myapp - 验证是否生效有两个方法:
systemctl show myapp.service | grep SIGPENDING,或者直接看进程的/proc/PID/status里的SigQ字段。
信号处理栈大小(SA_ONSTACK场景)
当主线程栈快用尽时,你可能会用备用栈来处理信号(比如捕获 SIGSEGV 做最后手脚)。这时栈空间够不够用就很关键。
- 查看当前信号栈软限制:
ulimit -s,单位是 KB。它对应RLIMIT_STACK,也影响信号备用栈的大小。 - 代码里调用
sigaltstack()设置信号专用栈时,最大可用空间受ulimit -s约束。如果你设得比这个值还大,调用会失败。 - 如果日志里出现
signal handler returned, but stack overflow detected,说明备用栈溢出了。解决办法是增大ulimit -s,同时检查sigaltstack.ss_size是否合理。
最后说一个容易掉坑的点:信号队列限制(RLIMIT_SIGPENDING)是按用户汇总统计的,不是单进程隔离。这意味着一个进程狂发信号,可能会拖累整个用户的所有进程。而且 sigqueue() 失败时返回的是 EAGAIN 而不是 ENOMEM,排查时第一反应很容易往内存方向跑偏,白费不少时间。


































