|
|
XCIV. プロセス制御関数
PHPがサポートするプロセス制御関数は、UNIX形式のプロセス生成、プロ
セス実行、シグナル処理、プロセス終了機能を実装しています。
プロセス制御は、Webサーバ環境で有効にするべきではなく、プロセス制
御関数のどれかがWebサーバ環境で使用された場合には、予期しない結果
を生じる可能性があります。
この文書は、プロセス制御関数の一般的な使用法を説明しようとするもの
です。UNIXのプロセス制御に関する詳細な情報については、fork(2),
waitpid(2) and signal(2)のようなシステムのドキュメントや、
[Advanced Programming in the UNIX Environment by
W. Richard Stevens (Addison-Wesley)]のような優れた参考書
を読まれることを推奨します。
注意: この拡張モジュールはWindows環境
では利用できません。
これらの関数は、標準モジュールの一部として利用可能であり、常に使用できます。
PHPがサポートするプロセス制御機能は、デフォルトでは有効となってい
ません。プロセス制御機能を有効にするには、PHPをコンパイルする際に
configure のオプションに
--enable-pcntl
を付ける必要があります。
注意:
現在、このモジュールは非UNIX環境(Windows)では動作しません。
この拡張モジュールは設定ディレクティブを全く定義しません。 この拡張モジュールはリソース型を全く定義しません。
以下のシグナルのリストは、プロセス制御関数でサポートされているもの
です。これらのシグナルのデフォルト動作の詳細については、 signal(7)
のマニュアルを参照下さい。
この例は、シグナルハンドラを有するデーモンプロセスをフォークオフ
します。
例 1. プロセス制御の例
<?php
$pid = pcntl_fork();
if ($pid == -1) {
die("could not fork");
} else if ($pid) {
exit(); } else {
}
if (!posix_setsid()) {
die("could not detach from terminal");
}
pcntl_signal(SIGTERM, "sig_handler");
pcntl_signal(SIGHUP, "sig_handler");
while(1) {
}
function sig_handler($signo) {
switch($signo) {
case SIGTERM:
exit;
break;
case SIGHUP:
break;
default:
}
}
?>
|
|
corychristison at lavacube dot com
03-Nov-2004 02:53
An easier way than what was first suggested in the first comment, to retrieve what signal your application is being sent would be to use get_defined_constants() to list all constants, loop through and strip out those that are not signals, and to check if it matches the value.
Here is my code for doing this, written for PHP5 only.
<?php
function pcntl_sig_identify ( $sig_no ) {
$get_constants = get_defined_constants(true);
$pcntl_contstants = $get_constants["pcntl"];
$keys = array_keys( $pcntl_contstants );
foreach($keys as $key){
if(strstr($key, "SIG") && !strstr($key, "_") && $pcntl_contstants[$key] == $sig_no){
return $key;
}
} } print pcntl_sig_identify(15) . "\n";
?>
andy at cbeyond dot net
20-Aug-2004 04:26
Suppose you want to fork off children to handle a few hundred different targets (like, say, SNMP polling, but that's just one example). Since you don't want to fork-bomb yourself, here's one method of limiting the number of children you have in play at any one time:
#!/usr/bin/php -q
<?php
declare(ticks = 1);
$max=10;
$child=0;
function sig_handler($signo) {
global $child;
switch ($signo) {
case SIGCHLD:
echo "SIGCHLD received\n";
$child -= 1;
}
}
pcntl_signal(SIGCHLD, "sig_handler");
for ($i=1;$i<=20;$i++) {
while ($child >= $max) {
sleep(5); echo "\t Maximum children allowed\n";
}
$child++;
$pid=pcntl_fork();
if ($pid == -1) {
die("could not fork");
} else if ($pid) {
} else {
echo "child number $i\n";
sleep(15);
exit;
}
}
04-Dec-2003 11:52
in example 1, i found unless i create function sig_handler _BEFORE_ pcnt_signal, it wouldnt work.. and would just fall to the floor bleeding to death
(note for people having these kinda probs)
cameronNO_SPAM at tripdubdev dot com
18-Nov-2003 08:36
I'm currently working on some code for this, but in case I forget to come back to post to the board, or in case it takes me a while, why not just have a separate background job running (started up via the shell) that tracks which sockets are available to clients ? Then all you'd have to do is communicate with the one job (or perhaps its own mini-server) run in the background that keeps an array of the available sockets for the server. This seems the most natural alternative since PHP disclaims that process control functionality should not be used in a web-server environment. I would hate to build a server, especially one with high traffic, that had to run through a loop in order to find an available socket.
luca dot mariano at email dot it
12-Nov-2003 02:49
Hi folks,
if someone uses PHP-CLI onWin32 and wants to experiment the PCNTL stuff, I've packed a binary version of PHP with built-in pcntl, shmop, sysvshm and other typical Unix extensions... (thanks to Cygwin DLLs).
Download it:
jeremy at nirvani dot net
16-Oct-2003 03:54
#!/usr/local/bin/php -q
<?php
$children = 5; for ($i=1; $i<=$children; $i++)
{
$pid = pcntl_fork();
if ($pid == -1)
{
die("could not fork\n");
}
else if ($pid)
{
exit(0);
}
else
{
$cpid = pcntl_fork();
if ($cpid == -1)
{
die("could not fork in child process\n");
}
if (!$cpid)
{
print "we are child number $i\n";
exit(0);
}
}
}
?>
David Koopman
14-Sep-2003 08:32
I had a hard time finding a complete example of using PHP as a multi-process (or multi-threaded - I don't understand the difference in these two terms) daemon using connection pooling. I put pieces of the puzzle together and came up with the program below. I hope it helps someone. Notes about making this work:
1) I rebuilt PHP on my machine with these config options:
./configure --enable-sockets --enable-pcntl --enable-sigchild
make
make install
2) I have problems when tried to handle SIGTERM and SIGHUP myself, so I removed these from my code, don't use them unless you have a special need for this:
pcntl_signal(SIGTERM, "sig_handler");
pcntl_signal(SIGHUP, "sig_handler");
What I do is:
1. start the program, then fork to detach from the terminal (kill the parent and make the child the session leader).
2. bind to address and port and start listening.
3. fork $poolNum times, creating $poolNum children (this is the pool of daemons running. The children handle the incoming connections).
4. keep the parent process running in a loop, constantly checking to see if it should create a new child. It will always keep $poolNum spare children ready (as long as the total pooled connections doesn't exceed $maxDaemon). As connections come in, more children are spawned.
5. When a new connection comes in, it is handed off to the first child. This child then sends a SIGUSR1 signal back to the parent. The parent has a signal handler for SIGUSR1, which will increment the $numActive variable by one. The loop that is running (see 4 above) will note the increment in $numActive and automatically create a new child process to keep the process pool going.
I have to post the code in the next note, the note engine on this site won't allow such a long note to be posted, but I think this code example is well worth a comment on this...
schst at php dot net
01-Sep-2003 02:39
To get rid of the zombies when child processes terminate you do not have to write a lot of code that uses complex stuff like message queues.
Instead you only set a signal handler:
pcntl_signal(SIGCHLD, SIG_IGN);
Stephan
Patrice Levesque
14-Jun-2003 08:11
So, you want to create multiple child processes and don't want any zombies, don't you?
You can use IPC to achieve just that. Every child that is spawned has to tell its parent that time has come for him to be terminated. So, yes, zombies will be created, but once in a while the parent will 'clean up' after his kids. Code:
<?php
declare(ticks = 1);
$msgqueue = msg_get_queue(ftok("/tmp/php_msgqueue.stat", 'R'),0666 | IPC_CREAT);
for ($c = 0; $c < 1000; $c++) {
$pcid = pcntl_fork();
if ($pcid == -1) {
die("Could not fork!");
}
elseif ($pcid) { $currentqueue = msg_stat_queue($msgqueue);
$n = $currentqueue['msg_qnum']; if ($n > 0) {
echo "There are $n kids to terminate.\n";
for ($i = 0; $i < $n; $i++) {
if (!msg_receive ($msgqueue, 1, $msg_type, 16384, $msg, true, 0, $msg_error)) {
echo "MSG_RECV ERROR: $errmsg \n"; }
else {
pcntl_waitpid($msg, $tmpstat, 0); };
};
};
}
else { if (!posix_setsid()) { die ("Could not detach"); }; echo "I am child number $c\n";
sleep(5); if (!msg_send($msgqueue, 1, posix_getpid(), true, true, $errmsg)) {
echo "MSG_SEND ERROR: $errmsg \n";
};
exit(); };
};
?>
van[at]webfreshener[dot]com
11-Oct-2002 02:53
Forking your PHP daemon will cause it to zombie on exit.
...or so I've seen on:
FreeBSD (PHP4.2.x)
Debian (PHP4.3.0-dev)
Darwin (PHP4.3.0-dev)
This was tested with the example code above and other scripts created for evaluation.
Seems adding --enable-sigchild to your configure will get rid of the problem.
Hope that saves some hair tearing :]
keksov[at]gmx.de
29-May-2002 05:10
You have to use socket_select before socket_accept, so your code will wait for connection with select. socket_select can be interrupted by signals easily. Below is an example from my library (methods of class TNetSocket):
//-- select
function select($aread=NULL,$awrite=NULL,$aexcept=NULL,$timeout=NULL)
{
while(1)
{
$res="";
$res=socket_select($aread, $awrite, $aexcept, $timeout);
// if errno===0 it means what select was interrrupted by SysV signal
if($res===false && socket_last_error($this->socket())!==0)
{ // error occured, interrupted not by a signal
$this->set_socket_error(__LINE__);
return(false);
}
break;
}
return(true);
}
//-- accept, wait for incomming connection
function accept()
{
$this->clear_socket_error();
$this->set_io_socket(_SOCKET_);
$socket=$this->socket();
$aread=array($socket);
if ($this->select($a=&$aread)===false)
return(false);
$child_socket=socket_accept($this->socket);
if($child_socket <= 0)
{ // error occured
$this->set_socket_error(__LINE__);
return(false);
}
$this->child_socket=$child_socket;
$this->sockets[_CHILD_SOCKET_]=&$this->child_socket;
$this->set_io_socket(_CHILD_SOCKET_);
$a=&$this->peername;
$res=socket_getpeername($child_socket,$a);
if($res <= 0)
{ // error occured
$this->set_socket_error(__LINE__);
return(false);
}
$this->get_address_and_port(_CHILD_SOCKET_);
TLogManager::phpserv("Connection accepted. ADDRESS $this->address, PORT $this->port","net_socket",__FILE__,__LINE__);
$this->connected=true;
return(true); // return new object of TNetSocket type
}
daniel[at]lorch.cc
27-Feb-2002 11:48
This piece of code helped me to find out what signals are being sent to my process:
function sig_identify($signo) {
switch($signo) {
case SIGFPE: return 'SIGFPE';
case SIGSTOP: return 'SIGSTOP';
case SIGHUP: return 'SIGHUP';
case SIGINT: return 'SIGINT';
case SIGQUIT: return 'SIGQUIT';
case SIGILL: return 'SIGILL';
case SIGTRAP: return 'SIGTRAP';
case SIGABRT: return 'SIGABRT';
case SIGIOT: return 'SIGIOT';
case SIGBUS: return 'SIGBUS';
case SIGPOLL: return 'SIGPOLL';
case SIGSYS: return 'SIGSYS';
case SIGCONT: return 'SIGCONT';
case SIGUSR1: return 'SIGUSR1';
case SIGUSR2: return 'SIGUSR2';
case SIGSEGV: return 'SIGSEGV';
case SIGPIPE: return 'SIGPIPE';
case SIGALRM: return 'SIGALRM';
case SIGTERM: return 'SIGTERM';
case SIGSTKFLT: return 'SIGSTKFLT';
case SIGCHLD: return 'SIGCHLD';
case SIGCLD: return 'SIGCLD';
case SIGIO: return 'SIGIO';
case SIGKILL: return 'SIGKILL';
case SIGTSTP: return 'SIGTSTP';
case SIGTTIN: return 'SIGTTIN';
case SIGTTOU: return 'SIGTTOU';
case SIGURG: return 'SIGURG';
case SIGXCPU: return 'SIGXCPU';
case SIGXFSZ: return 'SIGXFSZ';
case SIGVTALRM: return 'SIGVTALRM';
case SIGPROF: return 'SIGPROF';
case SIGWINCH: return 'SIGWINCH';
case SIGPWR: return 'SIGPWR';
}
}
function sig_handler($signo) {
echo "Caught " . sig_identify($signo) . " (" . $signo . ") on " . posix_getpid() . "\n";
}
pcntl_signal(SIGFPE, "sig_handler");
pcntl_signal(SIGHUP, "sig_handler");
// pcntl_signal(SIGINT, "sig_handler");
pcntl_signal(SIGQUIT, "sig_handler");
pcntl_signal(SIGILL, "sig_handler");
pcntl_signal(SIGTRAP, "sig_handler");
pcntl_signal(SIGABRT, "sig_handler");
pcntl_signal(SIGIOT, "sig_handler");
pcntl_signal(SIGBUS, "sig_handler");
pcntl_signal(SIGPOLL, "sig_handler");
pcntl_signal(SIGSYS, "sig_handler");
pcntl_signal(SIGCONT, "sig_handler");
pcntl_signal(SIGUSR1, "sig_handler");
pcntl_signal(SIGUSR2, "sig_handler");
pcntl_signal(SIGSEGV, "sig_handler");
pcntl_signal(SIGPIPE, "sig_handler");
pcntl_signal(SIGALRM, "sig_handler");
pcntl_signal(SIGTERM, "sig_handler");
pcntl_signal(SIGSTKFLT, "sig_handler");
pcntl_signal(SIGCHLD, "sig_handler");
pcntl_signal(SIGCLD, "sig_handler");
pcntl_signal(SIGIO, "sig_handler");
pcntl_signal(SIGTSTP, "sig_handler");
pcntl_signal(SIGTTIN, "sig_handler");
pcntl_signal(SIGTTOU, "sig_handler");
pcntl_signal(SIGURG, "sig_handler");
pcntl_signal(SIGXCPU, "sig_handler");
pcntl_signal(SIGXFSZ, "sig_handler");
pcntl_signal(SIGVTALRM, "sig_handler");
pcntl_signal(SIGPROF, "sig_handler");
pcntl_signal(SIGWINCH, "sig_handler");
pcntl_signal(SIGPWR, "sig_handler");
I commented out SIGNIT, as it is the signal which is sent to your process when you press CTRL-C. If you catch this signal, you must handle it properly:
function sig_handler($signo) {
switch($signo) {
case SIGINT:
// customized cleanup code
exit; // now exit
break;
}
}
Otherwise the only possibility to stop your process is by sending a SIGKILL signal - you can do this on the shell by typing "kill -9 PID" (where -9 is the numerical value for SIGKILL).
Note: You cannot add a handler (i.e. ignore signals) for SIGSTOP and SIGKILL - for obvious reasons.
| |