方法1:
php test.php &
这是依赖于终端的,如果terminal终端关闭 , 无论是正常关闭还是非正常关闭 , 这个php进程都会随着终端关闭而关闭 , 其次是代码中如果有echo或者print_r之类的输出文本 , 会被输出到当前的终端窗口中 。
方法2:
nohup php test.php &
默认情况下,代码中echo或者print_r之类输出的文本会被输出到php代码同级目录的nohup.out文件中,如果你用exit命令或者关闭按钮等正常手段关闭终端 , 该进程不会被关闭,依然会在后台持续运行。
因为echo,print_r等输出语句依赖终端,所以nohup命令默认将输出重新向到文件,不然,如果你的程序有输出,而你却关闭了会话终端,这就导致程序出错退出。
通常的做法是:nohup php test.php > /dev/null 2>&1 &
2>&1
将标准错误重定向到标准输出。/dev/null
将标准输出重定向到空文件,也就是丢弃的意思。
方法3:
setsid 就是set session id,关于setsid
1、要求当前进程不能是进程组组长。
2、会创建一个新的会话和进程组,从而脱离当前会话,并设置当前进程为进程组组长。
3、没有对应的控制终端。
<?php
// fork子进程
$pid = pcntl_fork();
if ( $pid < 0 ) {
exit( ' fork error. ' );
} else if( $pid > 0 ) {
exit( ' parent process. ' );
}
// 创建新会话,设置当前进程为组长
if ( ! posix_setsid() ) {
exit( ' setsid error. ' );
}
// fork子进程
$pid = pcntl_fork();
if( $pid < 0 ){
exit( ' fork error. ' );
} else if( $pid > 0 ) {
exit( ' parent process. ' );
}
// 重定向stdout, stderr到日志文件
global $STDOUT, $STDERR;
fclose(STDOUT);
fclose(STDERR);
$log = __DIR__ . '/daemon.txt';
if(file_exists($log)){
file_put_contents($log, '');
}else{
touch($log, 755);
}
$handler = fopen($log, "rw+");
$STDOUT = $handler;
$STDERR = $handler;
for( $i = 1 ; $i <= 100 ; $i++ ){
echo 'loop' . $i . PHP_EOL;
// 此处不要使用 $log,会出现文件内容混论
file_put_contents("php://output", $i . "--" . date("Y-m-d H:i:s", time()) . PHP_EOL, FILE_APPEND);
sleep(1);
}
注意:
1、守护进程如果有输出,那么它依然是依赖会话的,关闭会话,守护进程将会出错退出。
2、守护进程的输出是个伪需求,但是不排除某个输出调试忘记注释的情况,可以通过上面的例子将输出定向到文件;一般将echo, print_r等调试信息定向到 console 日志文件,与正常的日志内容分开。
3、守护进程也会在程序出现错误或异常时退出,所以你要在程序里接管错误并捕获异常然后记录到日志。
守护进程的原理
比如,我们在终端中启动了程序 test./test &
此时它的父进程是 sh 终端进程,如果关闭了窗口,也就是退出了 sh 进程,那么 test 进程会收到 SIGHUP 信号,此信号会导致程序退出。
我们经常看到很多程序使用 -d
参数来使程序成为守护进程,其原理就是:在程序内部对参数进行分析,如果有 -d
参数,就删掉 -d
,并保留其他参数,重新启动自己。
./test arg1 arg2 -d
if 有 -d 参数 {
./test arg1 arg2
}else{
...
}
父子关系
sh-100 --> test-101 --> test-102
由于test-101
进程在启动自己的时候就退出了,在Linux下,父进程先于子进程退出,那么子进程就会成为孤儿进程,而成为pid=1
的子进程,于是test-102
就脱离了 sh 的掌控,因此终端退出就不会导致其退出。