标准输出影响SIGKILL?

时间:2012-08-10 02:40:04

标签: php linux bash fork sigkill

我有一个脚本来限制命令的执行时间长度。

limit.php

<?php
declare(ticks = 1);

if ($argc<2) die("Wrong parameter\n");
$cmd = $argv[1];
$tl = isset($argv[2]) ? intval($argv[2]) : 3;

$pid = pcntl_fork();
if (-1 == $pid) {
    die('FORK_FAILED');
} elseif ($pid == 0) {
    exec($cmd);
    posix_kill(posix_getppid(), SIGALRM);
} else {
    pcntl_signal(SIGALRM, create_function('$signo',"die('EXECUTE_ENDED');"));
    sleep($tl);
    posix_kill($pid, SIGKILL);
    die("TIMEOUT_KILLED : $pid");
}

然后我用一些命令测试这个脚本。

测试A

php limit.php "php -r 'while(1){sleep(1);echo PHP_OS;}'" 3

3秒后,我们可以发现这些过程已经按照我们的预期被杀死了。

测试B

删除输出代码并再次运行。

php limit.php "php -r 'while(1){sleep(1);}'" 3

结果看起来不太好,函数“exec”创建的进程没有像TEST A那样被杀死。

[alix@s4 tmp]$ ps aux | grep whil[e]
alix      4433  0.0  0.1 139644  6860 pts/0    S    10:32   0:00 php -r while(1){sleep(1);}

系统信息

[alix@s4 tmp]$ uname -a
Linux s4 2.6.18-308.1.1.el5 #1 SMP Wed Mar 7 04:16:51 EST 2012 x86_64 x86_64 x86_64 GNU/Linux
[alix@s4 tmp]$ php -v
PHP 5.3.9 (cli) (built: Feb 15 2012 11:54:46) 
Copyright (c) 1997-2012 The PHP Group
Zend Engine v2.3.0, Copyright (c) 1998-2012 Zend Technologies

为什么进程在TEST A中被杀死而在TEST B中没有?输出是否会影响SIGKILL?

有什么建议吗?

2 个答案:

答案 0 :(得分:4)

PIPEphp -r 'while(1){sleep(1);echo PHP_OS;})与其父(process C)之间有一个process Bposix_kill($pid, SIGKILL)向{KILL发送process B信号1}},然后process B被终止,但是process C对信号一无所知并继续运行,并在broken pipe收到process C时向SIGPIPE输出内容php limit.php "strace php -r 'while(1){sleep(1); echo PHP_OS;};'" 1信号,但不知道如何处理它以便退出。

您可以使用strace验证它(运行14:43:49.254809 write(1, "Linux", 5) = -1 EPIPE (Broken pipe) 14:43:49.254952 --- SIGPIPE (Broken pipe) @ 0 (0) --- 14:43:49.255110 close(2) = 0 14:43:49.255212 close(1) = 0 14:43:49.255307 close(0) = 0 14:43:49.255402 munmap(0x7fb0762f2000, 4096) = 0 14:43:49.257781 munmap(0x7fb076342000, 1052672) = 0 14:43:49.258100 munmap(0x7fb076443000, 266240) = 0 14:43:49.258268 munmap(0x7fb0762f3000, 323584) = 0 14:43:49.258555 exit_group(0) = ? ),您会看到如下内容:

php -r 'while(1){sleep(1);}

至于broken pipe,因为在父亲去世后没有PHP,所以它会继续按预期运行。

一般来说,你应该杀死整个进程组,但不仅仅是进程本身,如果你想要杀死它的孩子,process B你可以将--- limit.php 2012-08-11 20:50:22.000000000 +0800 +++ limit-new.php 2012-08-11 20:50:39.000000000 +0800 @@ -9,11 +9,13 @@ if (-1 == $pid) { die('FORK_FAILED'); } elseif ($pid == 0) { + $_pid = posix_getpid(); + posix_setpgid($_pid, $_pid); exec($cmd); posix_kill(posix_getppid(), SIGALRM); } else { pcntl_signal(SIGALRM, create_function('$signo',"die('EXECUTE_ENDED');")); sleep($tl); - posix_kill($pid, SIGKILL); + posix_kill(-$pid, SIGKILL); die("TIMEOUT_KILLED : $pid"); } 添加到它自己的进程组,并杀死然后是整个小组,这里是您的代码的差异:

{{1}}

答案 1 :(得分:2)

您将kill signall发送到您的分叉进程,但这不会传播给它的子进程或孙子进程。因此,他们是孤儿并继续奔跑直到某些事情阻止他们这样做。 (在这种情况下,任何写入stdout的尝试都应该导致错误,然后强制它们退出。输出的重定向也可能导致无限期运行的孤儿。)

您希望向进程及其所有子进程发送一个终止信号。不幸的是,我缺乏告诉你一个好方法的知识。我不太熟悉PHP的过程控制功能。可以解析ps的输出。

我发现一种简单的方法是使用kill命令向整个进程组发送一个kill信号。它很乱,它在我的机器上输出了额外的“杀死”信息,但似乎有用。

<?php
declare(ticks = 1);

if ($argc<2) die("Wrong parameter\n");
$cmd = $argv[1];
$tl = isset($argv[2]) ? intval($argv[2]) : 3;

$pid = pcntl_fork();
if (-1 == $pid) {
    die('FORK_FAILED');
} elseif ($pid == 0) {
    exec($cmd);
    posix_kill(posix_getppid(), SIGALRM);
} else {
    pcntl_signal(SIGALRM, create_function('$signo',"die('EXECUTE_ENDED');"));
    sleep($tl);
    $gpid = posix_getpgid($pid);

    echo("TIMEOUT_KILLED : $pid");
    exec("kill -KILL -{$gpid}"); //This will also cause the script to kill itself.
}

有关详细信息,请参阅:Best way to kill all child processes