PHP multi-process programming example

Source: Internet
Author: User
Tags php file posix sleep

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:
Declare (ticks = 1 );

Function signal_handler ($ signal ){
Print "Caught SIGALRMn ";
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.
$ Dir = '/home/shankka /';
$ Cmd = 'Ls ';
$ Option = '-l ';
$ Pathtobin = '/bin/LS ';

$ Arg = array ($ cmd, $ option, $ dir );

Pcntl_exec ($ pathtobin, $ arg );
Echo '200'; // The row is not executed.
?>

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:
1. It is recommended that a sub-process have an exit statement to prevent unnecessary errors;
2. It is best not to have other statements between pcntl_fork, for example:
$ Pid = pcntl_fork ();
// It is better not to have other statements here
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
}

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:
1. If it is called in blocking mode, the return value of the function is the pid of the sub-process. If no sub-process is returned, the return value is-1;
2. For non-blocking calls, the 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:

$ 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:

1. 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.
2. 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

/**
* Project: Signfork: php multi-thread library
* File: Signfork. class. php
*/

Class Signfork {
/**
* Set the Directory of the sub-process communication file
* @ Var string
*/
Private $ tmp_path = '/tmp /';

/**
* Signfork engine master startup method
* 1. Determine the $ arg type. When the type is array, the value is passed to each sub-process. When the type is numeric, it indicates the number of processes to be created.
* @ Param object $ obj: execution object
* @ Param string | array $ arg is used for the parameter executed by the _ fork method in the object.
* For example, $ arg is automatically decomposed into: $ 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 then spawns! ');
Return $ this-> request ($ spawns );
}

/**
* Signfork master process control method
* 1. $ tmpfile: determines whether a sub-process file exists. If yes, the sub-process is executed and the content is read.
* 2. $ data collects the running results and data of sub-processes and is used for final return.
* 3. Delete a sub-process file
* 4. Round-robin for 0.03 seconds until all sub-processes are executed and sub-process resources are cleared.
* @ Param string | array $ arg indicates the ID of each sub-process.
* @ Return array returns array ([subprocess sequence] => [subprocess 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-processes.
* 2. file_put_contents saves the execution result of '$ obj->__ fork ($ val)' to the text named in the specific sequence.
* 3. posix_kill: kill the current process.
* @ Param object $ obj: object to be executed
* @ Param object $ I sequence ID of the sub-process to return data of each sub-process
* @ Param object $ param is used to input the $ obj method '_ fork' execution parameter of the object.
*/
Private function spawn ($ obj, $ I, $ param = null ){
If (pcntl_fork () === 0 ){
$ Cid = 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

//.....
// Install the php extension of pcntl and load it
If (function_exists ("pcntl_fork ")){
// Generate sub-process
$ Pid = pcntl_fork ();
If ($ pid =-1 ){
Die ('could not fork ');
} Else {
If ($ pid ){
$ Status = 0;
// Block the parent process until the child process ends. It is not suitable for scripts that require long running. 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 zombie process generation
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:

Pcntl_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:

Pcntl_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 re the second child; our parent becomes init as soon
* As our real parent callexit () 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 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:

//.....
// Install the php extension of pcntl and load it
If (function_exists ("pcntl_fork ")){
// Generate the first child process
$ Pid = pcntl_fork (); // $ pid indicates the id of the child process generated.
If ($ pid =-1 ){
// The sub-process fork fails.
Die ('could not fork ');
} Else {
If ($ pid ){
// Parent process code
Sleep (5); // wait for 5 seconds
Exit (0); // or $ this-> _ redirect ('/');
} Else {
// Code of the first sub-process
// Generate the Sun process
If ($ gpid = pcntl_fork () <0) {// $ gpid indicates the id of the created sun process.
// Sun process failed
Die ('could not fork ');
} Elseif ($ gpid> 0 ){
// The first child process code, that is, the parent process of the Sun process
$ Status = 0;
$ Status = pcntl_wait ($ status); // blocks the sub-process and returns the exit status of the Sun process 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 and the original parent process has exited.
// Obtain the sub-process id: posix_getpid (), getmypid (), or the variable $ pid returned by fork.
// Kill the sub-process
// 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 zombie processes
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:

// Action code
Public function createAction (){
//....
// Replace args with the parameter to be passed to insertLargeData. php. The parameters are separated by spaces.
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:

// Action code
Public function createAction (){
//....
// Replace args with the parameter to be passed to insertLargeData. php. The parameters are separated by spaces.
System ('nohup php-f insertLargeData. Php'. 'Args '.'&');
$ This-> redirect ('/');
}
?>

You can also use the screen command instead of the nohup command.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.