I've posted this here before, but the article has been down so my post got deleted.
I've written an in-depth look at pcntl_fork() which is available here:
http://www.van-steenbeek.net/?q=php_pcntl_fork
pcntl_fork
(PHP 4 >= 4.1.0, PHP 5)
pcntl_fork — Verzweigt den laufenden Prozess
Beschreibung
Die Funktion pcntl_fork() erzeugt einen Kindprozess der sich von seinem Vaterprozess nur durch seine PID und PPID unterscheidet. Bitte schauen Sie für spezifische Dateils, wie Fork auf Ihrem System arbeitet in die fork(2) man Seite Ihres Systems.
Bei Erfolg wird die PID des Kindprozesses an den Ausführungsstrang des Vaters und eine 0 an den Ausführungsstrang des Kindes zurückgegeben. Bei einem Fehlschlag wird -1 in den Kontext des Vaters zurückgegeben, kein Kindprozess erzeugt und ein PHP Fehler ausgelöst.
Beispiel #1 pcntl_fork() Beispiel
<?php
$pid = pcntl_fork();
if ($pid == -1) {
die('Konnte nicht verzweigen');
} else if ($pid) {
// Wir sind der Vater
pcntl_wait($status); //Schützt uns vor Zombie Kindern
} else {
// Wir sind das Kind
}
?>
Siehe auch pcntl_waitpid() und pcntl_signal().
pcntl_fork
30-May-2007 02:12
25-Oct-2006 11:06
The reason for the MySQL "Lost Connection during query" issue when forking is the fact that the child process inherits the parent's database connection. When the child exits, the connection is closed. If the parent is performing a query at this very moment, it is doing it on an already closed connection, hence the error.
An easy way to avoid this is to create a new database connection in parent immediately after forking. Don't forget to force a new connection by passing true in the 4th argument of mysql_connect():
<?
// Create the MySQL connection
$db = mysql_connect($server, $username, $password);
$pid = pcntl_fork();
if ( $pid == -1 ) {
// Fork failed
exit(1);
} else if ( $pid ) {
// We are the parent
// Can no longer use $db because it will be closed by the child
// Instead, make a new MySQL connection for ourselves to work with
$db = mysql_connect($server, $username, $password, true);
} else {
// We are the child
// Do something with the inherited connection here
// It will get closed upon exit
exit(0);
?>
This way, the child will inherit the old connection, will work on it and will close upon exit. The parent won't care, because it will open a new connection for itself immediately after forking.
Hope this helps.
26-Aug-2006 06:59
I think this simple code can help understand how fork works:
<?
echo "posix_getpid()=".posix_getpid().", posix_getppid()=".posix_getppid()."\n";
$pid = pcntl_fork();
if ($pid == -1) die("could not fork");
if ($pid) {
echo "pid=".$pid.", posix_getpid()=".posix_getpid().", posix_getppid()=".posix_getppid()."\n";
} else {
echo "pid=".$pid.", posix_getpid()=".posix_getpid().", posix_getppid()=".posix_getppid()."\n";
}
?>
21-Jul-2006 11:45
In response to Jon Dowland, a fork is actually not that expensive of an operation. Yes, a fork creates a copy of the process, so you would think it would be an expensive operation, but instead operating systems implement what is known as "copy-on-write." Instead of actually making a copy of the data, both processes reference the same data as long as they're only reading from it. Once a process goes to write to the data, then the child process gets its own copy of it. So, the majority of the PHP engine would not have to be copied and thus this would not be very expensive.
03-Mar-2006 07: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
26-May-2005 04: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.
13-Feb-2005 04: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()'
01-Jul-2004 07: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.
01-Apr-2004 01: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
26-Sep-2002 12: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);
}
?>