Location>code7788 >text

A Deeper Understanding of the Workerman Daemon, PHP's High-Performance Framework

Popularity:990 ℃/2024-08-12 09:09:50

Hi everyone, I'm Codemaster Pioneer.

A daemon, as the name suggests, is a process that can run in the background all the time, not hogging the user's session terminal, out of the terminal's control. I'm sure you're all familiar with this stuff, aren't you? If you can't even understand this concept, it is recommended to go back to the drawing board and take a look at the basics of Linux process management. In our daily programming, there are common similarphp think ...php artisan ...php yii ... and other commands to start tasks that need to be executed all the time are passed through thenohup mounted in the background to maintain a long-running state. Similarly, in Workerman, you can use thephp start command to start the process, but the difference is that it does not need to utilize thenohup Then it can be mounted to run in the background. Then some friends will be curious about how it is realized? In order to solve their doubts, we will focus on analyzing the implementation principle of the Workerman daemon.

Let's start with some process-related knowledge:

  • Parent process: The parent process is the process that spawns other processes. When a process creates another process, the creator is called the parent process and the created process becomes the child process. A parent process can identify the child process it creates by its process identifier (PID).
  • Child process: A child process is a new process created by the parent process. A child process inherits some of the attributes of the parent process, such as environment variables, file descriptors, and so on. A child process runs independently of the parent process, it 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) that identifies the group. A process group is usually created by a parent process and contains all child processes that have the same session ID (SID) as the parent process.
  • Session: A session is a collection of associated processes, usually starting from the time a user logs on to the system until the user logs off or closes the terminal session ends, processes in a session share the same control terminal. Each session has a unique session ID (SID) that identifies the session. A session usually contains one or more process groups, the first of which is the primary process group for the session.

These concepts are commonly known as octets and have never been well understood, so let's look at an example. The command is executedphp Then the process is created61052"The parent of this process is Bash process 8243, so don't worry about it", and then creates a child process via Fork.61053 and its parent process is61052The two processes have a common process group61052 peace conference8243Calling the posix_setsid function A call to the posix_setsid function will set the child process to the61053 Open a new process group61053 and new sessions61053, where the session can be interpreted as a new command window terminal. The final subprocess61053 Subprocesses created via Fork61054Process61053 The reason for Forking again is to avoid being associated with a terminal-controlled process that is61052 was created in terminal mode, since then the process61054 Then the daemon is formed.

[manongsen@root phpwork]$ php
[parent] Process ID: 61052, Parent ID: 8243, Group ID: 61052, Session ID: 8243
[parent1] process id: 61052, parent id: 8243, group id: 61052, session id: 8243 exited this process
[child1] Process ID: 61053, Parent ID: 61052, Group ID: 61052, Session ID: 8243
[child1] process id: 61053, parent id: 61052, group id: 61053, session id: 61053
[parent2] process id: 61053, parent id: 61052, group id: 61053, session id: 61053 exited the process
[child2] process id: 61054, parent id: 61053, group id: 61053, session id: 61053 Retained process

[manongsen@root phpwork]$ ps aux | grep
root 66064 0.0 0.0 408105040 1472 s080 S+ 10:00 PM 0:00.00 grep
root 61054 0.0 0.0 438073488 280 ????  S 10:00 PM 0:00.00 php

The above example of process information, it is this code run by the resulting. If you look at this code and careful friends, you will find why posix_setsid function is not placed in the first Fork before the call, but in the second Fork before the call, so that you do not have to Fork twice? The reason is that the group leader process is not able to create a session, process group ID61052 and process ID61052 The same "i.e., the current process is the leader process", so you need a child process to create a new session, which requires some special attention.

<?php

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

    echo "[{$prefix}] Process ID: {$pid}, Parent ID: {$ppid}, Process Group ID: {$pgid}, Session ID: {$sid} {$suffix}" . PHP_EOL;
}

// [parent] process id: 61052, parent process id: 8243, process group id: 61052, session id: 8243
echoMsg("parent");

// Fork the process for the first time
$pid = pcntl_fork(); if ( $pid < $pid = pcntl_fork())
if ( $pid < 0 ) {
    if ( $pid < 0 ) { exit('fork error'); } else if( $pid > 0 )
} else if( $pid > 0 ) {
    // [parent1] process id: 61052, parent id: 8243, process group id: 61052, session id: 8243 exited the process
    
    echoMsg("parent1", "exited the process"); exit.
}

// Create a child process with ID 61053, but the process group and session are still the same as the parent process.
// [child1] process id: 61053, parent id: 61052, process group id: 61052, session id: 8243
echoMsg("child1");

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

// Now you'll see that the process group ID and session ID have both changed to 61053, which is equivalent to starting a session window like in a Linux terminal.
// [child1] process id: 61053, parent id: 61052, group id: 61053, session id: 61053
echoMsg("child1");

// Fork the process a second time
// The reason for a second fork is to avoid being associated with a terminal-controlled process, which was created in terminal mode, process 61052.
// This process 61052 was created in terminal mode and needs to be detached to ensure daemon stability.
$pid = pcntl_fork(); if ( $pid < $pid = pcntl_fork() )
if ( $pid < 0 ){
    exit('fork error'); } else if( $pid < 0 ){
} else if( $pid > 0 ) {
    // [parent2] process id: 61053, parent id: 61052, process group id: 61053, session id: 61053 exited the process
    
    echoMsg("parent2", "exited the process"); exit.
}

// Now the process is out of the control of the terminal process and has become a daemon.
// [child2] Process ID: 61054, Parent ID: 61053, Group ID: 61053, Session ID: 61053 Retained the process.
echoMsg("child2", "retained the process"); echoMsg("child2", "retained the process"); echoMsg("child2", "retained the process")

echoMsg("child2", "retained the process"); sleep(100);

If you have the time, you'd better execute the code and analyze it by yourself, you'll get different results. Pretending you've already done this, let's look at the static::daemonize() function in the runAll method on line 554 of Workerman's documentation, which implements the same logic as the example above. However, it also uses the umask function, whose main purpose is to assign permissions to the file or directory created by the process, so that it has permission to manipulate the file or directory.

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

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

    // Setting umask to 0 means that all files created by the current process have the highest permissions, 777.
    \umask(0); }

    // Creating a process for the first time
    $pid = \pcntl_fork(); if (-1 === $pid) { $pid = \pcntl_fork()
    if (-1 === $pid) {
    // Failed to create process.
        throw new Exception('Fork fail'); } elseif ($pid > 0) { // Create the process for the first time.
    } elseif ($pid > 0) {
    // Main process exits.
        exit(0); } elseif ($pid > 0) { // Master process exits.
    }

// The child process continues to execute...
    // Calling the posix_setsid function allows the process to break away from the parent and become a daemon.
    if (-1 === \posix_setsid()) {
        throw new Exception("Setsid fail"); }
    }

// Create the process a second time, and on System V-based systems, exit the parent process by Forking it again.
// Ensure that the resulting daemon does not become the session leader and does not have a control terminal.
    $pid = \pcntl_fork(); if (-1 === $pid); // Create the daemon a second time.
    if (-1 === $pid) {
    // Failed to create process
        throw new Exception("Fork fail"); } elseif (0 !0 !0 !0 !0 !0 )
    } elseif (0 ! == $pid) {
    // The main process exits.
        exit(0); } elseif (0 !== $pid) { // Master process exits.
    }

    // Subprocesses continue execution...
}

The daemon is also an important part of Workerman that guarantees the stability of the Workerman process. Unlike what we do with thenohup The command to start, hanging in the background, sometimes also God does not know the ghost on the hang, friends may have such an experience. Of course, there are some open source daemon management software on the market, such as supervisor, etc., followed by the use of session terminal screen, tmux and other tools to realize. In fact, there are many ways to implement the daemon, we are here just to analyze the principle of implementation of the daemon in Workerman, and lead to the realization of the daemon mode in PHP example, I hope the content of this can help you.

Thanks for reading, personal opinion is for reference only, welcome to express different views in the comment section.


Welcome to follow, share, like, favorite, in the watch, I'm the author of WeChat public number "code farmer forefather".