问题描述
我有一个脚本来限制命令的执行时间.
I have a script to limit the execution time length of commands.
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");
}
然后我用一些命令测试此脚本.
Then I test this script with some commands.
测试A
php limit.php "php -r 'while(1){sleep(1);echo PHP_OS;}'" 3
3秒后,我们可以发现进程已按预期终止.
After 3s, we can find the processes were killed as we expected.
测试B
删除输出代码,然后再次运行.
Remove the output code and run again.
php limit.php "php -r 'while(1){sleep(1);}'" 3
结果看起来不太好,由函数"exec"创建的进程没有像测试A那样被杀死.
Result looks not good, the process created by function "exec" was not killed like 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?
Why the processes killed in TEST A but not in TEST B? Does the output impact the SIGKILL?
有什么建议吗?
推荐答案
在php -r 'while(1){sleep(1);echo PHP_OS;}
(process C
)和它的父级(process B
)之间有一个PIPE
,posix_kill($pid, SIGKILL)
发送KILL
信号到process B
,然后process B
被终止,但是process C
对信号一无所知,并继续运行,并在process C
接收到SIGPIPE
信号但不知道时将某些内容输出到broken pipe
如何处理它以便退出.
There is a PIPE
between php -r 'while(1){sleep(1);echo PHP_OS;}
(process C
) and it's parent (process B
), posix_kill($pid, SIGKILL)
sends KILL
signal to process B
, then process B
is terminated, but process C
doesn't know anything about the signal and continues to run and outputs something to the broken pipe
, when process C
receives the SIGPIPE
signal but has no idea how to handle it so it exits.
您可以使用strace验证它(运行php limit.php "strace php -r 'while(1){sleep(1); echo PHP_OS;};'" 1
),您将看到类似以下内容:
You can verify it with strace (run php limit.php "strace php -r 'while(1){sleep(1); echo PHP_OS;};'" 1
), and you will see something like this:
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
的父项去世后没有发生,所以它继续按预期运行.
As to php -r 'while(1){sleep(1);}
, because there is no broken pipe
occurs after it's parent dies, so it continues to run as expected.
通常来说,您应该杀死整个进程组,但如果要杀死它的子进程,则不仅要杀死进程本身,还可以使用PHP
杀死process B
到其自己的进程组中,然后杀死整个进程组,这是您代码的差异:
Generally speaking, you should kill the whole process group but not only the process itself if you want to kill it's children too, with PHP
you can add process B
to its own process group, and kill the whole group then, here is the diff with your code:
--- 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");
}
这篇关于标准输出影响SIGKILL?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!