如何避免子进程因信号终止时父进程随之终止?
问题分析与解决
问题背景
我希望生成一个进程并通过标准输入/输出与其通信,功能运行正常,但发现当子进程因信号终止时,父进程也会随之终止。以下为示例代码:
- 取消注释
/usr/bin/true时,程序会输出"Finish"; - 编译并取消注释
/tmp/selfterm时,程序不会输出该内容,且父进程会以退出码137终止。
请问父进程终止的原因是什么,该如何避免?
示例代码
main.c
#include <signal.h> #include <sched.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <sys/wait.h> int doexec(void*) { //char*bin = "/usr/bin/true"; //char*bin = "/tmp/selfterm"; char*args[] = {bin, 0}; int err = execv(bin, args); return err; } int main(int argc, char *argv[]) { int pid = clone(doexec, malloc(2*1024*1024)+2*1024*1024, CLONE_VFORK|SIGCHLD, 0); int status=0; wait(&status); puts("Finish"); }
selfterm.c
#include<signal.h> int main(int argc, char *argv[]) { kill(0, 9); }
父进程终止的原因
kill(0, 9)的作用机制:kill(0, SIGKILL)会向当前进程组内的所有进程发送SIGKILL信号(信号9)。由于你用clone(CLONE_VFORK)创建的子进程默认和父进程属于同一个进程组,所以子进程执行kill(0,9)时,会把父进程也一并杀死。- 退出码137的含义:Linux下进程被信号终止时,退出码为
128 + 信号编号。137 = 128 + 9,明确说明父进程是被SIGKILL信号强制终止的。
解决方法
方法1:让子进程脱离父进程的进程组(推荐,无需root)
在子进程执行execv之前,调用setpgid(0, 0)创建一个新的进程组,让子进程成为该组的组长。这样kill(0,9)只会发送给子进程自己的进程组,不会影响父进程。
修改后的doexec函数:
int doexec(void*) { //char*bin = "/usr/bin/true"; char*bin = "/tmp/selfterm"; // 创建新进程组,子进程作为组长 setpgid(0, 0); char*args[] = {bin, 0}; int err = execv(bin, args); return err; }
注意:
setpgid必须放在execv之前,因为execv会替换整个进程镜像,之后的代码不会被执行。
方法2:使用PID命名空间隔离(需要root权限)
如果需要更强的进程隔离,可以在clone的参数中添加CLONE_NEWPID,创建一个独立的PID命名空间。子进程在这个命名空间内是PID 1,kill(0,9)只会影响命名空间内的进程,完全不会波及父进程。但这种方式需要root权限,适合容器类场景。
修改后的clone调用:
int pid = clone(doexec, malloc(2*1024*1024)+2*1024*1024, CLONE_VFORK|SIGCHLD|CLONE_NEWPID, 0);
内容的提问来源于stack exchange,提问作者Spill
相关产品推荐
相关产品推荐

