This article mainly talks about using PHP7 to implement the daemon daemon process. It has certain reference value. Interested friends can learn about it.
In a multi-tasking computer operating system, a daemon process is a computer program that executes in the background. Such programs will be initialized as processes. The names of daemon programs usually end with the letter "d": for example, syslogd refers to the daemon that manages system logs.
The daemon program is a server program that is always running, also called a daemon process. Usually it runs in the background of the system. There is no control terminal and it does not interact with the foreground. The daemon program is generally used as a system service. A daemon is a long-running process that usually runs after the system starts and ends when the system shuts down. Generally speaking, the Daemon program runs in the background because it does not have a control terminal and cannot interact with users in the foreground. Daemon programs are generally used as service programs, waiting for client programs to communicate with it. We also call the running daemon program a daemon process.
Normally, the daemon process does not have any existing parent process (that is, PPID=1), and is located directly under init in the UNIX system process hierarchy. A daemon process usually makes itself a daemon by the following method: Run fork on a child process, and then terminate its parent process immediately so that the child process can be used in init Run under . This method is often called "shelling."
The system usually starts the daemon process at startup. Daemons provide support for responding to network requests, hardware activity, or other requests from other applications through certain tasks. Daemons can also configure hardware (such as devfsd on some Linux systems), run scheduled tasks (such as cron), and run other tasks. Each process has a parent process. When the child process exits, the parent process can get the exit status of the child process.
A daemon process is simply a process that can run in the background without the terminal. This is a very common process in Linux. For example, after services such as apache or mysql are started, It will be resident in memory as a daemon process. Daemons are applications that run in the background and are not directly operated by the user. Examples of daemons are Cron and MySQL. Using the PHP daemon is very simple and requires compilation parameters with PHP 4.1 or higher: --enable-pcntl
##Suppose there is a time-consuming task that needs to be run in the background: import all 20 million users in the user table in all mysql into redis for preheating cache, then this task will probably not end for a while. At this time, you need to write a php script to run in the system as a daemon, and it will be automatically launched after the end.
2. Use the nohup command
nohup php task.php &. By default, echo in the code Or the text output by print_r will be output to the nohup.out file in the same directory as the PHP code. If you close the terminal by normal means such as the exit command or the close button, the process will not be closed and will still continue to run in the background. . However, if the terminal encounters an abnormal exit or termination, the php process will also exit immediately. In essence, it is not a stable and reliable daemon solution.
##3. Implemented throughpcntl and posix extensions
Things to pay attention to during programming include:
pcntl_fork()
and posix_setsid
Let the main process leave the terminal pcntl_signal()
Ignore or process SIGHUP
signalpcntl_fork()
or pcntl_signal ()
Ignore the SIGCHLD
signal to prevent the child process from becoming a Zombie process umask()
, Prevent the influence of permissions from inherited file permissionsSTDIN/STDOUT/STDERR
of the running process to /dev/null
Or on other streams##daemon has the following characteristics:
Want to view the running daemon Processes can be viewed through ps -ax or
ps -ef, where
-x means that processes without controlling terminals will be listed.
The fork system call is used to copy a process that is almost identical to the parent process. The newly generated child process is different. The problem is that it has a different pid and a different memory space from the parent process. According to the code logic, the parent and child processes can complete the same work, or they can be different. The child process inherits resources such as file descriptors from the parent process.
The pcntl extension in PHP implements the
pcntl_fork() function, which is used to fork a new process in PHP.
setsid system call is used to create a new session and set the process group id. There are several concepts here: Session, Process Group
.
In Linux, user login generates a session. A session contains one or more process groups, and a process group contains multiple processes. Each process group has a group leader (Session Leader), and its pid is the group id of the process group. Once the process leader opens a terminal, this terminal is called the controlling terminal. Once an exception occurs in the control terminal (disconnection, hardware error, etc.), a signal will be sent to the process group leader.
Background running programs (such as shell execution instructions ending with &) will also be killed after the terminal is closed, that is, when the control terminal is disconnected, it is not handled well. The
SIGHUP signal is emitted, and the default behavior of the SIGHUP
signal is to exit the process.
Call setsid After the system call, the current process will create a new process group. If the terminal is not opened in the current process, then this one There will be no controlling terminal in the process group, and there will be no problem of killing the process by closing the terminal.
The posix extension in PHP implements the
posix_setsid() function, which is used to set new processes in PHP Group.
First of all, the setsid system call cannot be called by the process group leader and will return -1.
The sample code for the secondary fork operation is as follows:
<span style="font-size: 16px;">$pid1 = pcntl_fork(); if ($pid1 > 0) {</span><br/><span style="font-size: 16px;"> // 父进程会得到子进程号,所以这里是父进程执行的逻辑 exit('parent process. 1'."\n"); } else if ($pid1 < 0) { exit("Failed to fork 1\n"); } if (-1 == posix_setsid()) { exit("Failed to setsid\n"); } $pid2 = pcntl_fork(); if ($pid2 > 0) { exit('parent process. 2'."\n"); } else if ($pid2 < 0) { exit("Failed to fork 2\n"); }</span>
pcntl_fork() function creates a child process. This child process Only the PID (process ID) and PPID (parent process ID) differ from its parent process.
Return value
When successful, the PID of the generated child process is returned in the parent process execution thread, and is executed in the child process. 0 is returned in the thread. When fails, -1 is returned in the context of the parent process. The child process will not be created and a PHP error will be raised.
假定我们在终端中执行应用程序,进程为 a,第一次 fork 会生成子进程 b,如果 fork 成功,父进程 a 退出。b 作为孤儿进程,被 init 进程托管。
此时,进程 b 处于进程组 a 中,进程 b 调用 posix_setsid
要求生成新的进程组,调用成功后当前进程组变为 b。
php fork2.php parent process. 1 parent process. 2
此时进程 b 事实上已经脱离任何的控制终端,例程:
cli_set_process_title('process_a'); $pidA = pcntl_fork(); if ($pidA > 0) { exit(0); } else if ($pidA < 0) { exit(1); } cli_set_process_title('process_b'); if (-1 === posix_setsid()) { exit(2); } while(true) { sleep(1); }
执行程序之后:
$ php cli-title.php $ ps ax | grep -v grep | grep -E 'process_|PID' PID TTY STAT TIME COMMAND 15725 ? Ss 0:00 process_b
重新打开一个shell窗口,效果一样,都在呢
从 ps 的结果来看,process_b 的 TTY 已经变成了 ?
,即没有对应的控制终端。
代码走到这里,似乎已经完成了功能,关闭终端之后 process_b 也没有被杀死,但是为什么还要进行第二次 fork 操作呢?
StackOverflow 上的一个回答写的很好:
The second fork(2) is there to ensure that the new process is not a session leader, so it won’t be able to (accidentally) allocate a controlling terminal, since daemons are not supposed to ever have a controlling terminal.
这是为了防止实际的工作的进程主动关联或者意外关联控制终端,再次 fork 之后生成的新进程由于不是进程组组长,是不能申请关联控制终端的。
综上,二次 fork 与 setsid 的作用是生成新的进程组,防止工作进程关联控制终端。
写一个demo测试下
<?php // 第一次fork系统调用 $pid_A = pcntl_fork(); // 父进程 和 子进程 都会执行下面代码 if ($pid_A < 0) { // 错误处理: 创建子进程失败时返回-1. exit('A fork error '); } else if ($pid_A > 0) { // 父进程会得到子进程号,所以这里是父进程执行的逻辑 exit("A parent process exit \n"); } // B 作为孤儿进程,被 init 进程托管,此时,进程 B 处于进程组 A 中 // 子进程得到的$pid为0, 所以以下是子进程执行的逻辑,受控制终端的影响,控制终端关闭则这里也会退出 // [子进程] 控制终端未关闭前,将当前子进程提升会会话组组长,及进程组的leader // 进程 B 调用 posix_setsid 要求生成新的进程组,调用成功后当前进程组变为 B if (-1 == posix_setsid()) { exit("Failed to setsid\n"); } // 此时进程 B 已经脱离任何的控制终端 // [子进程] 这时候在【进程组B】中,重新fork系统调用(二次fork) $pid_B = pcntl_fork(); if ($pid_B < 0) { exit('B fork error '); } else if ($pid_B > 0) { exit("B parent process exit \n"); } // [新子进程] 这里是新生成的进程组,不受控制终端的影响,写写自己的业务逻辑代码 for ($i = 1; $i <= 100; $i++) { sleep(1); file_put_contents('daemon.log',$i . "--" . date("Y-m-d H:i:s", time()) . "\n",FILE_APPEND); }
Window 下跑回直接抛出异常
php runtime\daemon.php PHP Fatal error: Uncaught Error: Call to undefined function pcntl_fork() in D:\phpStudy\PHPTutorial\WWW\notes\runtime\daemon.php:13 Stack trace: #0 {main} thrown in D:\phpStudy\PHPTutorial\WWW\notes\runtime\daemon.php on line 13
Linux 下执行,输出结果
<span style="font-size: 16px;">php daemon.php</span><br/><span style="font-size: 16px;">... 97--2018-09-07 03:50:09 98--2018-09-07 03:50:10 99--2018-09-07 03:50:11 100--2018-09-07 03:50:12</span>
相关教程:PHP视频教程
The above is the detailed content of Detailed explanation of daemon daemon implementation in PHP7. For more information, please follow other related articles on the PHP Chinese website!