perl fork() exec() ,子进程变得狂野

Posted

技术标签:

【中文标题】perl fork() exec() ,子进程变得狂野【英文标题】:perl fork() exec() , child process gone wild 【发布时间】:2012-09-26 18:24:39 【问题描述】:

我正在使用Linux,而.shtcsh 中。

我已经制作了一个非常基本的fork 和exec,但我需要帮助来实施安全措施。

基本上,我的 perl 脚本在子进程中调用 .sh 脚本。但是当我 Ctrl+c 杀死父母时,信号被孩子忽略了。

1) 我如何捕获子进程的SIGINT

2) 运行 .sh 脚本的子进程仍然 STDOUT 到 xterm 的屏幕。我怎样才能删除这个?我正在考虑在后台运行脚本

 exec("shell.sh args &");  

但还没有测试,因为我需要先弄清楚如何让孩子不发狂。

3) 父进程(perl 脚本)不会等待子进程(.sh 脚本)。所以我读过很多关于孩子变成僵尸的文章???脚本完成后会发生吗?我该如何阻止它?

$pid = fork();
if($pid < 0)
    print "Failed to fork process... Exiting";
    exit(-1);
    
elsif ($pid ==0) 
    #child process
    exec("shell.sh args");
    exit(1);
    
else  #execute rest of parent

【问题讨论】:

【参考方案1】:

但是当我按 ctrl+c 杀死父级时,信号会被子级忽略。

信号被发送给父母和孩子两个。

$ perl -E'
   if (my $pid = fork()) 
      local $SIGINT = sub  say "Parent got SIGINT" ;
      sleep;
      waitpid($pid, 0);
    else 
      local $SIGINT = sub  say "Child got SIGINT" ;
      sleep;
   
'
^CParent got SIGINT
Child got SIGINT

如果那个孩子忽略了它,那是因为它开始了一个新的会话,或者因为它明确地忽略了它。

运行 .sh 脚本的子进程仍然 STDOUT 到 xterm 的屏幕。我怎样才能删除它?

在调用exec之前在孩子中执行以下操作:

open(STDOUT, '>', '/dev/null');
open(STDERR, '>', '/dev/null');

实际上,我会使用open3 来进行一些错误检查。

open(local *CHILD_STDIN,  '<', '/dev/null') or die $!;
open(local *CHILD_STDOUT, '>', '/dev/null') or die $!;
my $pid = open3(
   '<&CHILD_STDIN',
   '>&CHILD_STDOUT',
   '>&CHILD_STDOUT',
   'shell.sh', 'args',
);

父进程(perl 脚本)不会等待子进程(.sh 脚本)。所以我读过很多关于孩子变成僵尸的故事???

当父级退出或在父级退出后退出时,子级会自动收割。

$ perl -e'
   for (1..3) 
      exec(perl => (-e => 1)) if !fork;
   
   sleep 1;
   system("ps");
' ; ps
  PID TTY          TIME CMD
26683 pts/13   00:00:00 bash
26775 pts/13   00:00:00 perl
26776 pts/13   00:00:00 perl <defunct>       <-- zombie
26777 pts/13   00:00:00 perl <defunct>       <-- zombie
26778 pts/13   00:00:00 perl <defunct>       <-- zombie
26779 pts/13   00:00:00 ps
  PID TTY          TIME CMD
26683 pts/13   00:00:00 bash
26780 pts/13   00:00:00 ps
                                             <-- all gone

如果父母先于孩子退出,则没有问题。

如果父母在孩子离开后不久就退出了,那没有问题。

如果父母离开孩子很长时间后离开,你会想要收获他们。您可以使用waitwaitpid(可能来自SIGCHLD 处理程序)来执行此操作,或者您可以使用$SIGCHLD = 'IGNORE'; 使它们自动收获。见perlipc。

【讨论】:

改进了最后一个问题的答案。 回到第一个问题。如果他们仍然收到签名,我如何使用 ctrl+C 杀死子进程?我把它放在父 $SIGINT = sub kill -9 $pid; ?还有一个相关的问题,如果孩子在父母执行 waitpid($pid, 0) 之前已经完成,会发生什么?在多个孩子一次分叉的情况下(带有循环)然后在最后等待。 我无法安装任何模块,所以我应该将错误输出到文件吗? 首先,这不是真的。如果可以安装脚本,则可以安装模块。没有区别。其次,open3 是 Perl 的一部分。您还可以选择传递一个 shell 命令 ('shell.sh args &gt;/dev/null 2&gt;&amp;1'),尽管您必须正确转义您插入其中的任何参数。 我无权在 linux 上安装模块,学校问题。如果我使用 $pid = open3( '&CHILD_STDOUT', '>&CHILD_STDOUT', 'shell.sh', 'args', );这会产生另一个进程吗?或者它是否取代了当前的 exec 之类的。我使用 Fork 和 Exec 的原因是因为我不希望父母等待孩子。但害怕创建僵尸进程。【参考方案2】:

在父线程中使用waitpid:http://perldoc.perl.org/functions/waitpid.html

waitpid($pid, 0);

您还可以将您的 exec 的标准输出重定向到 /dev/null:

exec("shell.sh args > /dev/null");

【讨论】:

以上是关于perl fork() exec() ,子进程变得狂野的主要内容,如果未能解决你的问题,请参考以下文章

逐行读取文件 fork/exec - Perl

fork和exec函数

如果子发出信号,Perl 从 fork/exec 中检测到错误的退出代码

Perl和操作系统交互:fork

fork 与 vfork

Linux下进程的创建(system(); fork(); exec*())