A Preliminary Study of PHP Multiprocesses-Again on daemon Process

  php, Process

[Original Address:https://blog.ti-node.com/blog …]

In fact, the daemon process was discussed earlier, but it did not involve too many principles, but did not affect the use. Today I’m going to talk about two or three more things about the daemon process. In essence, if you simply implement and use the daemon process, you can do it without looking at it.

Gao Zhen, *NIX is really profound and profound. The deeper one looks, the more diao it is discovered. The principle is often boring and everyone doesn’t like it, but it doesn’t affect my understanding of these things.

Three concepts, reason (bei) solution (song):

  • Process group. A group of related processes can form a process group. Each process group will have a group ID (positive integer), and each process group will have a group leader process whose ID is equal to the process group ID. The leader process can create a new process group and other processes in the process group. A process group has a life cycle. Even if the leader process is dead and there are only other members in the group, then even if the process group is still alive and only the last member in the group is dead, it is really gone.
  • Conversation. A group of related processes forms a conversation. Under *NIX, a new session is created through setsid (). However, it is worth noting that the leader process cannot create a session. A simple understanding is that in the leader process, executing setsid function will report an error, which is very important. Therefore, it is generally the leader process that executes fork, and then the main process exits. because the process ID of the sub-process is newly assigned and the process group ID of the sub-process is inherited from the parent process, the sub-process is doomed not to be the leader process, thus ensuring that the setsid function can be executed in the sub-process. When executing the setsid function, the following three more important things generally occur:

    • The process will create a new process group and the process will be the group leader (or you can think of it as a promotion)
    • The process creates a session group and becomes the session head process of the session (the session head process is the process that created the session)
    • The process will lose control of the terminal. If the process does not already have control of the terminal, then (liao). If so, then the process will also leave the control terminal and lose contact with it.
  • Control terminal. Each session may have a control terminal (looking at metaphysics, you can temporarily understand it as a kind of black command line window). The session head process that establishes a connection with the control terminal is called the control process.

In combination with Linux command ps, let’s look at the feud between the above concepts. Let’s look at the execution results of PS-O PID, PPID, PGID, SID, COMM | LESS that we commonly use:

The first line is PID, PPID, PGID, SID and COMMAND respectively, followed by process ID, parent process ID, process group ID, session ID and command respectively.

Through the last column, we know that the second row is bash, that is, bash shell process, with process ID 15793, parent process 13291, process group ID 15793, and session ID 15793. combined with the previous concepts, we can know bash shell is the group leader of the process.

The third line is the process of ps command, whose process ID is 15816. He came out of bash process fork, so his parent process ID is 15793, then his group ID is 15816, and his session ID is still 15793.

The last line is the process of less command, its process ID is 15817, and it is also generated by bash process fork, so its parent process ID is 15793, then its group ID is 15816, and its session ID is still 15793.

To summarize briefly:

  • The above three processes have formed two process groups, bash himself is a group, group ID is 15793, and group leader process is bash himself; Ps and less are one group, group ID is 15816, and group leader process is ps process
  • The above-mentioned three processes belong to the same session, the session ID is 15793, and the session head process is bash process (to be determined)
  • The control terminal is the open terminal window, and the control process associated with it is the bash process.

Through such an analysis, does it feel acceptable? Then, what is the relationship between this and the daemon process?
La la la, the following direct analysis by introducing code:

$pid = pcntl_fork();
if( $pid < 0 ){
  exit('fork error.');
} else if( $pid > 0 ) {
  // 主进程退出
  exit();
}
// 子进程继续执行

// 最关键的一步来了,执行setsid函数!
if( !posix_setsid() ){
  exit('setsid error.');
}

// 理论上一次fork就可以了
// 但是,二次fork,这里的历史渊源是这样的:在基于system V的系统中,通过再次fork,父进程退出,子进程继续,保证形成的daemon进程绝对不会成为会话首进程,不会拥有控制终端。

$pid = pcntl_fork();
if( $pid  < 0 ){
  exit('fork error');
} else if( $pid > 0 ) {
  // 主进程退出
  exit;
}

// 子进程继续执行

// 啦啦啦,啦啦啦,啦啦啦,已经变成daemon啦,开心
cli_set_process_title('testtesttest');
// 睡眠1000000,防止进程执行完毕挂了
sleep( 1000000 );

Save the above file as daemon.php, then PHP daemon.php will execute it, using ps -aux | grep testte. If there is no big problem, you should be able to see the process running in the background.

So why should fork be the first step? Because the process that calls setsid cannot be the leader process. ), so you must fork it once, then exit the main process directly and keep the child processes. Because the subprocess must not be a leader process, the subprocess can call setsid. Calling setsid will result in three phenomena: creating a new session and becoming the first process of the session, creating a process group and becoming the leader process, leaving the control terminal.

La la la, understand why the boring piece of knowledge at the beginning of the article is for the sake of what?

However, in fact, the above code has only completed 80% of a standard daemon, and 20% still needs further improvement. So, what needs to be improved? Let’s modify the above code so that the program performs some text output in the final code segment:

$pid = pcntl_fork();
if( $pid < 0 ){
  exit('fork error.');
} else if( $pid > 0 ) {
  // 主进程退出
  exit();
}
// 子进程继续执行

// 最关键的一步来了,执行setsid函数!
if( !posix_setsid() ){
  exit('setsid error.');
}

// 理论上一次fork就可以了
// 但是,二次fork,这里的历史渊源是这样的:在基于system V的系统中,通过再次fork,父进程退出,子进程继续,保证形成的daemon进程绝对不会成为会话首进程,不会拥有控制终端。

$pid = pcntl_fork();
if( $pid  < 0 ){
  exit('fork error');
} else if( $pid > 0 ) {
  // 主进程退出
  exit;
}

// 子进程继续执行

// 啦啦啦,啦啦啦,啦啦啦,已经变成daemon啦,开心
cli_set_process_title('testtesttest');
// 循环1000次,每次睡眠1s,输出一个字符test
for( $i = 1; $i <= 1000; $i++ ){
  sleep( 1 );
  echo "test".PHP_EOL;
}

Save the file as daemon.php, and then PHP daemon.php executes the file. Well, is there something strange, like the following figure:

Even if you press Ctrl+C, the terminal is continuously outputting test. The only way is to close the current terminal window and open another one. However, this does not conform to the mainstream values of socialism. Therefore, we have to solve the standard output and error output. Our daemon program can no longer use the terminal window as the default standard output.

The second is to change the current working directory to the root directory. Otherwise, the following problem may occur: if the working directory of the parent process is a mounted directory, then the child process will inherit the working directory of the parent process. when the child process has been daemonized, there will be a tragedy: although the originally mounted directory is no longer used, it cannot be unloaded by umount, which is very tragic.

最后一个问题是,要在第一次fork后设置umask(0),避免权限上的一些问题。所以较为完整的代码如下:
// 设置umask为0,这样,当前进程创建的文件权限则为777
umask( 0 );

$pid = pcntl_fork();
if( $pid < 0 ){
  exit('fork error.');
} else if( $pid > 0 ) {
  // 主进程退出
  exit();
}
// 子进程继续执行

// 最关键的一步来了,执行setsid函数!
if( !posix_setsid() ){
  exit('setsid error.');
}

// 理论上一次fork就可以了
// 但是,二次fork,这里的历史渊源是这样的:在基于system V的系统中,通过再次fork,父进程退出,子进程继续,保证形成的daemon进程绝对不会成为会话首进程,不会拥有控制终端。

$pid = pcntl_fork();
if( $pid  < 0 ){
  exit('fork error');
} else if( $pid > 0 ) {
  // 主进程退出
  exit;
}

// 子进程继续执行

// 啦啦啦,啦啦啦,啦啦啦,已经变成daemon啦,开心
cli_set_process_title('testtesttest');
// 一般服务器软件都有写配置项,比如以debug模式运行还是以daemon模式运行。如果以debug模式运行,那么标准输出和错误输出大多数都是直接输出到当前终端上,如果是daemon形式运行,那么错误输出和标准输出可能会被分别输出到两个不同的配置文件中去
// 连工作目录都是一个配置项目,通过php函数chdir可以修改当前工作目录
chdir( $dir );

[Original Address:https://blog.ti-node.com/blog …]