PHPのお勉強!

PHP TOP

PCNTL 関数

参考

POSIX 関数の節を参照することも 有用でしょう。

目次

  • pcntl_alarm — シグナルを送信するアラームを設定する
  • pcntl_async_signals — 非同期のシグナルハンドリングを有効/無効にするか、古い設定を返す
  • pcntl_errno — pcntl_get_last_error のエイリアス
  • pcntl_exec — 現在のプロセス空間で指定したプログラムを実行する
  • pcntl_fork — 現在実行中のプロセスをフォークする
  • pcntl_get_last_error — 直近の pcntl 関数が失敗したときのエラー番号を取得する
  • pcntl_getpriority — プロセスの優先度を取得する
  • pcntl_rfork — プロセスのリソースを管理する
  • pcntl_setpriority — プロセスの優先度を変更する
  • pcntl_signal — シグナルハンドラを設定する
  • pcntl_signal_dispatch — ペンディングシグナル用のハンドラをコールする
  • pcntl_signal_get_handler — 指定されたシグナルの現在のハンドラを取得する
  • pcntl_sigprocmask — ブロックされたシグナルを設定あるいは取得する
  • pcntl_sigtimedwait — タイムアウトつきでシグナルを待つ
  • pcntl_sigwaitinfo — シグナルを待つ
  • pcntl_strerror — 指定した errno に対応するシステムのエラーメッセージを取得する
  • pcntl_unshare — プロセスの実行コンテキストの一部を切り離す
  • pcntl_wait — 待つかフォークした子プロセスのステータスを返す
  • pcntl_waitid — Waits for a child process to change state
  • pcntl_waitpid — 待つかフォークした子プロセスのステータスを返す
  • pcntl_wexitstatus — 終了した子プロセスのリターンコードを返す
  • pcntl_wifexited — ステータスコードが正常終了を表しているかどうかを調べる
  • pcntl_wifsignaled — ステータスコードがシグナルによる終了を表しているかどうかを調べる
  • pcntl_wifstopped — 子プロセスが現在停止しているかどうかを調べる
  • pcntl_wstopsig — 子プロセスを停止させたシグナルを返す
  • pcntl_wtermsig — 子プロセスを終了させたシグナルを返す
add a note

User Contributed Notes 18 notes

up
10
kevin at vanzonneveld dot net
16 years ago
If you want to create a daemon in PHP, consider using System_Daemon http://pear.php.net/package/System_Daemon

Install it like this:
pear install -f system_daemon

Then use it like this:
<?php
// Include PEAR's Daemon Class
require_once "System/Daemon.php";

// Bare minimum setup
System_Daemon::setOption("appName", "mydaemonname");

// Spawn Deamon!
System_Daemon::start();

// Your PHP Here!
while (true) {
doTask();
}

// Stop daemon!
System_Daemon::stop();
?>

More examples can be found inside the PEAR package.
up
10
registrazioni at XSPAMX dot tassetti dot net
17 years ago
(PHP 5.2.4)

This is an example of multithreading keeping different connections to a mysql database: when children exit they close the connection and others can't use it any more generating problems. In this example I used variable variables to make a different connection per each child.

This scripts loops forever with one mother detached from the terminal and five children 'named' from 1 to 5. When one children sees it's name in the database (one table 'test.tb' with only one field 'test') he lets himself die. To kill children insert their value in the db. The mother suicides only when all children are dead.

What a sad but interesting story...

$npid = pcntl_fork(); // DETACH FROM TERMINAL AND BE REAPED BY INIT

if ($npid==-1) die("Error: impossible to pcntl_fork()\n");
else if ($npid) exit(0); // THE GRANPA DIES
else // MOTHER GOES ON TO MAKE CHILDREN
{
$children = 5;
for ($i=1; $i<=$children; $i++)
{
$pid = pcntl_fork();
if ($pid==-1) die("Error: impossible to pcntl_fork()\n");
else if ($pid)
{
$pid_arr[$i] = $pid;
}
if (!$pid) // CHILDREN
{
global $vconn;
$vconn = "vconn$i";
global $$vconn;
$$vconn = @mysql_connect("mydbhost","mydbuser","mydbpwd");
if (!($$vconn)) echo mysql_error();
if (!($$vconn)) exit;

while (1)
{
$query = "SELECT test FROM test.tb";
$rs = mysql_query($query,$$vconn);
$rw = mysql_fetch_row($rs);
if ($rw[0]==$i) exit;
else
{
echo "Database is $rw[0] and I am $i, it's not my time, I will wait....\n";
sleep(1);
}
}
}
}

foreach ($pid_arr as $pid)
{
// we are the parent and we wait for all children to die
pcntl_waitpid($pid, $status);
}
echo "All my children died, I will suicide...\n";
exit();
}
up
2
kementeusNOSPAM at gmail dot com
17 years ago
In the example of the documentation we need to put the pctnl_signal statements BEFORE the while loop.

In that way we can execute whatever we put in the signal handler functions.
up
1
David Koopman
21 years ago
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...
up
1
luca dot mariano at email dot it
21 years ago
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: http://phplet.sf.net/modules.php?name=Web_Links&l_op=visit&lid=4
up
2
debasiss at mindfiresolutions dot com
5 years ago
<?php
/**
* @file
* Working demo of simulating parallel process in PHP.
*/

// **** **** //
print "Main program started.... \n";
// Start two child processes.
createProcess("Job1");
createProcess("Job2");
print
" *** Child Process started *** \n";

while (
TRUE) {

$pid = pcntl_waitpid(0, $status, WNOHANG);
if (
$pid > 0) {
childProcessComplete($pid);
}else if(
$pid === -1){
print
" *** Child Process Completed *** \n";
allChildProcessComplete();
exit();
}

}
print
"Main program end. \n";

/**
* Method to start child process.
*/
function startChildProcess($childProcessName) {
$executionTime = rand(5, 10);

if(
$childProcessName === "Job1"){
print
"Starting Job1 on processId " .getmypid()." at " . date('l jS \of F Y h:i:s A') . " expected time $executionTime seconds\n";
}else if(
$childProcessName === "Job2"){
print
"Starting Job2 on processId " .getmypid()." at " . date('l jS \of F Y h:i:s A') . " expected time $executionTime seconds\n";
}

// Simulate doing actual work with sleep().
sleep($executionTime);
}

/**
* Method to notify when system unable to start a process.
*/
function errorOnProcessLunch() {
print
"Failed to lunch process \n";
}

/**
* Method to notify when a child process complete the task.
*/
function childProcessComplete($pid) {
print
"Child processing is done for $pid at " . date('l jS \of F Y h:i:s A') . " \n";
}

/**
* Method to create a new process.
*/
function createProcess($pname) {
$pid = pcntl_fork();

if (
$pid == -1) {
errorOnProcessLunch();
}
else if (
$pid === 0) {
startChildProcess($pname);
exit();
// Make sure to exit.
}
else {
startParentProcess($pid);
}

}

/**
* Method to notify when parent thread execute.
*/
function startParentProcess($childProcessID){
print
"In parent thread created child processid $childProcessID \n";
}

/**
* Method to notify when all child process has completed the task.
*/
function allChildProcessComplete(){
print
"All child processing completed \n";
}

?>
up
2
jeremy at nirvani dot net
21 years ago
#!/usr/local/bin/php -q
<?php

# Jeremy Brand <jeremy@nirvani.net>
# http://www.nirvani.net/

# ./configure --enable-pcntl --enable-sigchild
# make
# make install

# This code example shows how to use a script to do multiprocessing. Each time
# this script is ran, the result is 5 (in this example) processes running to
# accomplish a specified task.

# Examples could be a messaging queue. You could get the number of messages in
# a queue and handle any or all of them asynchronously.

# Get the number of children you want to be born by running this
# script once.
$children = 5; # likely a function call here.

for ($i=1; $i<=$children; $i++)
{

$pid = pcntl_fork();
if (
$pid == -1)
{
die(
"could not fork\n");
}
else if (
$pid)
{
# If we are the parent, we did our job of giving birth,
# now lets finish our job and die!
exit(0);
}
else
{
# Since we are the child, fork so that init will become our parent. Init
# is good at wait()ing on children - ie: reaping.
$cpid = pcntl_fork();
if (
$cpid == -1)
{
die(
"could not fork in child process\n");
}
if (!
$cpid)
{
# We have now forked off from our parent and also are not waiting on any
# other children to process, however other children are running
# simutaniously to us. Make sure the code you write here is safe to run
# in this environment of multiprocessing - ie: proper locking, etc.
# Write the custom code here that you want to multiprocess.

# ADD CODE HERE TO MULTIPROCESS
print "we are child number $i\n";

# Don't forget to exit after the child processing is done. Certainly
# change this exit code if you need to however.
exit(0);
}
}
}

?>
up
2
keksov[at]gmx.de
22 years ago
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
}
up
1
Anonymous
21 years ago
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)
up
1
cameronNO_SPAM at tripdubdev dot com
21 years ago
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.
up
1
schst at php dot net
21 years ago
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
up
1
daniel[at]lorch.cc
22 years ago
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.
up
0
m dot quinton at gmail dot com
16 years ago
a nice example of a class framework to manage tasks using pcntl_fork.

<?php

/**
* author : Marc Quinton / April 2008.
*
* a simple task management framework using pcntl_fork, pcntl_wait.
*
* - see at bottom for a sample usage.
* - you shoud overring Task class (SleepingClass is an example), and manage them in a pool, using taskManager
*/

error_reporting(E_ALL);

class
Task {

protected
$pid;
protected
$ppid;

function
__construct(){
}

function
fork(){
$pid = pcntl_fork();
if (
$pid == -1)
throw new
Exception ('fork error on Task object');
elseif (
$pid) {
# we are in parent class
$this->pid = $pid;
# echo "< in parent with pid {$his->pid}\n";
} else{
# we are is child
$this->run();
}
}

function
run(){
# echo "> in child {$this->pid}\n";
# sleep(rand(1,3));
$this->ppid = posix_getppid();
$this->pid = posix_getpid();
}

# call when a task in finished (in parent)
function finish(){
echo
"task finished {$this->pid}\n";
}

function
pid(){
return
$this->pid;
}
}

class
SleepingTask extends Task{
function
run(){
parent::run();
echo
"> in child {$this->pid}\n";

# print_r($this);

sleep(rand(1,5));
echo
"> child done {$this->pid}\n";
exit(
0);
}
}

class
TaskManager{

protected
$pool;

function
__construct(){
$this->pool = array();
}

function
add_task($task){
$this->pool[] = $task;
}

function
run(){

foreach(
$this->pool as $task){
$task->fork();
}

# print_r($this);
# sleep(60);

while(1){
echo
"waiting\n";
$pid = pcntl_wait($extra);
if(
$pid == -1)
break;

echo
": task done : $pid\n";
$this->finish_task($pid);
}

echo
"processes done ; exiting\n";
exit(
0);
}

function
finish_task($pid){
if(
$task = $this->pid_to_task($pid))
$task->finish();
}

function
pid_to_task($pid){
foreach(
$this->pool as $task){
if(
$task->pid() == $pid)
return
$task;
}
return
false;
}
}

$manager = new TaskManager();

for(
$i=0 ; $i<10 ; $i++)
$manager->add_task(new SleepingTask());

$manager->run();

?>