进程接收信号的底层机制是什么?如何在运行代码同时监听指定信号?
信号监听的底层实现机制
核心结论先明确:进程本身不会主动“等待”信号,信号的投递和处理完全是操作系统内核触发的异步流程,不需要进程自己做轮询监听。
1. 信号注册阶段
你调用signal()或者sigaction()的时候,本质是在进程的内核态控制块(PCB,进程控制块)里更新一个信号处理函数映射表,内核会记录当该进程收到特定信号(比如SIGINT)时,应该执行用户自定义的处理函数,而不是走默认逻辑(比如SIGINT默认是终止进程)。
这个阶段只是修改内核里的配置,不会阻塞你的进程执行,你的业务代码会继续正常运行。
2. 信号投递的触发时机
信号的产生源头大部分在内核,比如用户按下Ctrl+C时,终端驱动会给前台进程组发送SIGINT,内核会把这个信号标记到对应进程的PCB的pending(待处理)信号队列里。
内核只会在两个固定时间点检查进程的待处理信号:
- 进程从系统调用返回用户态之前
- 进程从内核中断处理返回用户态之前
也就是说,你的进程在用户态正常执行业务代码时,不会被信号打断,只有当它因为系统调用(比如read、write、sleep)、或者时间片用完触发调度中断进入内核态,再要切回用户态的时候,内核才会检查有没有待处理的信号需要处理。
3. 信号处理的执行流程
如果检查到有待处理的信号,而且进程没有屏蔽该信号,内核会直接修改用户态进程的栈结构,把信号处理函数的入口地址压到栈顶,这样当进程切回用户态的时候,就会直接先执行你注册的信号处理函数,执行完之后再回到原来被中断的代码位置继续执行。
这个过程对用户态进程是完全透明的,你感知不到自己的代码被临时打断去执行了信号处理逻辑,所以看起来就像是进程一边跑自己的代码,一边在监听信号。
常见注意点
- 正是因为信号处理是异步插入执行的,所以信号处理函数里不能调用不可重入的函数(比如
printf、malloc),否则可能会出现竞态问题。 - 如果进程处于阻塞状态(比如调用了
sleep(10)),收到信号会提前中断阻塞的系统调用,先执行信号处理函数,再返回系统调用的结果(一般是返回-1,errno设为EINTR)。 - 如果进程一直在用户态做CPU密集型计算,完全不进入内核态,内核会等该进程的时间片用完触发调度中断进入内核的时候,再检查待处理信号,不会出现信号一直得不到处理的情况。
内容的提问来源于stack exchange,提问作者heturing
相关产品推荐
相关产品推荐

