Single process management Process

Since our service application has provided a multi-process running mode, can we directly operate the process ourselves? The answer is of course no problem. What we are going to see today is how to operate a single process in Swoole, which is actually learning the use of a Process object.

one example

To use Swoole's multi-process capabilities is as simple as instantiating the Process object directly.

for ($i = 0; $i < 2; $i++) {
    $process = new \Swoole\Process(function () {
        $t = rand(1020);
        echo 'Child Process #' . getmypid() . 'start and sleep ' . $t . 's', PHP_EOL;
        sleep($t);
        echo 'Child Process #' . getmypid() . ' exit', PHP_EOL;
    });
    $process->start();
}

In this way, we have created two subprocesses, and in the callback function, we can get the ID information of the process. In this test code, let the two subprocesses rest for a few seconds while they work. When running this code, if you use ps to view the process, you will generally only see two processes, and their parent process ID is 1. In fact, our current executing process has ended, and the remaining two processes that are blocked by sleep() have been taken over by the system and become two main processes due to the exit of the main process. But anyway, at least we have started multiple processes.

Of course, the current effect is not what we want, after all, what we need is a main process to run, and multiple child processes to perform task processing. Don't worry, let's keep watching.

Zombie processes and recycling

Since the parent-child process is an asynchronous process, just like the above example, the parent process exits and the child process is still executing. But we usually start a suspended parent process when applying multiple child processes, and then work through the child process to achieve parallel processing capabilities. In fact, this is like the feeling of our Manager and Worker. However, the child process ends without the parent's knowledge, they are active asynchronously. At this time, if there is no recycling mechanism, the previously created child process will become a zombie process.

For this knowledge, you can look at the operating system-related content, especially Unix-related. Let's simulate it first and add the following line to the code to keep the main program running.

while(1) sleep(100);

Then run the code again, wait for the child process to finish running, and check the result through ps.

picture

At the same time, using the top command, we can also see two zombie processes appear.

picture

Zombie processes appear, so we also need a way to deal with these processes. Most programs provide the wait() function, and Swoole is no exception. However, it will block the main process, as can be seen from the name, its role is to wait for the completion of the execution of the child process.

for ($n = 2; $n--;) {
    $status = \Swoole\Process::wait(true);
    echo "Recycled #{$status['pid']}, code={$status['code']}, signal={$status['signal']}" . PHP_EOL;
}
echo 'Parent #' . getmypid() . ' exit' . PHP_EOL;
//while(1) sleep(100);

Even if we remove the hang loop below, the final content will not be output until both child processes have finished executing. The wait() method has one parameter, which means that if it is set to false, it will wait for recycling in a non-blocking state. But at this time, it is useless for you to set it to false, and you must use another way to achieve non-blocking recycling.

Swoole\Process::signal(SIGCHLD, function ($sig) {
    //必须为false,非阻塞模式
    while ($ret = Swoole\Process::wait(false)) {
        echo "PID={$ret['pid']}\n";
    }
});
echo 'Parent #' . getmypid() . ' exit' . PHP_EOL;
//while(1) sleep(100);
Swoole\Timer::tick(2000function () {});

The signal() method is used to set an asynchronous listener, and SIGCHLD means that when a process terminates, this signal will be sent to the main process. After the main process listens to this information, we call the wait() method to recycle the child process, thereby realizing a non-blocking recycling process. At the same time, you can't use while + sleep() to suspend the program, because while is blocked synchronously, we need to use a timer method in Swoole to achieve asynchronous suspension. The output of your code should now look like this.

➜  source git:(main) ✗ php 3.3单进程管理Process.php
Parent #43188 exit
Child Process #43189start and sleep 19s
Child Process #43190start and sleep 15s
Child Process #43190 exit
Array
(
    [pid] => 43190
    [code] => 0
    [signal] => 0
)
PID=43190
Child Process #43189 exit
Array
(
    [pid] => 43189
    [code] => 0
    [signal] => 0
)
PID=43189

The line of text that Parent exits is output first, which actually means that we can do other things in the main process.

process relationship

For child processes, they inherit the parent's memory and file handles. Let's see if that is the case.

$obj = new stdClass();
$obj->parent = 1;
var_dump($obj);

(new \Swoole\Process(function () use ($obj) {
    $obj->child1 = 1;
    var_dump($obj);
}))->start();

(new \Swoole\Process(function () use ($obj) {
    $obj->child2 = 1;
    var_dump($obj);
}))->start();
// [root@localhost source]# php 3.3单进程管理Process.php
// object(stdClass)#1 (1) {
// ["parent"]=>
//   int(1)
// }
// object(stdClass)#1 (2) {
// ["parent"]=>
//   int(1)
//   ["child1"]=>
//   int(1)
// }
// object(stdClass)#1 (2) {
// ["parent"]=>
//   int(1)
//   ["child2"]=>
//   int(1)
// }

There are also two processes. You can see that in the child process, the printed object contains the attributes of the parent process. However, objects are not shared between the two child processes. It can also be seen from here that if you have a Redis connection or data connection or a file handle in the parent process, they can be used in the child process, they are the same connection object or handle.

Other methods of operation

With the main conceptual relevance stuff out of the way, let's finally look at some other interesting methods in the Process object.

Process callback function parameters

(new \Swoole\Process(function () {
    var_dump(func_get_args());
}))->start();
//  [root@localhost source]# php 3.3单进程管理Process.php
//  array(1) {
//      [0]=>
//    object(Swoole\Process)#1 (6) {
//    ["pipe"]=>
//      int(4)
//      ["msgQueueId"]=>
//      NULL
//      ["msgQueueKey"]=>
//      NULL
//      ["pid"]=>
//      int(1956)
//      ["id"]=>
//      NULL
//     ["callback":"Swoole\Process":private]=>
//      object(Closure)#2 (0) {
//      }
//    }
//  }

The callback function of the process has parameters, that is, the Process object itself of the process itself. You should know this, because we will use it later.

rename

Use the name() method to rename a process. What does renaming mean? Look down.

(new \Swoole\Process(function (\Swoole\Process $process) {
    $process->name('Child Test1');
    sleep(10);
}))->start();

(new \Swoole\Process(function (\Swoole\Process $process) {
    $process->name('Child Test2');
    sleep(10);
}))->start();

swoole_set_process_name("Parent Test");

// [root@localhost ~]# ps -ef | grep Test
// root      1942  1413  0 21:45 pts/0    00:00:00 Parent Test
// root      1943  1942  0 21:45 pts/0    00:00:00 Child Test1
// root      1944  1942  0 21:45 pts/0    00:00:00 Child Test2

Interesting, the name() method of the Process object is an alias for the global function swoole_set_process_name(). So we use the swoole_set_process_name() demonstration in the main process. If it is the main process, the rename method should be used after start(). If it is a child process, it should be used in the callback function of the child process. The following is invalid.

$process = new \Swoole\Process(function () {
   sleep(10);
});
$process->start();
$process->name('Child Test3');

Execute external program

// php -r "echo 1+1;"
(new \Swoole\Process(function (\Swoole\Process $process) {
    echo $process->exec('/usr/local/php/bin/php', ['-r''echo 1+1;']);
    // 2
}))->start();

Through the exec() function, we can directly call other programs outside the system. Of course, the premise is that you have permission. Here, we directly use the php command line to perform a simple calculation, which is actually to execute the command line statement in the above comment.

daemon

Swoole\Process::daemon();

I don’t need to say more about the concept of a daemon process. The most obvious thing is that if our process is not a daemon process, the interface will always remain in the state of the program running when the command line is running. After the daemon is turned on, the process is transferred to the background to run, just like the role of nohup.

exit and kill

(new \Swoole\Process(function(\Swoole\Process $pro){
    $pro->exit(9);
    sleep(20);
}))->start();
//  Array
//  (
//      [pid] => 2086
//      [code] => 9
//      [signal] => 0
//  )
//  PID=2086

$process = new \Swoole\Process(function(\Swoole\Process $pro){
    sleep(20);
});
$process->start();
Swoole\Process::kill($process->pid);
//  Array
//  (
//      [pid] => 2087
//      [code] => 0
//      [signal] => 15
//  )
//  PID=2087

Swoole\Process::signal(SIGCHLD, function ($sig) {
    //必须为false,非阻塞模式
    while ($ret = Swoole\Process::wait(false)) {
        print_r($ret);
        echo "PID={$ret['pid']}\n";
    }
});

The child process can use exit() to exit by itself, and the main process can use kill to terminate the specified child process. The signal codes of these two functions can be returned in wait().

Bind CPU and process priority settings

Swoole\Process::setAffinity([0,1]);

$process = new \Swoole\Process(function(\Swoole\Process $pro){
    echo $pro->getPriority(PRIO_PROCESS), PHP_EOL;
});
$process->start();
$process->setPriority(PRIO_PROCESS, -10);

The setAffinity() method sets the affinity of the CPU, that is, the process can be bound to a specific CPU core. Its parameters are data starting from 0. For example, the range that can be used by a 4-core CPU is 0-3.

The setPriority() function is used to set the priority of the process, the level is a number from -20 to 20, the smaller the higher the level. And getPriority() is to get the priority of the current process.

These two method functions can also be seen in the Nginx configuration file.

Summarize

Today there are more people, we mainly start from the Process object, and deeply study how to operate the process in Swoole. And I learned about the problems of zombie processes and inheriting the parent process, and finally looked at some operation methods of the Process object. I believe that through today's study, we can all have a better understanding of the process.

In addition to the content learned today, Process has several methods, which we will learn together in the next article related to inter-process communication.

Test code:

https://github.com/zhangyue0503/swoole/blob/main/3.Swoole%E8%BF%9B%E7%A8%8B/source/3.3%E5%8D%95%E8%BF%9B%E7% A8%8B%E7%AE%A1%E7%90%86Process.php

Reference documentation:

https://wiki.swoole.com/#/process/process