Linux中信号处理程序返回后系统调用重启机制及差异问题
信号处理与系统调用重启问题分析
程序1:按下CTRL+C时系统调用始终重启阻塞
当按下CTRL+C触发SIGINT信号后,信号处理完成后会自动重启被中断的getchar()调用,进程继续阻塞等待输入。
#define _GNU_SOURCE #include <signal.h> #include <pthread.h> #include <stdio.h> #include <sys/select.h> #include <unistd.h> #include <stdlib.h> #include <ucontext.h> void sighandler(int signo, siginfo_t *info, void *ctx) { } int main() { stack_t s; s.ss_flags = 0; s.ss_size = 40000; s.ss_sp = malloc(40000); sigaltstack(&s, NULL); struct sigaction action; action.sa_flags = SA_SIGINFO|SA_ONSTACK|SA_RESTART; sigfillset(&action.sa_mask); action.sa_sigaction = sighandler; sigaction(SIGINT, &action, NULL); getchar(); }
程序2:按下CTRL+C后打印哈哈并退出
触发SIGINT信号后,程序不会重启getchar()调用,而是执行自定义函数打印内容后直接退出。
#define _GNU_SOURCE #include <signal.h> #include <pthread.h> #include <stdio.h> #include <sys/select.h> #include <unistd.h> #include <stdlib.h> #include <ucontext.h> void restore_here() { printf("哈哈\n"); } void sighandler(int signo, siginfo_t *info, void *ctx) { ucontext_t *sigctx = ctx; makecontext(ctx, restore_here, 0); } int main() { stack_t s; s.ss_flags = 0; s.ss_size = 40000; s.ss_sp = malloc(40000); sigaltstack(&s, NULL); struct sigaction action; action.sa_flags = SA_SIGINFO|SA_ONSTACK|SA_RESTART; sigfillset(&action.sa_mask); action.sa_sigaction = sighandler; sigaction(SIGINT, &action, NULL); getchar(); }
问题
已知信号处理程序返回后,C库会调用sigreturn系统调用,原子性设置信号掩码并跳转到ctx指定的位置。现需解答:
- 内核何时重启系统调用?
- 为何程序2在
sigreturn调用后未重启系统调用?
解答
内核重启系统调用的时机
当同时满足以下两个条件时,内核会在sigreturn恢复上下文之前重启被中断的系统调用:
- 被信号中断的是可重启类型的系统调用(如
getchar、select这类阻塞式I/O调用、进程控制类调用等); - 注册信号处理函数时,
sigaction的sa_flags参数设置了SA_RESTART标记。
此时内核会自动重新执行被中断的系统调用,进程逻辑会回到被信号打断前的状态,继续执行原系统调用。
程序2未重启系统调用的原因
程序2的信号处理函数中调用makecontext(ctx, restore_here, 0)直接修改了sigreturn要恢复的上下文ctx:
- 原本
ctx中保存着被中断的getchar()系统调用的执行状态、返回地址等关键信息,内核正是依靠这些信息判断需要重启的系统调用; - 修改后的上下文指向了自定义函数
restore_here,sigreturn执行后会直接跳转到restore_here执行,而非回到getchar()被中断的位置; - 内核找不到原系统调用的执行上下文,因此不会触发系统调用重启逻辑。
restore_here执行完毕后,进程没有回到原主程序的执行流,直接退出。
内容的提问来源于stack exchange,提问作者Markity
相关产品推荐
相关产品推荐

