DEV Community

Violet Alexander
Violet Alexander

Posted on

Deep understanding of the Workerman daemon principle in PHP high-performance framework

In our daily programming, there are common examples such as PHP think Tasks that need to be executed continuously, such as PHP artists… and PHP yii…, will be mounted to the background through nohup to maintain a long-term running state. Similarly, in Workerman, a command similar to php index.php start is used to start processes, but the difference is that it does not require the use of nohup to mount and run in the background. Some friends may wonder how it was achieved? To address the concerns of our friends, today we will focus on in-depth analysis of the implementation principle of the Workerman daemon process.

Let’s first understand some process related knowledge:

Parent process: The parent process is the process that generates other processes. When a process creates another process, the creator is called the parent process, and the created process becomes the child process. The parent process can identify the child process it creates through a process identifier (PID).
Child process: A child process is a new process created by the parent process. The child process inherits some properties of the parent process, such as environment variables, file descriptors, etc. A child process runs independently of its parent process, can execute its own code, and has its own resources and memory space.
Process group: A process group is a collection of associated processes. Each process group has a unique Process Group ID (PGID) used to identify that process group. A process group is typically created by a parent process and includes all child processes that have the same session ID (SID) as the parent process.
Session: A session is a collection of associated processes that typically start when a user logs into the system and end when the user logs out or closes the terminal. Processes in a session share the same control terminal. Each session has a unique session ID (SID) used to identify the session. A session typically contains one or more process groups, with the first process group becoming the main process group of the session.

These concepts are commonly known as the Eight legged Essay and have never been easy to understand. Let’s take a look at an example. After executing the command ‘php index. php’, process 61052 was generated. The parent process of this process is Bash process 8243, so there is no need to worry about it here. Then, a child process 61053 was created through Fork, and its parent process is 61052. These two processes have a common process group 61052 and session 8243. Calling the posix_setsid function will open a new process group 61053 and a new session 61053 for the child process 61053, where the session can be understood as a new command window terminal. Finally, child process 61053 created child process 61054 through Fork, and process 61053 was upgraded to the parent process. The reason for Fork again is to avoid being associated with terminal controlled processes. This process 61052 was created in terminal mode, and since then, process 61054 has formed a daemon process.

[ manongsen@root  phpwork]$ php index.php
[parent] Process ID: 61052, Parent Process ID: 8243, Process Group ID: 61052, Session ID: 8243
[parent1] Process ID: 61052, parent process ID: 8243, process group ID: 61052, session ID: 8243 exited the process
[child1] Process ID: 61053, parent process ID: 61052, process group ID: 61052, session ID: 8243
[child1] Process ID: 61053, parent process ID: 61052, process group ID: 61053, session ID: 61053
[parent2] Process ID: 61053, parent process ID: 61052, process group ID: 61053, session ID: 61053 exited the process
[child2] Process ID: 61054, parent process ID: 61053, process group ID: 61053, session ID: 61053. This process is retained

[ manongsen@root  phpwork]$ ps aux | grep index.php
Root 66064 0.0 0.0 408105040 1472 s080 S+10:00 PM 00:00 grep index.php
Root 61054 0.0 438073488 280?? S 10:00 PM 00:00 PHP index.php
Enter fullscreen mode Exit fullscreen mode

The process information mentioned above is generated by the execution of this code. If you read this code and are careful, you will find why the posix_setsid function is not called before the first Fork, but before the second Fork. In this way, you don’t need to use Fork twice? The reason is that the leader process cannot create a session, and process group ID 61052 is the same as process ID 61052, which means the current process is the leader process. Therefore, a child process is needed to create a new session, which requires special attention.

<? php

function echoMsg($prefix, $suffix="") {
//Process ID
$pid = getmypid(); 
//Process group ID
$pgid = posix_getpgid($pid);
//Session ID
$sid = posix_getsid($pid); 
//Parent process ID
$ppid = posix_getppid();

Echo "[{$prefix}] Process ID: {$pid}, Parent Process ID: {$ppid}, Process Group ID: {$pgid}, Session ID: {$sd} {$Suffix}". PHP_EOL;
}

//[parent] Process ID: 61052, Parent Process ID: 8243, Process Group ID: 61052, Session ID: 8243
echoMsg("parent");

//The first Fork process
$pid = pcntl_fork();
if ( $pid < 0 ) {
exit('fork error');
} else if( $pid > 0 ) {
//[parent1] Process ID: 61052, parent process ID: 8243, process group ID: 61052, session ID: 8243 exited the process
Echoes ("parent1", "exited the process");
exit;
}

//The child process ID created is 61053, but the process group, session, and parent process are still the same
//[child1] Process ID: 61053, parent process ID: 61052, process group ID: 61052, session ID: 8243
echoMsg("child1");

//Calling the posix_setsid function will create a new session and process group, and set the process group ID and session ID to that process ID
if (-1 === \posix_setsid()) {
throw new Exception("Setsid fail");
}

//Now you will find that both the process group ID and session ID have changed to 61053, which is equivalent to starting a session window similar to a Linux terminal
//[child1] Process ID: 61053, parent process ID: 61052, process group ID: 61053, session ID: 61053
echoMsg("child1");

//Second Fork Process
//The reason for requiring a secondary Fork process here is to avoid being associated with terminal controlled processes. This process 61052 was created in terminal mode
//Need to detach from process 61052 to ensure the stability of the daemon process
$pid = pcntl_fork();
if ( $pid  < 0 ){
exit('fork error');
} else if( $pid > 0 ) {
//[parent2] Process ID: 61053, parent process ID: 61052, process group ID: 61053, session ID: 61053 exited the process
Echoes ("parent2", "exited the process");
exit;
}

//At this point, the process has broken free from the control of the terminal process and formed a daemon process
//[child2] Process ID: 61054, parent process ID: 61053, process group ID: 61053, session ID: 61053. This process is retained
Echoes ("child2", "This process is reserved");

sleep(100);
Enter fullscreen mode Exit fullscreen mode

It’s best for friends who have time to execute the code and analyze it themselves, as there will be different rewards. Pretending that you have already practiced it here, let’s take a look at the static:: daemon () function in the runAll method at line 554 of Workerman’s Worker.php file. The implemented process logic is almost the same as the example above. However, the umask function is also used here, whose main function is to grant corresponding permissions to the files or directories created by the process, ensuring that there is permission to operate on the files or directories.

// workerman/Worker.php:554
/**
* Run all worker instances.
*Run process
* @return void
*/
public static function runAll()
{
static::checkSapiEnv();
static::init();
static::parseCommand();
static::lock();
//Create a process and form a daemon process
static::daemonize();
static::initWorkers();
static::installSignal();
static::saveMasterPid();
static::lock(\LOCK_UN);
static::displayUI();
static::forkWorkers();
static::resetStd();
static::monitorWorkers();
}

// workerman/Worker.php:1262
/**
* Run as daemon mode.
*Run in daemon mode
* @throws Exception
*/
protected static function daemonize()
{
//Determine whether it is already in a guarded state and whether the current system is in a Linux environment
if (! static::$daemonize || static::$_OS !== \OS_TYPE_LINUX) {
return;
}

//If umask is set to 0, the file permissions created by the current process will be 777, which has the highest permission
\umask(0);

//The first time creating a process
$pid = \pcntl_fork();
if (-1 === $pid) {
//Process creation failed
throw new Exception('Fork fail');
} elseif ($pid > 0) {
//Main process exits
exit(0);
}

//The child process continues to execute
//By calling the posix_setsid function, a process can detach from its parent process and transform into a daemon process
if (-1 === \posix_setsid()) {
throw new Exception("Setsid fail");
}

//The second creation process, in a System V based system, exits by forking the parent process again
//Ensure that the formed daemon process does not become the first session process and does not have control terminals
$pid = \pcntl_fork();
if (-1 === $pid) {
//Process creation failed
throw new Exception("Fork fail");
} elseif (0 !== $pid) {
//Main process exits
exit(0);
}

//The child process continues to execute
}
Enter fullscreen mode Exit fullscreen mode

The daemon process is also an important part of Workerman, which ensures the stability of the Workerman process. Unlike the commands we launch through nohup, which sometimes hang up in the background without anyone noticing, friends may have experienced this. Of course, there are also some open-source daemon management software on the market, such as supervisors, and some people use session terminals such as screen and tmux to implement them. In fact, there are various ways to implement daemon processes. Here, we only introduce an example of implementing daemon patterns in PHP to analyze the implementation principles of daemon processes in Workerman. We hope this content can be helpful to you.

If you want to experience the new version of PHP or switch PHP versions, welcome to use Servbay to deploy the PHP development environment. This is the product I am working on and I will maintain this tool for a long time.

Top comments (0)