C语言sleep系统调用是否触发上下文切换及信号处理结果疑问
代码先修正语法错误(方便分析,不影响逻辑)
#include <stdio.h> #include <signal.h> #include <unistd.h> void sUsr() { printf("A1\n"); sleep(1); printf("A2\n"); } int main(int argc, const char* argv[]) { struct sigaction sa; sigfillset(&sa.sa_mask); sa.sa_handler = &sUsr; sigaction(SIGINT, &sa, NULL); sigaction(SIGTSTP, &sa, NULL); kill(getpid(), SIGINT); kill(getpid(), SIGTSTP); return 0; }
你推导的核心错误点
- 错误1:认为信号处理函数执行期间,主进程的代码会继续执行。信号处理是抢占式中断主执行流的,当内核决定调用信号处理函数时,主进程的执行会被完全暂停,直到信号处理函数返回,才会继续执行main中后续的代码。你提到的「sleep系统调用把控制权交回主进程执行第二个kill」完全不成立,sleep是在信号处理函数的上下文里运行的,此时主进程的执行流卡在
kill(getpid(), SIGINT)调用后的位置,根本不会往后走。 - 错误2:认为信号处理函数运行在用户空间就不会处理挂起的信号。信号处理函数返回前会再次进入内核,内核会自动检查当前有没有未被阻塞的挂起信号,如果有会继续处理,不会直接回到主进程更不会直接退出。
- 错误3:对两次kill的执行时序理解错误。两个kill是main中顺序执行的系统调用,第一个kill返回时触发SIGINT信号处理,此时第二个kill还没有被执行,不存在「SIGSTP信号在第一个信号处理期间被发送、被掩码阻塞」的情况。
正确执行流程
- main中完成sigaction配置:
sigfillset(&sa.sa_mask)意味着当信号处理函数运行时,所有信号都会被加入进程的信号掩码,不会触发嵌套的信号处理。 - 执行第一个
kill(getpid(), SIGINT)系统调用,进入内核态,内核给当前进程标记SIGINT为挂起状态,kill系统调用结束准备返回用户态。 - 内核在切回用户态前检查挂起信号,发现有未阻塞的SIGINT,于是暂停主进程执行流,先切换到信号处理上下文,同时将sa_mask中的所有信号加入进程信号掩码(此时所有信号都被阻塞),开始执行
sUsr。 - 执行
sUsr打印A1,调用sleep(1)进入内核态挂起1秒,期间即使有信号到来也会被阻塞,不会处理。 - 1秒后sleep返回,继续执行
sUsr打印A2,信号处理函数执行完毕,内核将信号掩码恢复到进入信号处理前的状态,此时准备切回主进程的执行流。 - 回到main函数继续执行下一行代码:
kill(getpid(), SIGTSTP),进入内核态给进程标记SIGTSTP为挂起状态,kill返回准备切回用户态。 - 内核再次检查挂起信号,发现有未阻塞的SIGTSTP,重复步骤3-5的逻辑,再次打印
A1和A2。 - 信号处理返回后,main没有后续代码,进程正常退出。
为什么不可能只输出一次A1/A2
整个流程中两个信号是顺序触发、顺序处理的,没有任何提前退出的逻辑,两个信号处理一定会完整执行,因此唯一可能的输出就是两次A1和A2的组合:
A1 A2 A1 A2
内容的提问来源于stack exchange,提问作者Asurii
相关产品推荐
相关产品推荐

