You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何用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);
}

关键细节解释

  1. POSIX::setpgid(0, 0):在子进程中调用此函数,将当前进程设为新进程组的组长。loop.bin后续fork出的所有子进程都会自动加入这个进程组。
  2. kill 'KILL', -$pid:通过负号指定操作进程组,一次性向组内所有进程发送终止信号,直接解决了孙进程残留的问题。
  3. waitpid($pid, 0):阻塞等待子进程完全退出,确保不会留下僵尸进程占用系统资源。

现在运行修改后的脚本,所有相关进程都会被彻底终止,不会再出现无限打印parent的情况。

内容的提问来源于stack exchange,提问作者day dreamer

火山引擎 最新活动