8

这些设置有什么区别?

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

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

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

从那里并没有变得更清楚:

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

我很难理解各种值(或未定义的非值)的影响是什么。到目前为止,解决方案一直是在这些选择之间进行轮换,直到获得所需的行为,我宁愿准确了解每个值如何定义信号的行为。

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

有人可以照亮我吗?

更新: 根据 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

4 回答 4

13

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

  1. 显式设置$SIG{CHLD}='IGNORE'
  2. wait使用or调用明确地收割死去的孩子waitpid(这可以在 SIGCHLD 处理程序中完成,但不必这样做)

设置$SIG{CHLD}='IGNORE'会在操作系统级别捕获 SIGCHLD,清理子进程甚至无需向您的 Perl 程序发出信号。

任何其他设置,包括'DEFAULT', undef, "", sub {}'some_function_name_that_doesnt_even_exist'都会导致信号被传递给 Perl,并且不会自动收割子节点。

通过使用waitand自己获取进程waitpid,您可以获得额外的信息,例如子进程的退出状态,以及(或多或少)子进程完成的顺序。如果$SIG{CHLD}设置为'IGNORE'waitwaitpid始终返回 -1 且不设置$?

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

systemSIGCHLD并且反引号将(在大多数系统上)在完成时生成一个,并将$?使用命令的退出状态设置值。但是 Perl 将自己获取这些子进程,您将无法使用 or 捕获 a 或反引号调用的进程systemID 。waitwaitpid

于 2011-12-05T18:09:54.693 回答
11

%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

$
于 2011-12-05T18:17:38.953 回答
2

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

第一个用途涉及是否完全传递信号。当子进程退出时,它通常由操作系统持有,并向其父进程发送 CHLD 信号。当将 $SIG{CHLD} 设置为 'IGNORE' 时,它告诉系统根本不生成信号而只是让子进程退出。父进程没有机会获得有关子进程的信息,因此不会导致僵尸进程。

但是如果 $SIG{CHLD} 不是 'IGNORE' ,则表示将信号传递给父进程,此时有自定义信号处理程序或默认信号处理程序,默认处理程序会因不同而有所不同系统甚至相同操作系统的版本。这本书和 signal(7) 手册页讨论了如果将信号传递给父进程(即处理程序未设置为“IGNORE”)默认情况下会发生什么。因此,ignore 的第二种用法与对传递的信号采取何种操作有关。例如,SIGINT 将导致您的进程终止,而 SIGSTOP 将导致您的进程“暂停”,尽管自定义信号处理程序会更改这些默认操作。

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

于 2015-06-19T01:54:00.127 回答
1

$SIG{CHLD} = '忽略'

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

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

这些在操作系统级别都是相同的(它们将使用 SIG_DFL 调用 signal())。然而,当 $SIG{CHLD} 设置为 'DEFAULT' 时,一些 perl 包,尤其是 AnyEvent::child,将无法工作。

于 2016-04-04T22:11:58.863 回答