如何实现由其他进程而非systemd接管孤儿进程(含Bash命令行与C程序实现方法)
如何实现由其他进程而非systemd接管孤儿进程(含Bash命令行与C程序实现方法)
嘿,刚好对这个点有研究,给你拆解一下具体实现方法!首先得提一句,Linux内核3.4版本之后支持PR_SET_CHILD_SUBREAPER这个特性,只要进程设置了这个属性,它就会成为「子收割者」,它的后代里出现的孤儿进程会被它接管,而不是默认的systemd(PID 1)。下面分Bash命令行和C程序两种场景给你一步步来:
Bash命令行实现方法
- 先启动一个后台进程作为「子收割者」,设置好subreaper属性后挂起:
# 启动subreaper进程,后台运行并挂起 bash -c 'prctl --set-subreaper 1; exec sleep infinity' & # 记录这个收割者的PID,方便后续操作 REAPER_PID=$!
- 进入这个收割者的PID命名空间,启动一个新的shell:
nsenter --target $REAPER_PID --pid -- bash
- 在这个新shell里启动一个后台测试进程,然后退出当前shell:
# 启动一个后台sleep进程,模拟会变成孤儿的程序 sleep 60 & # 退出当前shell,让sleep的父进程消失 exit
- 验证效果:用
ps -o ppid= -p <sleep的PID>命令查看,会发现sleep的父PID是之前记录的REAPER_PID,而不是1(systemd的PID)。
C程序实现方法
直接通过prctl()系统调用给进程设置PR_SET_CHILD_SUBREAPER属性,然后创建多层子进程模拟孤儿场景,具体代码如下:
#define _GNU_SOURCE #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/prctl.h> #include <sys/wait.h> int main() { // 给当前进程设置子收割者属性 if (prctl(PR_SET_CHILD_SUBREAPER, 1, 0, 0, 0) == -1) { perror("prctl设置subreaper失败"); exit(EXIT_FAILURE); } printf("子收割者进程PID:%d\n", getpid()); // 第一层子进程 pid_t child_pid = fork(); if (child_pid == -1) { perror("创建子进程失败"); exit(EXIT_FAILURE); } if (child_pid == 0) { // 子进程中创建孙子进程(后续会变成孤儿) pid_t grandchild_pid = fork(); if (grandchild_pid == -1) { perror("创建孙子进程失败"); exit(EXIT_FAILURE); } if (grandchild_pid == 0) { // 孙子进程:等待父进程退出,然后查看自己的父PID printf("孙子进程PID:%d,等待父进程退出...\n", getpid()); sleep(2); // 留时间让父进程退出 printf("父进程退出后,孙子进程的父PID变为:%d\n", getppid()); sleep(10); // 保持运行,方便用ps命令验证 exit(EXIT_SUCCESS); } else { // 子进程直接退出,让孙子进程变成孤儿 printf("子进程PID:%d,现在退出...\n", getpid()); exit(EXIT_SUCCESS); } } else { // 子收割者进程等待第一层子进程退出 waitpid(child_pid, NULL, 0); printf("子收割者:子进程已退出,开始接管孤儿进程...\n"); sleep(15); // 保持运行,方便验证 exit(EXIT_SUCCESS); } }
编译运行这个程序后,你会看到孙子进程的父PID变成了子收割者的PID,而不是systemd的PID 1,完美实现了我们要的效果。
备注:内容来源于stack exchange,提问作者tahzibi.jafar
相关产品推荐
相关产品推荐

