0

コマンドの実行時間を制限するスクリプトがあります。

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」によって作成されたプロセスは、テスト 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

テスト A でプロセスが強制終了されたのに、テスト B ではプロセスが強制終了されなかったのはなぜですか? 出力は SIGKILL に影響しますか?

なにか提案を?

4

2 に答える 2

4

( ) とその親 ( )のPIPE間にがあり、にシグナルを送信し、その後終了しますが、シグナルについて何も知らず、実行を続けて に何かを出力します。シグナルを受信して​​も、処理方法がわかりません。それで終了します。php -r 'while(1){sleep(1);echo PHP_OS;}process Cprocess Bposix_kill($pid, SIGKILL)KILLprocess Bprocess Bprocess Cbroken pipeprocess CSIGPIPE

strace (run php limit.php "strace php -r 'while(1){sleep(1); echo PHP_OS;};'" 1) で確認できます。次のように表示されます。

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");
 }
于 2012-08-10T06:33:45.453 に答える
2

フォークされたプロセスにキルシグナルを送信しますが、それはその子または孫には伝播しません。そのため、それらは孤立し、何かが停止するまで実行を続けます。(この場合、stdout に書き込もうとすると、エラーが発生し、強制的に終了する必要があります。出力のリダイレクトも、無期限に実行される孤児になる可能性があります。)

プロセスとそのすべての子プロセスに kill シグナルを送信します。残念ながら、それを行う良い方法を教えてくれる知識がありません。私は PHP のプロセス制御機能にあまり詳しくありません。ps の出力を解析できました。

私が見つけた簡単な方法の 1 つは、kill コマンドを使用してプロセス グループ全体に kill シグナルを送信することです。面倒ですし、私のマシンの出力に「Kill​​ed」メッセージを追加しますが、うまくいくようです。

<?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.
}

詳細については、次を参照してください:すべての子プロセスを強制終了する最良の方法

于 2012-08-10T06:02:02.653 に答える