|
pcntl_fork
Forks the currently running process
(PHP 4 >= 4.0.7, PHP 5)
Example 1711. pcntl_fork() example<?php Code Examples / Notes » pcntl_forkastrolox
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 amatsak
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. magicaltux
QUOTE from arnold at helderhosting dot nl: 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()' ============================================== This is not true. I already had to use pcntl_fork() from PHP running as an Apache module so I know. By default, pcntl is disabled when php is compiled as an apache module, which is to be expected. Forking in an apache module is rather complex, and one of the most important things you should remember is to let your child kill himself with SIGKILL. If you just call exit(), you might end having duplicate headers. Forking from an Apache module is NOT RECOMMANDED unless you know what you do and fully understand how apache works. underdog
QUOTE from arnold at helderhosting dot nl: 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()' ============================================== THANKS, Arnold! This bears repeating... (Note to PHP documentation developers: Please add this important comment to the main documentation for this function - it would saved me a lot of time debugging!) arnold
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()' jeff
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.
ej
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. frans-jan
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 ben
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); } ?> xuecan
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"; } ?> rob
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.
spam
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 |