Home  >  Article  >  Backend Development  >  PHP multi-process programming example description_PHP tutorial

PHP multi-process programming example description_PHP tutorial

WBOY
WBOYOriginal
2016-07-13 17:00:17686browse

Using PHP’s true multi-process operating mode, it is suitable for data collection, mass mailing, data source update, tcp server, etc.

PHP has a set of process control functions (-enable-pcntl and posix extensions are required when compiling), which allows PHP to create sub-processes, use the exec function to execute programs, and handle signals in *nix systems like C. PCNTL uses ticks as a signal handle callback mechanism, which can minimize the load when processing asynchronous events. What are ticks? Tick ​​is an event that occurs every time the interpreter executes N low-level statements in a code segment. This code segment needs to be specified through declare.

Commonly used PCNTL functions

1. pcntl_alarm (int $seconds)

Set a counter to send the SIGALRM signal after $seconds seconds
2. pcntl_signal ( int $signo , callback $handler [, bool $restart_syscalls ] )

Set a callback function for $signo to handle the signal. The following is an example of sending a SIGALRM signal every 5 seconds, getting it by the signal_handler function, and then printing a "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 the specified program in the current process space, similar to the exec family function in c. The so-called current space is the space where the code of the specified program is loaded and overwrites the current process. The process ends after executing the program.
$dir = '/home/shankka/';
$cmd = 'ls';
$option = '-l';
$pathtobin = '/bin/ls';

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

pcntl_exec($pathtobin, $arg);
echo '123'; //This line will not be executed
?>

4. pcntl_fork (void)

Create a child process for the current process, and run the parent process first. What is returned is the PID of the child process, which must be greater than zero. In the code of the parent process, you can use pcntl_wait (&$status) to pause the parent process until its child process has a return value. Note: Blocking of the parent process will also block the child process. However, the end of the parent process does not affect the operation of the child process.

After the parent process has finished running, it will then run the child process. At this time, the child process will start executing from the statement that executes pcntl_fork() (including this function), but at this time it returns zero (meaning that this is a child process ). It is best to have an exit statement in the code block of the child process, that is, it will end immediately after executing the child process. Otherwise it will start executing parts of the script over again.

Note two points:
​ 1. It is best for the child process to have an exit; statement to prevent unnecessary errors;
​​ 2. It is best not to have other statements between pcntl_fork, such as:
$pid = pcntl_fork();
//It’s best 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 ] )

Block the current process until a child process of the current process exits or receives a signal to end the current process. Use $status to return the status code of the child process, and you can specify a second parameter to indicate whether to call in a blocking state:
​​ 1. When called in blocking mode, the function return value is the pid of the child process. If there is no child process, the return value is -1;
​​ 2. Called in a non-blocking manner, the function can also return 0 when a child process is running but has not ended.
6. pcntl_waitpid ( int $pid , int &$status [, int $options ] )

The function is the same as pcntl_wait, the difference is that waitpid is the child process waiting for the specified pid. When pid is -1, pcntl_waitpid is the same as pcntl_wait. The status information of the child process is stored in $status in the pcntl_wait and pcntl_waitpid functions. This parameter can be used in functions such as pcntl_wifexited, pcntl_wifstopped, pcntl_wifsignaled, pcntl_wexitstatus, pcntl_wtermsig, 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 sleeps for 2 seconds before ending after outputting words such as child process, while the parent process blocks until the child process exits before continuing to run.
7. pcntl_getpriority ([ int $pid [, int $process_identifier ]] )

Get the priority of the process, that is, the nice value, which defaults to 0. In Linux in my test environment (CentOS release 5.2 (Final)), the priority is -20 to 19, -20 is the highest priority, 19 is the lowest. (-20 to 20 in the manual).
8. pcntl_setpriority ( int $priority [, int $pid [, int $process_identifier ]] )

Set the priority of the process.
9. posix_kill

You can send signals to the process
10. pcntl_singal

​​Callback function used to set the signal

When the parent process exits, how does the child process know the parent process's exit
When the parent process exits, the child process can generally learn that the parent process has exited through the following two relatively simple methods:

1. When the parent process exits, there will be an INIT process to adopt the child process. The process number of this INIT process is 1, so the child process can obtain the pid of the current parent process by using getppid(). If 1 is returned, it indicates that the parent process has become an INIT process, and the original process has been launched.
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. So, if this function returns -1 it means the parent process has exited.

In addition to the above two methods, there are also some more complex methods in implementation, such as establishing pipelines or sockets for real-time monitoring, etc.

Example of PHP multi-process data collection

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

class Signfork{
/**
* Set the directory where the sub-process communication file is located
* @var string
*/
private $tmp_path='/tmp/';

/**
* Signfork engine main startup method
* 1. Determine the $arg type. When the type is an array, the value is passed to each child process; when the type is a numeric type, it represents the number of processes to be created.
* @param object $obj execution object
* @param string|array $arg is used for the parameters executed by the __fork method in the object
* For example: $arg, automatically decomposed into: $obj->__fork($arg[0]), $obj->__fork($arg[1])…
* @return array Return array (subprocess sequence=>subprocess 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 many spawns!');
return $this->request($spawns);
}

/**
* Signfork main process control method
* 1. $tmpfile determines whether the sub-process file exists. If it exists, the sub-process will be executed and the content will be read
* 2. $data collects the running results and data of the sub-process and is used to finally return
* 3. Delete child process files
* 4. Poll once for 0.03 seconds until all sub-processes are executed and clean up sub-process resources
* @param string|array $arg is used to correspond to the ID of each child process
* @return array Return 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 child processes
* 2. file_put_contents stores the execution result of '$obj->__fork($val)' into a text named in a 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, so that data corresponding to each child process can be returned
* @param object $param is used to input object $obj method '__fork' execution parameters
*/
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 release resources by the pcntl_waitpid() function. However, what is released in pcntl_waitpid() is not necessarily the currently running process, it may be a zombie process generated in the past (not released); it may also be a zombie process of other visitors during concurrency. But you can use posix_kill($cid, SIGTERM) to kill the child process when it ends.

The child process will automatically copy the variables in the parent process space.

PHP multi-process programming example 2

//…..
//You need to install the php extension of pcntl and load it
if(function_exists(“pcntl_fork”)){
//Generate child 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 need to run for a long time. You can use pcntl_wait($status, 0) to achieve non-blocking
pcntl_wait($status);
// parent proc code
exit;
}else{
// child proc code
//End the current child process to prevent the generation of zombie processes
if(function_exists(“posix_kill”)){
posix_kill(getmypid(), SIGTERM);
}else{
system('kill -9'. getmypid());
}
exit;
}
}
}else{
//The code when multi-process processing is not supported is here
}
//…..
?>

If you do not need to block the process and want to get the exit status of the child process, you can comment out the pcntl_wait($status) statement, or write:

pcntl_wait($status, 1);
//or
pcntl_wait($status, WNOHANG);

In the above code, if the parent process exits (using the exit function to exit or redirect), it will cause the child process to become a zombie process (will be handed over to the init process for control), and the child process will no longer execute.

A zombie process means that the parent process has exited, and if no process accepts the process after it is dead, it becomes a zombie process. (zombie) process. Any process will become a zombie process (used to save the status and other information of the process) before exiting (using exit), and then the init process will take over. If the zombie process is not recycled in time, it will occupy a process table entry in the system. If there are too many zombie processes, the system will eventually have no usable process table entries, so it will no longer be able to run other programs.

There are several ways to prevent zombie processes:

1. The parent process uses functions such as wait and waitpid to wait for the child process to end before executing the code in the parent process, which will cause the parent process to hang. The above code is implemented in this way, but in a WEB environment, it is not suitable for situations where the child process needs to run for a long time (will cause timeout).

Use the wait and waitpid methods to make the parent process automatically recycle its zombie child processes (according to the return status of the child process). waitpid is used to temporarily control the specified child process, and wait is for all child processes.
2. If the parent process is very busy, you can use the signal function to install a handler for SIGCHLD, because after the child process ends, the parent process will receive the signal, and you can call wait in the handler to recycle
3. If the parent process does not care when the child process ends, it can use signal (SIGCHLD, SIG_IGN) to notify the kernel that it is not interested in the end of the child process. Then after the child process ends, the kernel will recycle it and no longer give it to the parent process. Send a signal, for example:

pcntl_signal(SIGCHLD, SIG_IGN);
$pid = pcntl_fork();
//….code

4. Another trick is to fork twice. The parent process forks a child process and then continues to work. The child process then forks a child process and then exits. Then the child process will be taken over by init. After the child process ends, init will Recycle. However, you have to do the recycling of the child process yourself. Here 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);
}

During the fork()/execve() process, it is assumed that the parent process still exists when the child process ends, and the parent process has not installed the SIGCHLD signal processing function before fork(), calls waitpid(), waits for the child process to end, and has not displayed If the signal is ignored, the child process becomes a zombie process and cannot end normally. At this time, even kill-9 as root 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 is adopted by process No. 1, init. Init will regularly call wait to recycle and clean up the zombie children whose parent processes have exited. process.

So, the above example can be changed to:

//…..
//You need to install the php extension of pcntl and load it
if(function_exists(“pcntl_fork”)){
//Generate the first child process
$pid = pcntl_fork(); //$pid is the generated child process id
if($pid == -1){
//Subprocess fork failed
die('could not fork');
}else{
if($pid){
//Parent process code
sleep(5); //Wait for 5 seconds
exit(0); //or $this->_redirect('/');
}else{
//First child process code
//Generate grandchild process
if(($gpid = pcntl_fork()) < 0){ ////$gpid is the generated grandson process id
//The grandson process failed to generate
die('could not fork');
}elseif($gpid > 0){
//The code of the first child process, which is the parent process of the grandchild process
$status = 0;
$status = pcntl_wait($status); //Block the child process and return the exit status of the grandchild process to check whether it exits normally
if($status ! = 0) file_put_content('filename', 'The process exited abnormally');
//Get the parent process id
//$ppid = posix_getppid(); //If $ppid is 1, it means that its parent process has become an init process and the original parent process has exited
//Get the child process id: posix_getpid() or getmypid() or the variable $pid
returned by fork //Kill the child process
//posix_kill(getmypid(), SIGTERM);
exit(0);
}else{ //i.e. $gpid == 0
//Sun process code
//….
//End the grandson process (i.e. 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 when multi-process processing is not supported is here
}
//…..
?>

How to generate a zombie process
When a process calls the exit command to end its life, it is not actually destroyed, but leaves a data structure called a zombie process (Zombie) (the system calls exit, its function is to make the process exit, But it is only limited to turning a normal process into a zombie process, and cannot completely destroy it). Among the status of Linux processes, a zombie process is a very special kind. It has given up almost all memory space, does not have any executable code, and cannot be scheduled. It only retains a position in the process list to record the exit of the process. Status and other information can be collected by other processes. In addition, the zombie process no longer occupies any memory space. It needs its parent process to collect its corpse. If its parent process does not install the SIGCHLD signal processing function and calls wait or waitpid() to wait for the child process to end, and does not explicitly ignore the signal, then it will remain in the zombie state. If At this time, the parent process ends, then the init process will automatically take over the child process and collect its corpse, and it can still be cleared. But if the parent process is a loop and will not end, then the child process will remain in a zombie state. This is why there are sometimes many zombie processes in the system.

Any child process (except init) does not disappear immediately after exit(), but leaves a data structure called a zombie process (Zombie), waiting for the parent process to process. This is the stage that every child process goes through at the end. If the child process does not have time to process after exit(), then you can use the ps command to see that the status of the child process is "Z". If the parent process can handle it in time, it may be too late to see the zombie state of the child process using the ps command, but this does not mean that the child process will not go through the zombie state.

If the parent process exits before the child process ends, the child process will be taken over by init. init will process the child process in zombie state as the parent process.

In addition, you can also write a php file and run it in the background, for example:

//Action code
public function createAction(){
//….
//Replace args with the parameters to be passed to insertLargeData.php, and separate the parameters with 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 amounts of data.

If you run the php command in the terminal, when the terminal is closed, the command just executed will also be forcibly closed. If you want it not to be affected by the terminal closing, you can use the nohup command to achieve this:

//Action code
public function createAction(){
//….
//Replace args with the parameters to be passed to insertLargeData.php, and separate the parameters with spaces
system('nohup php -f insertLargeData.php ' . ' args ' . '&');
$this->redirect('/');
}
?>

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

www.bkjia.comtruehttp: //www.bkjia.com/PHPjc/631261.htmlTechArticleUse PHP’s true multi-process operating mode, suitable for data collection, mass mailing, data source update, tcp server, etc. link. PHP has a set of process control functions (enable-pcn is required when compiling...
Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn