This article mainly introduces how to use multi-process programming functions in PHP using examples, including processing zombie processes! You can refer to a set of process control functions in PHP (enable-pcntl and posix extensions are required during compilation ), php allows you to create sub-processes, execute programs using exec functions, and process signals like c.
<? Php header ('content-type: text/html; charset = UTF-8 '); // The extension if (! Function_exists ("pcntl_fork") {die ("pcntl extention is must! ");} // Total number of processes $ totals = 3; // Number of executed scripts $ cmdArr = array (); // array of the number of executed scripts ($ I = 0; $ I <$ totals; $ I ++) {$ cmdArr [] = array ("path" => _ DIR __. "/run. php ", 'pid '=> $ I, 'Total' => $ totals);}/* expand: $ cmdArr Array ([0] => Array ([path] =>/var/www/html/company/pcntl/run. php [pid] => 0 [total] => 3) [1] => Array ([path] =>/var/www/html/company/pcntl/run. php [pid] => 1 [total] => 3) [2] => Array ([Path] =>/var/www/html/company/pcntl/run. php [pid] => 2 [total] => 3) */pcntl_signal (SIGCHLD, SIG_IGN); // if the parent process does not care when the child process ends, after the child process ends, the kernel will recycle it. Foreach ($ cmdArr as $ cmd) {$ pid = pcntl_fork (); // create a sub-process // both the parent process and the sub-process will execute the following code if ($ pid =-1) {// error handling:-1 is returned when the sub-process fails to be created. die ('could not fork');} else if ($ pid) {// The parent process will get the sub-process number, so here is the logic of the parent process. // if you do not need to block the process and want to exit the sub-process, you can comment out the pcntl_wait ($ status) statement or write it: pcntl_wait ($ status, WNOHANG); // wait for the sub-process to interrupt and prevent the sub-process from becoming a zombie process.} Else {// The $ pid obtained by the sub-process is 0, so here is the logic of sub-process execution. $ Path = $ cmd ["path"]; $ pid = $ cmd ['pid ']; $ total = $ cmd ['Total']; echo exec ("/usr/bin/php {$ path} {$ pid} {$ total }"). "\ n"; exit (0) ;}}?>
The real multi-process running mode of PHP is suitable for data collection, mass mailing, data source update, and tcp server.
PHP has a set of process control functions (enable-pcntl and posix extensions are required during compilation ), this allows php to create sub-processes, execute programs using exec functions, and process signals in * nix systems like c. PCNTL uses ticks as the signal processing mechanism (signal handle callback mechanism) to minimize the load for processing asynchronous events. What is ticks? Tick is an event that occurs when the interpreter executes N low-level statements in a code segment. This code segment needs to be specified through declare.
Common PCNTL Functions
1. pcntl_alarm (int $ seconds)
Sets a counter for sending SIGALRM signals after $ seconds.
2. pcntl_signal (int $ signo, callback $ handler [, bool $ restart_syscils])
Set a callback function for $ signo to process the signal. The following is an example of how to send a SIGALRM signal every five seconds, which is obtained by the signal_handler function, and then print "Caught SIGALRM:
<?phpdeclare(ticks = 1); function signal_handler($signal) { print "Caught SIGALRM\n"; pcntl_alarm(5);} pcntl_signal(SIGALRM, "signal_handler", true);pcntl_alarm(5); for(;;) {} ?>
3. pcntl_exec (string $ path [, array $ args [, array $ envs])
Execute a specified program in the current process space, similar to the exec family function in c. The so-called current space, that is, the Code loaded into the specified program overwrites the space of the current process, and the process ends after the program is executed.
<? Php $ dir = '/home/shankka/'; $ cmd = 'LS'; $ option = '-l'; $ pathtobin ='/bin/ls '; $ arg = array ($ cmd, $ option, $ dir); pcntl_exec ($ pathtobin, $ arg); echo '2016'; // will not execute to this row?>
4. pcntl_fork (void)
Create a child process for the current process and run the parent process first. The PID of the child process is returned, which must be greater than zero. In the code of the parent process, you can use pcntl_wait (& $ status) to pause the parent process to know that its child process has returned values. Note: Blocking of the parent process also blocks the child process. However, the completion of the parent process does not affect the running of the child process.
After the parent process is run, the child process is run. The child process starts to execute (including this function) from the statement that executes pcntl_fork ), however, it returns zero (representing a sub-process ). It is better to have an exit statement in the code block of the child process, that is, the execution of the child process ends immediately after the execution. Otherwise, it starts executing some parts of the script again.
Note:
- It is best for a sub-process to have an exit statement to prevent unnecessary errors;
- It is best not to have other statements between pcntl_fork, for example:
<? Php $ pid = pcntl_fork (); // it is best not to have other statements here if ($ pid =-1) {die ('could not fork ');} else if ($ pid) {// we are the parentpcntl_wait ($ status); // Protect against Zombie children} else {// we are the child}?>
5. pcntl_wait (int & $ status [, int $ options])
Blocks the current process. Only one sub-process of the current process exits or receives a signal to end the current process. Use $ status to return the status code of the sub-process, and specify the second parameter to indicate whether the call is blocked:
If the call is blocked, the return value of the function is the pid of the sub-process. If no sub-process is returned, the return value is-1;
A function can be called in non-blocking mode. A function can return 0 when a child process is running but has not ended.
6. pcntl_waitpid (int $ pid, int & $ status [, int $ options])
The difference between the function and pcntl_wait is that waitpid is a child process waiting for the specified pid. When the pid is-1, pcntl_waitpid is the same as pcntl_wait. The $ status of the sub-process is saved in the pcntl_wait and pcntl_waitpid functions. This parameter can be used for functions such as pending, running, running, pcntl_wexitstatus, closing, pcntl_wstopsig, and pcntl_waitpid.
For example:
<?php$pid = pcntl_fork();if($pid) { pcntl_wait($status); $id = getmypid(); echo "parent process,pid {$id}, child pid {$pid}\n";}else{ $id = getmypid(); echo "child process,pid {$id}\n"; sleep(2);}?>
The child process stops running after two seconds after the child process is output, and the parent process is blocked until the child process exits.
7. pcntl_getpriority ([int $ pid [, int $ process_identifier])
Get the priority of the process, that is, the nice value. The default value is 0. In my testing environment linux (CentOS release 5.2 (Final), the priority is-20 to 19, -20 is the highest priority, and 19 is the lowest. (From-20 to 20 in the Manual ).
8. pcntl_setpriority (int $ priority [, int $ pid [, int $ process_identifier])
Set the priority of a process.
9. posix_kill
Sends signals to processes.
10. pcntl_singal
Callback function used to set signals
When a parent process exits, how does the child process know that the parent process exits?
When the parent process exits, the child process can generally use the following two simple methods to know that the parent process has exited the message:
When the parent process exits, there will be an INIT process to adopt this child process. The process of this INIT process is 1, so the child process can use getppid () to obtain the pid of the current parent process. If the returned value is 1, it indicates that the parent process has changed to the INIT process, then the original process has been released.
Use the kill function to send an empty signal (kill (pid, 0) to the original parent process )). Use this method to check the existence of a process without actually sending a signal. Therefore, if this function returns-1, the parent process has exited.
In addition to the above two methods, there are also some complicated implementation methods, such as establishing pipelines or Sockets for constant monitoring.
Example of PHP multi-process data collection
<? Php/*** Project: Signfork: php multi-threaded library * File: Signfork. class. php */class Signfork {/*** sets the sub-process communication file directory * @ var string */private $ tmp_path = '/tmp /'; /*** main startup method of the Signfork engine * 1. Determine the $ arg type. When the type is array, the value is passed to each sub-process. When the type is numeric, number of processes to be created. * @ param object $ obj: Execution object * @ param string | array $ arg is used for parameters executed by the _ fork method in the object. * For example, $ arg is automatically decomposed: $ obj->__ fork ($ arg [0]), $ obj->__ fork ($ arg [1])... * @ return array returns array (sub-process sequence => sub-process execution result); */public Function run ($ obj, $ arg = 1) {if (! Method_exists ($ obj, '_ fork') {exit ("Method '_ fork' not found! ");} If (is_array ($ arg) {$ I = 0; foreach ($ arg as $ key => $ val) {$ spawns [$ I] = $ key; $ I ++; $ this-> spawn ($ obj, $ key, $ val );} $ spawns ['Total'] = $ I;} elseif ($ spawns = intval ($ arg) {for ($ I = 0; $ I <$ spawns; $ I ++) {$ this-> spawn ($ obj, $ I) ;}} else {exit ('bad argument! ');} If ($ I> 1000) exit ('too your spawns! '); Return $ this-> request ($ spawns);}/*** Signfork Main Process Control Method * 1. $ tmpfile checks whether the sub-process file exists, if the sub-process exists, the sub-process is executed and the content * 2 and $ data is read to collect the sub-process running results and data, it is used to finally return * 3. Delete the sub-process file * 4. Round-Robin for 0.03 seconds until all sub-processes are completed, clean sub-process resources * @ param string | array $ arg is used to correspond to the ID of each sub-process * @ return array returns array ([sub-process sequence] => [sub-process execution result]); */private function request ($ spawns) {$ data = array (); $ I = is_array ($ spawns )? $ Spawns ['Total']: $ spawns; for ($ ids = 0; $ ids <$ I; $ ids ++) {while (! ($ Cid = pcntl_waitpid (-1, $ status, WNOHANG) usleep (30000); $ tmpfile = $ this-> tmp_path. 'sfpid _'. $ cid; $ data [$ spawns ['Total']? $ Spawns [$ ids]: $ ids] = file_get_contents ($ tmpfile); unlink ($ tmpfile);} return $ data ;} /*** Signfork sub-process execution Method * 1. pcntl_fork generates sub-process * 2. file_put_contents splits '$ obj->__ fork ($ val) 'execution result is saved to the text named by the specific sequence * 3. posix_kill kills the current process * @ param object $ obj the object to be executed * @ param object $ I the sequence ID of the child process, in this way, the corresponding sub-process data * @ param object $ param is used to input the $ obj method '_ fork' execution parameter */private function spawn ($ obj, $ I, $ param = null) {if (pcntl_fork () === 0) {$ ci D = getmypid (); file_put_contents ($ this-> tmp_path. 'sfpid _'. $ cid, $ obj->__ fork ($ param); posix_kill ($ cid, SIGTERM); exit ;}}?>
The child process (usually a zombie process) generated by php after pcntl_fork () must be released by the pcntl_waitpid () function. However, pcntl_waitpid () may not release the processes that are currently running, or the zombie processes generated in the past (not released), or zombie processes of other visitors during concurrency. However, posix_kill ($ cid, SIGTERM) can be used to kill a child process when it ends.
The child process automatically copies the variables in the parent process space.
PHP multi-process programming example 2
<? Php //..... // you need to install the php extension of pcntl and load it if (function_exists ("pcntl_fork") {// generate the sub-process $ pid = pcntl_fork (); if ($ pid =-1) {die ('could not fork');} else {if ($ pid) {$ status = 0; // blocks the parent process, it is not suitable for scripts that require long running until the child process ends. You can use pcntl_wait ($ status, 0) to implement non-blocking pcntl_wait ($ status ); // parent proc code exit;} else {// child proc code // end the current sub-process to prevent the generation of zombie process if (function_exists ("posix_kill ")) {posix_kill (getmypid (), SIGTERM);} else {system ('Kill-9 '. getmypid ();} exit ;}} else {// the code for multi-process processing is not supported here} // ......?> If you do not need to block the process and want to exit the sub-process, you can comment out the pcntl_wait ($ status) statement or write it as: <? Phppcntl_wait ($ status, 1); // or pcntl_wait ($ status, WNOHANG);?>
In the above Code, if the parent process exits (exit using the exit function or redirect), the child process will become a zombie process (which will be handed over to the init process for control) and the Child process will not be executed.
A zombie process is a parent process that has exited and is not accepted by the dead process. It becomes a zombie process. Any process changes to a zombie process (used to save the state and other information of the Process) before exiting (using exit to exit), and is then taken over by the init process. If the zombie process is not recycled in time, it will occupy an entry in the system. If there are too many zombie processes, the system will not have an available entry in the process, therefore, other programs cannot be run.
There are several methods to prevent botnets:
1. The parent process waits for the child process to end through functions such as wait and waitpid, and then executes the code in the parent process. This causes the parent process to stop. The above code is implemented in this way, but in the WEB environment, it is not suitable for the situation where the process needs to run for a long time (resulting in timeout ).
Use the wait and waitpid methods to enable the parent process to automatically recycle its zombie sub-processes (based on the returned status of the sub-processes). waitpid is used to control the sub-processes. wait is used for all sub-processes.
2. If the parent process is very busy, you can use the signal function to install handler for SIGCHLD, because after the child process ends, the parent process will receive this signal and can call wait recycle in handler.
3. if the parent process does not care when the child process ends, you can use SIGCHLD (SIGCHLD, SIG_IGN) to notify the kernel that you are not interested in the end of the Child process. After the child process ends, the kernel recycles and does not send signals to the parent process. For example:
<?phppcntl_signal(SIGCHLD, SIG_IGN);$pid = pcntl_fork();//....code?>
4. another technique is to fork two times. The parent process fork is a sub-process, and then continue to work. The sub-process fork is a sub-process and then exits, so the sun process is taken over by init, after the sun process ends, init will recycle it. However, the sub-process must be recycled by itself. The following is an example:
#include "apue.h"#include
int main(void){pid_t pid; if ((pid = fork()) < 0){ err_sys("fork error");} else if (pid == 0){ /**//* first child */ if ((pid = fork()) < 0){ err_sys("fork error"); }elseif(pid > 0){ exit(0); /**//* parent from second fork == first child */ } /** * We're the second child; our parent becomes init as soon * as our real parent calls exit() in the statement above. * Here's where we'd continue executing, knowing that when * we're done, init will reap our status. */ sleep(2); printf("second child, parent pid = %d ", getppid()); exit(0);} if (waitpid(pid, NULL, 0) != pid) /**//* wait for first child */ err_sys("waitpid error"); /** * We're the parent (the original process); we continue executing, * knowing that we're not the parent of the second child. */ exit(0);}
In the fork ()/execve () process, assume that the parent process still exists at the end of the Child process, and the parent process fork () has not installed the SIGCHLD signal processing function to call waitpid () when the sub-process ends and the signal is not explicitly ignored, the sub-process becomes a zombie and cannot end normally. In this case, even the root identity kill-9 cannot kill the zombie process. The remedy is to kill the parent process of the zombie process (the parent process of the zombie process must exist). The zombie process becomes an "orphan process" and passes the process init to process 1, init regularly calls wait to clear zombie sub-processes that have exited the parent process.
Therefore, the preceding example can be changed:
<? Php //..... // install the php extension of pcntl and load it if (function_exists ("pcntl_fork") {// generate the first sub-process $ pid = pcntl_fork (); // $ pid is the generated sub-process idif ($ pid =-1) {// sub-process fork failed die ('could not fork ');} else {if ($ pid) {// parent Process code sleep (5); // wait for 5 seconds to exit (0 ); // or $ this-> _ redirect ('/');} else {// The first sub-process code // generate the sun process if ($ gpid = pcntl_fork ()) <0) {// $ gpid indicates the sun process id generated. // sun process fails to generate die ('could not fork');} elseif ($ gpid> 0) {// The first child process code, that is, the parent process of the Sun process $ st Atus = 0; $ status = pcntl_wait ($ status); // block the sub-process and return the exit status of the sun process. It is used to check whether the sub-process Exits normally if ($ status! = 0) file_put_content ('filename', 'Sun process exited abnormally '); // obtain the parent process id // $ ppid = posix_getppid (); // If $ ppid is 1, the parent process has changed to the init process. The original parent process has exited. // obtain the sub-process id: posix_getpid () or getmypid () or $ pid // kill the sub-process returned by fork // posix_kill (getmypid (), SIGTERM); exit (0 );} else {// that is, $ gpid = 0 // sun Process code //.... // terminate the sun process (that is, the current process) to prevent the generation of the zombie process if (function_exists ('posix _ kill ') {posix_kill (getmypid (), SIGTERM );} else {system ('Kill-9 '. getmypid ();} exit (0) ;}}} else {// the code for multi-process processing is not supported here} // ......?>
How to generate zombie Processes
When a process calls the exit command to end its own life, it is not actually destroyed, but it leaves a data structure called Zombie (the system calls exit, it is used to exit a process, but it is only limited to converting a normal process into a zombie process and cannot completely destroy it ). In the status of a Linux Process, a zombie process is a very special one. It has abandoned almost all the memory space, no executable code, and cannot be scheduled, only one location is retained in the process list, and information such as the exit status of the process is recorded for collection by other processes. In addition, zombie processes no longer occupy any memory space. It requires its parent process to collect dead parts for it. If its parent process does not have the SIGCHLD signal processing function installed, it calls wait or waitpid () to wait until the child process ends, if the signal is not explicitly ignored, it will remain in zombie state. If the parent process ends, the init process will automatically take over the child process and send a zombie to it, it can still be cleared. However, if the parent process is a loop and does not end, the child process will remain zombie, which is why many zombie processes sometimes exist in the system.
Any sub-process (except init) does not disappear immediately after exit (), but leaves a data structure called Zombie, waiting for processing by the parent process. This is the stage that every sub-process will go through at the end. If the parent process does not have time to process the child process after exit (), run the ps command to check that the child process is in the "Z" status ". If the parent process can be processed in a timely manner, you may not be able to see the zombie state of the child process using the ps command, but this does not mean that the child process does not pass through the zombie state.
If the parent process exits before the child process ends, the child process will be taken over by init. Init processes child processes in zombie state as the parent process.
In addition, you can write a PHP file and run it in the form of a later server, for example:
<? Php // Action Code public function createAction (){//.... // replace args with insertLargeData. php parameters, which are separated by spaces between system ('php-f insertLargeData. php '. 'args '. '&'); $ this-> redirect ('/');}?>
Then, perform database operations in the insertLargeData. php file. You can also use cronjob + php to process large data volumes.
If you run the php Command on the terminal, after the terminal is closed, the command you just executed will also be forced to close. If you want to disable the command, you can use the nohup command:
<? Php // Action Code public function createAction (){//.... // replace args with insertLargeData. php parameters, which are separated by spaces between system ('nohup php-f insertLargeData. php '. 'args '. '&'); $ this-> redirect ('/');}?>
You can also use the screen command instead of the nohup command.