Perl中无法终止system()启动的无限循环子进程问题
问题分析与解决:无法终止由Perl子进程启动的无限循环程序
嘿,我明白你碰到的麻烦了——用Perl的fork()创建子进程后调用system()启动那个无限循环的C程序,结果10秒后父进程根本杀不掉背后的loop.bin,对吧?
先还原你的原始代码
C程序(loop.c)
#include <stdio.h> int main(){ while(1){ printf("Infinite loop\n"); } return 0; }
编译命令:
gcc loop.c -o loop.bin
Perl脚本(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"; system("./loop.bin"); } else { print "Printed by parent process\n"; sleep(10); my $ret = waitpid($pid, WNOHANG); if ($ret == 0){ kill ('KILL',$pid); sleep(1); } }
为什么杀不掉?核心是进程层级搞反了!
当Perl子进程调用system("./loop.bin")时,system本身会再创建一个全新的进程来执行loop.bin,然后这个Perl子进程会一直等着loop.bin结束(当然它永远不会结束)。此时的进程树是这样的:
Perl父进程 → Perl子进程 → loop.bin进程
你现在用kill('KILL', $pid)杀掉的只是中间的Perl子进程,但loop.bin会因为父进程(Perl子进程)退出,被系统的init/systemd进程接管,继续疯狂输出循环内容,自然就杀不掉了。
两种靠谱的解决办法
方法1:用exec替换system,直接替换子进程
exec不会创建新进程,而是直接用loop.bin替换当前的Perl子进程。这样进程树就简化成:
Perl父进程 → loop.bin进程
修改Perl脚本的子进程部分即可:
elsif ($pid == 0) { print "Printed by child process\n"; # 用exec替换当前进程,不会留下中间的Perl子进程 exec("./loop.bin") or die "Failed to exec loop.bin: $!"; }
这样父进程kill的$pid就是loop.bin的PID,10秒后就能直接干掉它。
方法2:用进程组杀掉整个进程树
如果你一定要用system(比如需要在执行前后做一些额外操作),可以让Perl子进程成为进程组组长,然后父进程杀掉整个进程组:
修改后的完整Perl脚本:
#!/usr/bin/perl use strict; use warnings; use POSIX ":sys_wait_h"; use Time::HiRes qw(sleep); use POSIX qw(setpgrp); # 引入进程组设置函数 if(!defined( my $pid = fork())) { die "Cannot fork a child: $!"; } elsif ($pid == 0) { print "Printed by child process\n"; # 将当前子进程设为进程组组长 setpgrp(0, 0); system("./loop.bin"); } else { print "Printed by parent process\n"; sleep(10); my $ret = waitpid($pid, WNOHANG); if ($ret == 0){ # 杀掉整个进程组,-KILL表示向进程组发送KILL信号 kill('-KILL', $pid); sleep(1); } }
这样不管system创建多少子进程,整个进程组都会被一次性清理掉。
验证效果
修改后运行perl timeout.pl,10秒后程序会停止输出,用ps aux | grep loop.bin也找不到这个进程了。
内容的提问来源于stack exchange,提问作者day dreamer
相关产品推荐
相关产品推荐

