C语言无需全局变量通过Ctrl+C/Ctrl+\等信号杀死子进程的实现方法
方案1:拆分信号处理函数+函数内静态变量存储上下文
这个方案不需要修改原有逻辑结构,完全规避全局变量:
- 将信号注册逻辑移到
fork之后,父子进程分别注册属于自己的信号处理函数,不需要再用变量判断当前是父/子进程 - 父进程的信号处理函数内部用静态
volatile类型存储子PID,仅在父进程处理函数范围内可见,不属于全局变量
修改后的完整代码如下:
#include <signal.h> #include <stdbool.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <assert.h> // 父进程专用信号处理函数 void parent_signal_handler(int sig) { // 函数内部静态变量,仅本函数可访问,不属于全局变量 static volatile pid_t child_pid = 0; // 用信号0作为特殊指令传递子PID,不会触发实际信号逻辑 if (sig == 0) { child_pid = (pid_t)sig; return; } signal(sig, parent_signal_handler); if (sig == SIGINT) { const char str[] = "this is ctrl + c in the parent process, killing child\n"; write(STDOUT_FILENO, str, sizeof(str) - 1); // 直接用存储的子PID杀死子进程 kill(child_pid, SIGTERM); exit(EXIT_SUCCESS); } else if (sig == SIGQUIT) { const char str[] = "this is ctrl + \\ in the parent process\n"; write(STDOUT_FILENO, str, sizeof(str) - 1); } } // 子进程专用信号处理函数 void child_signal_handler(int sig) { signal(sig, child_signal_handler); if (sig == SIGINT) { const char str[] = "this is ctrl + c in the child process\n"; write(STDOUT_FILENO, str, sizeof(str) - 1); exit(EXIT_SUCCESS); } else if (sig == SIGQUIT) { const char str[] = "this is ctrl + \\ in the child process\n"; write(STDOUT_FILENO, str, sizeof(str) - 1); } } int main(int argc, char **argv) { pid_t pid; (void)argv; pid = fork(); assert(pid != -1); if (pid == 0) { // 子进程注册自己的处理函数 signal(SIGINT, child_signal_handler); signal(SIGQUIT, child_signal_handler); printf("child pid, pgid = %ju, %ju\n", (uintmax_t)getpid(), (uintmax_t)getpgid(0)); while (1); exit(EXIT_SUCCESS); } // 父进程注册自己的处理函数,并存入子PID signal(SIGINT, parent_signal_handler); signal(SIGQUIT, parent_signal_handler); parent_signal_handler((int)pid); printf("parent pid, pgid = %ju, %ju\n", (uintmax_t)getpid(), (uintmax_t)getpgid(0)); while (1); return 0; }
方案2:使用signalfd事件化处理信号(更推荐)
如果运行环境支持POSIX标准的signalfd,可以完全弃用信号处理函数,把信号转换成文件描述符的可读事件,在主循环中处理,完全不需要任何全局/静态变量,所有上下文都可以用主函数的局部变量传递,也不存在异步信号安全的限制:
- 先阻塞要处理的信号,避免触发默认处理逻辑
- 创建signalfd关联要监听的信号
- 主循环用
read读取signalfd的事件,拿到信号类型后直接调用kill处理子进程即可
内容的提问来源于stack exchange,提问作者DarkSide77
相关产品推荐
相关产品推荐

