如何用Perl杀死多fork生成的进程?现有脚本单fork有效多fork失效
解决多Fork场景下Perl脚本无法终止所有子进程的问题
你的问题核心在于原Perl脚本只杀死了直接启动loop.bin的子进程,但loop.bin里多次fork出的孙进程在父进程被杀死后,会被系统的init进程(或systemd)接管,继续运行无限循环。要解决这个问题,我们需要找到所有属于这个进程组的进程并一次性终止。
问题复现的测试代码
先看你的测试用例loop.c:
#include<stdio.h> #include <sys/types.h> #include<unistd.h> void process_1() { fork(); fork(); fork(); if(fork() == 0) { printf( "G_Child1\n"); }else{ while(1){ printf("parent\n"); } } } int main() { process_1(); return 0; }
编译命令:
gcc loop.c -o loop.bin
原脚本的问题分析
你的timeout.pl仅针对直接子进程操作,当直接子进程被杀死后,它的子进程(孙进程)会成为孤儿进程被系统收养,继续运行:
#!/usr/bin/perl use strict; use warnings; use POSIX ":sys_wait_h"; use Time::HiRes qw(sleep); if(!defined( my $pid = fork())) { die "Cannot fork a child: $!"; } elsif ($pid == 0) { print "Printed by child process\n"; exec("./loop.bin"); } else { print "Printed by parent process\n"; sleep(1); my $ret = waitpid(-1, WNOHANG); if ($ret == 0){ kill ('KILL',$pid); sleep(1); } }
运行后会看到孙进程持续打印parent:
$ perl timeout.pl Printed by child process G_Child1 parent parent parent ...
修复后的Perl脚本
我们可以通过杀死整个进程组的方式,一次性终止所有相关进程。让子进程成为新进程组的组长,之后向整个进程组发送KILL信号:
#!/usr/bin/perl use strict; use warnings; use POSIX ":sys_wait_h"; use Time::HiRes qw(sleep); if(!defined( my $pid = fork())) { die "Cannot fork a child: $!"; } elsif ($pid == 0) { # 让子进程成为新进程组的组长,后续所有fork出的进程都会归属这个组 POSIX::setpgid(0, 0) or die "Failed to set process group: $!"; print "Printed by child process\n"; exec("./loop.bin"); } else { print "Printed by parent process\n"; sleep(1); # 向整个进程组发送KILL信号,负号表示操作对象是进程组而非单个进程 kill 'KILL', -$pid; # 等待所有进程退出,避免僵尸进程 waitpid($pid, 0); }
关键细节解释
POSIX::setpgid(0, 0):在子进程中调用此函数,将当前进程设为新进程组的组长。loop.bin后续fork出的所有子进程都会自动加入这个进程组。kill 'KILL', -$pid:通过负号指定操作进程组,一次性向组内所有进程发送终止信号,直接解决了孙进程残留的问题。waitpid($pid, 0):阻塞等待子进程完全退出,确保不会留下僵尸进程占用系统资源。
现在运行修改后的脚本,所有相关进程都会被彻底终止,不会再出现无限打印parent的情况。
内容的提问来源于stack exchange,提问作者day dreamer




