pcntl_fork

(PHP 4 >= 4.1.0, PHP 5)

pcntl_fork -- Forks the currently running process

Description

int pcntl_fork ( void )

The pcntl_fork() function creates a child process that differs from the parent process only in its PID and PPID. Please see your system's fork(2) man page for specific details as to how fork works on your system.

On success, the PID of the child process is returned in the parent's thread of execution, and a 0 is returned in the child's thread of execution. On failure, a -1 will be returned in the parent's context, no child process will be created, and a PHP error is raised.

例子 1. pcntl_fork() example

<?php

$pid
= pcntl_fork();
if (
$pid == -1) {
     die(
'could not fork');
} else if (
$pid) {
     
// we are the parent
     
pcntl_wait($status); //Protect against Zombie children
} else {
     
// we are the child
}

?>

See also pcntl_waitpid() and pcntl_signal().


add a note add a note User Contributed Notes
spam at kacke dot de
04-Mar-2006 02:52
I disagree too with reinoud at hyves dot nl too.

It seems youll always get the mysql-error 0 "lost connetion during query" when massive forking at the same time like
while x = mysql_fetch_array() -> fork

If you put for example a usleep between each fork youll dont loose the connection.

I figured that out sometimes and maybe its a "workaround" that helps

Toppi
erm at the-erm dot com
07-Dec-2005 11:52
This creates children, and grand children processes.

<?php

  
declare(ticks=1);

   echo
"I'm going to be a Dad.\n";
   if (
spawn_child('child_function')) {
       echo
'Parent pid:'.posix_getpid()."\n";
   }
  
  
   echo
"I'm going to be a Dad again!.\n";
   if (
spawn_child('child_function',1,2,3,4)) {
       echo
'Child - 2 Parent pid:'.posix_getpid()."\n";
   }

   echo
"What you're pregnant again!?.\n";
   if (
spawn_child('grand_children')) {
       echo
'Child - 3 Parent pid:'.posix_getpid()."\n";
   }

   function
grand_children() {
       echo
"Dad I'm going to have a baby.\n";
      
spawn_child('child_function','Joe');
       echo
"I'm so proud of my kids.\n";
   }

   function
spawn_child($function) {
      
$original_pid = posix_getpid();
      
$pid = pcntl_fork();
       if (
$pid == -1) {
           die (
"Unable to spawn child\n");
       }
      
$new_pid = posix_getpid();
       if (
$new_pid == $original_pid) {
           return
true;
       }
       if (
function_exists($function)) {
          
$numargs = func_num_args();
           if (
$numargs > 1) {
              
$args = func_get_args();
              
//print_r ($args);
              
unset($args[0]);
              
call_user_func($function,$args);
           } else {
              
call_user_func($function);
           }
           echo
"Done with child ".$new_pid." they moved out.\n";
           exit;
       } else {
           die (
"$function does not exist\n");
       }
   }

   function
child_function() {
       echo
'Child pid:'.posix_getpid()."\n";
      
$args = func_get_args();
       if (!empty(
$args))
          
print_r ($args);
   }

  
pcntl_wait( $status);

?>
rob at bronco dot co dot uk
26-May-2005 10:53
I disagree with reinoud at hyves dot nl. I have multiple examples of software which connects to a database then forks a great number of times, and every child has full access to the database link resource. The only issues with forking and MySQL that I have experienced is that you may get the occasional "MySQL server has gone away", although that particular error is probably more related to massively overloading a database.
reinoud at hyves dot nl
24-Mar-2005 03:01
Note that (among other things ?) your mysql resources need to be closed neatly before doing a pcntl_fork. If not, you might have mysql_query return false with errno=0 and no error message - even if the child doesn't touch the database at all. So I ended up using the following code:

$database_resource=mysql_connect($server,$port, $dbuser, $dbpass);
someCodeWhichDoesSomeQueries($database_resource);

mysql_close($database_resource);
if (pcntl_fork() === 0)
{
   $database_resource=mysql_connect($server,$port, $dbuser, $dbpass); //obviously you only add this if the child is going to interact with the database
   executeChildCode($database_resource);
   exit();
}
$database_resource=mysql_connect($server,$port, $dbuser, $dbpass);
executeParentCode($database_resource);
arnold at helderhosting dot nl
13-Feb-2005 11:12
It is not possible to use the function 'pcntl_fork' when PHP is used as Apache module. You can only use pcntl_fork in CGI mode or from command-line.

Using this function will result in: 'Fatal error: Call to undefined function: pcntl_fork()'
Jon Dowland <bugs at alcopop dot org>
21-Dec-2004 07:26
I thought I should add a quick note to clarify the mentioning of threads below.

This procedure does not create a new thread: a thread is a seperate execution strand within the same process. So, a thread has access to the same memory as the parent and no duplication of process space is done.

A fork is a relatively expensive procedure as the parent processes memory segments are copied. I don't know the internal PHP implementation details but this most likely includes the PHP interpreter. A child does not have access to the memory of the parent: only a copy of it.
ej <at> wellkeeper dot com
01-Jul-2004 01:56
If you understand what a UNiX fork() command actually does, then the behaviour astrolox describes below is precisely what one would expect. A fork() command creates an identical process (save for the PID) and that includes the TEXT segment, DATA segment, stack, heap, file descriptors, etc.  If you don't want the child to have what's in the buffer, just throw it out (in the child - the parent doesn't need to do anything differently).

  As for his new server that doesn't display the same behaviour, I'm not sure what to say. It would be a mistake for PHP to throw away that buffer for you behind the scenes because someone may well want a child that has access to that buffer. If fork() is properly implemented the *ONLY* thing that is different in the child is that it has it's own PID.
astrolox at lawyersonline dot co dot uk
01-Apr-2004 07:40
When using php 4.3.3, I found that output buffering can really confuse you if you use fork within your script.

The buffer (everything which hasn't been placed on the screen yet) is copied to the child process.

This means that when the child flushes it's buffer, which happens automatically when the script ends, you will get a copy of everything which your code (before the fork) tried to place on to the screen.

This causes it to look like all your code before the fork has run twice, when actually it hasn't.

This doesn't happen on my other server running a newer version of php, but that might be a configuration issue.

Note that using ob_implicit_flush() at the start of the script doesn't appear to solve the problem. Adding ob_end_flush() before the pcntl_fork() command does solve the problem.

Example:
<?
echo "This is an echo before I called the fork command\n";

// uncomment the next line to fix the described problem
#ob_end_flush(); 

$pid = pcntl_fork();
if (
$pid == -1) {
   die(
"could not fork");
} else if (
$pid) {
   echo
"I am the parent, pid = ". $pid ."\n";
} else {
   echo
"I am the child, pid = ". $pid ."\n";
}
?>

Output, before "fixed":

This is an echo before I called the fork command
I am the parent, pid = 15449
This is an echo before I called the fork command
I am the child, pid = 0

Output, after "fixed":

This is an echo before I called the fork command
I am the parent, pid = 15023
I am the child, pid = 0
Jazeps Basko, jb4 [at] 42901.lv
01-Nov-2003 12:01
Here is my example how one can create a number of threads that do the same thing.
The thing that each instance should execute is written in Process::go() method.
This code creates three threads called a, b and c. They all do the same thing: they iterate ten times through a loop that says them to wait for a second and then print out their name and the iteration number.
If you executed this on your UNIX machine that has PHP installed in /usr/local/phpcli (change that if you have PHP elsewhere), you'd get smth like this:

a1
b1
c1
a2
b2
c2
a3
...
..
a9
b9
c9

[ note that the order how a1, b1, c1 appear may change - if by some reason a thread goes a little bit slower than b or c, it would appear after the b1 and c1, and other iteration indicators of b, c threads too ]

So, here comes the code:

#! /usr/local/phpcli/bin/php -q
<?php

  
/*
       PHP jaabuut kaa CGI, --enable-pcntl
   */

  
class Process
  
{
       var
$ident;
  
       function
Process( $ident )
       {
          
$this->ident = $ident;
       }
      
       function
go()
       {
           for (
$i=1; $i<10; $i++ ) {
              
sleep(1);
               echo
$this->ident . $i . "\n";
           }
       }
   }
  
  
$childrenTotal = 3;
  
$childrenNow = 0;
  
$childrenNames = array( 'a', 'b', 'c' );
  
   while (
$childrenNow < $childrenTotal ) {
  
      
$pid = pcntl_fork();
      
       if (
$pid == -1 ) {
           die(
"error\n" );
       } elseif (
$pid == 0 ) {
          
$childrenNow++;
       } else {
          
$process = new Process( $childrenNames[$childrenNow] );
          
$process->go();
           die();
       }
      
   }
?>
porkchop at porkchop dot net
24-Jul-2003 09:58
On a fork, vars themselves are copied into the new child's space, not just a refrence to that var.

Consider:
<?
$foo
="foo";
$pid=pcntl_fork();
if(
$pid != 0) { /* we're here if this is the parent */
 
$foo="bar";
 echo
$foo; // returns "bar".
 
die("Child complete");
} else {
/* we're the child */
 
`sleep 5`
 echo
$foo; // returns "foo" even though the parent set $foo to "bar" about 5 seconds earlier.
};
?>
ben at gelbnet dot com
26-Sep-2002 06:54
I was writing a shell script to get input from a user, however, I needed my script to time out after a certain number of seconds if the user didn't enter enough data. The code below descibes the method I used. It's a little hairy but it does work.

-Ben

#!/home/ben/php/bin/php -q
<?
//GLOBALS
$RETURN_CHAR = "\n";
$TIMEOUT = 5; //number of seconds to timeout on input
$PID = getmypid();
$CHILD_PID = 0;

//Make sure program execution doesn't time out
set_time_limit(0);

function
set_timeout() {
global
$PID;
global
$CHILD_PID;
global
$TIMEOUT;

$CHILD_PID = pcntl_fork();
if(
$CHILD_PID == 0) {
sleep($TIMEOUT);
posix_kill($PID, SIGTERM);
exit;
}
}

function
clear_timeout() {
global
$CHILD_PID;
posix_kill($CHILD_PID, SIGTERM);
}

// read_data()
// gets a line of data from STDIN and returns it
function read_data() {

$in = fopen("php://stdin", "r");
set_timeout();
$in_string = fgets($in, 255);
clear_timeout();
fclose($in);
return
$in_string;
}

// write_data($outstring)
// writes data to STDOUT
function write_data($outstring) {
$out = fopen("php://stdout", "w");
fwrite($out, $outstring);
fclose($out);
}

while(
1) {
write_data("say something->");
$input = read_data();
write_data($RETURN_CHAR.$input);
}

?>