pselect调用期间触发信号时,信号处理函数如何被调用?
pselect执行期间SIGTERM信号处理函数的调用流程
pselect是具备原子性信号掩码管理的系统调用,它会一次性完成「替换进程信号掩码→等待I/O/超时→恢复原信号掩码」的操作。当SIGTERM在pselect阻塞阶段到达时,调用流程如下:
内核触发信号响应
进程处于pselect的内核态阻塞等待时,内核收到SIGTERM信号后,会立刻暂停pselect的执行,将进程从内核态切换回用户态。执行信号处理函数
内核会调用你通过sigaction(或同类接口)注册的SIGTERM自定义处理函数:- 处理函数在用户态执行,内核会为其准备独立的栈环境。
- 默认情况下,当前正在处理的SIGTERM信号会被自动加入进程信号掩码(同一时间不会重复触发该信号的处理),你可以通过
sigaction的sa_mask字段自定义需要阻塞的信号集合。 - 若此时有其他未被阻塞的信号到达,会在当前处理函数执行完毕后再触发对应逻辑;普通非实时信号只会保留一个待处理实例,实时信号则会排队处理。
pselect的返回状态
信号处理函数执行完成后,进程回到pselect的中断点,此时pselect会返回-1,并将全局变量errno设置为EINTR,明确标识系统调用被信号中断。
pselect与select的核心差异
普通select没有原子性的信号掩码处理逻辑,如果手动修改信号掩码后再调用select,可能在「修改掩码」和「进入select阻塞」的间隙收到信号,引发竞态条件。而pselect的信号掩码切换、等待、恢复操作是原子完成的,确保信号只会在等待阶段触发中断。
示例代码
#include <stdio.h> #include <signal.h> #include <unistd.h> #include <errno.h> void sigterm_handler(int sig) { write(STDOUT_FILENO, "Caught SIGTERM\n", 15); } int main() { sigset_t sigmask; struct sigaction sa; // 注册SIGTERM处理函数 sa.sa_handler = sigterm_handler; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sigaction(SIGTERM, &sa, NULL); // 定义pselect期间的信号掩码:不阻塞任何信号,允许SIGTERM触发中断 sigemptyset(&sigmask); while (1) { int ret = pselect(0, NULL, NULL, NULL, NULL, &sigmask); if (ret == -1) { if (errno == EINTR) { printf("pselect interrupted by signal\n"); } else { perror("pselect failed"); return 1; } } } return 0; }
运行程序后执行kill -TERM <进程ID>,会先打印Caught SIGTERM,随后输出pselect interrupted by signal,程序继续进入下一轮pselect等待。
内容的提问来源于stack exchange,提问作者Harmonia
相关产品推荐
相关产品推荐

