各种$ SIG {CHLD}值之间有什么区别?

时间:2011-12-05 17:34:58

标签: perl unix signals signal-handling sigchld

这些设置有什么区别?

$SIG{CHLD} = 'IGNORE'  
$SIG{CHLD} = 'DEFAULT'  
$SIG{CHLD} = ''  
$SIG{CHLD} = undef

根据“UNIX环境中的高级编程,第2版”,图10.1 SIGCHLD的默认值为“ignore”。

如果“忽略”意味着“SIG_IGN”,那么任何孩子都不会成为僵尸,事实并非如此。

从那里开始并没有那么清楚:

  

如果该过程专门将其处置设置为SIG_IGN,则为子项   调用进程不会生成僵尸进程。注意   这与其默认操作(SIG_DFL)不同,后者来自图   10.1将被忽略。相反,在终止时,这些子进程的状态   被丢弃了。

我很难了解各种值(或未定义的非值)的影响。到目前为止,解决方案一直是轮换这些选择,直到我得到所需的行为,而我更确切地了解每个值如何定义信号的行为。

行为:子进程正在调用“system”或使用反引号来创建另一个子进程,并且信号通常会被错误的(父)处理程序捕获。设置本地处理程序可以工作,但如果我希望来自祖母的信号什么都不做,我不明白哪个值最合适。

有人可以照亮我吗?

更新 根据ikegami的反馈,我做了一些具体的测试。该行为至少部分是特定于平台的。

考虑以下片段:

$SIG{CHLD} = sub {
    while( ( my $child = waitpid( -1, &WNOHANG ) ) > 0 ) {
        print "SIGNAL CHLD $child\n";
    }
};

my $pid = fork();

if( ! $pid ) {
    system( 'echo Grandchild PID = $$' );
    sleep 2;
    exit;
}

print "Child PID = $pid\n";
sleep 5;

Solaris 10上的Perl 5.8.6将显示system()调用的PID的“SIGNAL CHLD”消息。做任何事情,即使像

那样微不足道
  

本地$ SIG {CHLD};

孩子中的

会压制这些消息。

在我尝试过的每一种味道上,收割者都看不到孩子。

4 个答案:

答案 0 :(得分:12)

有两种方法可以避免创建僵尸进程:

  1. 明确设置$SIG{CHLD}='IGNORE'
  2. 使用waitwaitpid调用明确地抓住死去的孩子(这可以在SIGCHLD处理程序中完成,但不一定要这样做)
  3. 设置$SIG{CHLD}='IGNORE'在操作系统级别捕获SIGCHLD,清除子进程,甚至不用发信号通知您的Perl程序。

    任何其他设置,包括'DEFAULT'undef""sub {}'some_function_name_that_doesnt_even_exist'都会导致信号传递给Perl和孩子将不会自动获得。

    通过使用waitwaitpid自行获取流程,您可以获得其他信息,例如子流程的退出状态,以及(或多或少)子流程的处理顺序完了。如果$SIG{CHLD}设置为'IGNORE',则waitwaitpid始终返回-1并且不设置$?

    SIGCHLD,如果有的话,总是被传递给产生子进程的进程,所以我不认为你说来自孙子进程的SIGCHLD是正确的。父进程中捕获了{child}进程中的system调用。可能发生的事情是你的子进程从其父进程继承信号处理程序。

    system和反引号(在大多数系统上)将在完成时生成SIGCHLD,并将使用命令的退出状态设置$?值。但Perl本身会收获这些子流程,您将无法使用systemwait捕获waitpid或反引号通话的流程ID。

答案 1 :(得分:10)

请参阅%SIG

$SIG{CHLD} = 'IGNORE';会导致您的进程忽略SIGCHLD信号。

$SIG{CHLD} = 'DEFAULT';会导致您的进程处理SIGCHLD信号,就像您没有使用$SIG{CHLD}或等效信号一样。根据kill(1),我的系统上的进程默认忽略SIGCHLD

$SIG{CHLD} = '';$SIG{CHLD} = undef;不是有效值。

至于收割,系统会在退出后立即自动获取其SIGCHLD处理程序明确设置为IGNORE的父级子女。

$ perl -e'
   my $pid = fork;
   if (!$pid) { sleep(1); exit; }
   sleep(2);
   system "ps -o pid,stat,command $pid";
'
  PID STAT COMMAND
14667 ZN+  [perl] <defunct>

$ perl -e'
   $SIG{CHLD}="IGNORE";
   my $pid = fork;
   if (!$pid) { sleep(1); exit; }
   sleep(2);
   system "ps -o pid,stat,command $pid";
'
  PID STAT COMMAND

$

答案 2 :(得分:2)

“忽略”有两种不同的含义,它们发生在两个不同的评估点。

第一个用途是关于是否传递信号。当子进程退出时,它通常由操作系统保存,并且CHLD信号被发送到其父进程。将$ SIG {CHLD}设置为'IGNORE'时,它会告诉系统不要生成信号并让子进程退出。父母没有机会获得有关子进程的信息,也没有僵尸的结果。

但是如果$ SIG {CHLD}不是'IGNORE',它意味着将信号传递给父进程,此时有自定义信号处理程序或默认信号处理程序,默认处理程序将在不同系统甚至同一操作系统的版本上有所不同。如果将信号传递给父进程(即处理程序未设置为'IGNORE'),book和signal(7)手册页将讨论默认情况下发生的情况。因此第二次使用忽略与动作采取交付信号有关。例如,SIGINT将导致您的进程终止,而SIGSTOP将导致您的进程“暂停”,尽管自定义信号处理程序会更改这些默认操作。

对于SIGCHLD信号,默认是“忽略”它,但在这个用法中,它只是意味着(父)进程在子进程等待时正常继续执行(即没有终止或中止父进程)。然后,您可以稍后等待()获取信息并避免僵尸。

答案 3 :(得分:1)

  

$ SIG {CHLD} ='IGNORE'

这将作为SIG_IGN传递给操作系统,根据操作系统文档,它会使子进程终止并变为僵尸(或将其退出代码报告给父进程)。

  

$ SIG {CHLD} ='默认'
  $ SIG {CHLD} =''
  $ SIG {CHLD} = undef

这些在OS级别都是相同的(它们将使用SIG_DFL调用signal())。但是,当$ SIG {CHLD}设置为'DEFAULT'时,某些perl包,尤其是AnyEvent :: child,将无效。