php中的进程是以扩展的形式来完成。通过这些扩展,我们能够很轻松的完成进程的一系列动作。
pcntl扩展:主要的进程扩展,完成进程创建于等待操作。。
- 构建此扩展不需要其他扩展。
- 在PHP中进程控制支持默认是关闭的。您需要使用 --enable-pcntl 配置选项重新编译PHP的 CGI或CLI版本以打开进程控制支持。
- 此扩展没有在 php.ini 中定义配置指令。
- 此扩展没有定义资源类型。
-
当前,这个模块没有非Unix平台可用的函数(即非Unix类系统不支持此模块)。
posix扩展:完成posix兼容机通用api,如获取进程id,杀死进程等。
- 构建此扩展不需要其他扩展。
- POSIX functions are enabled by default. You can disable POSIX-like functions with --disable-posix .
- 此扩展没有在 php.ini 中定义配置指令。
- 此扩展没有定义资源类型。
关于fork后的执行顺序:
<?php
function aa(){
echo 'AA_start';
bb();
echo 'AA_end';
}
function bb(){
echo 'BB_start';
$pid = pcntl_fork();
if ($pid == 0) {
echo 'son';
return; //此处是return
} elseif ($pid > 0) {
echo 'parent'
}
pcntl_waitpid($pid , $status, 0); //等待子进程结束
echo 'BB_end';
}
aa();
/*
AA_start
BB_start
parent
son
AA_end
BB_end //父进程被阻塞了 要等子进程执行完才会继续 所以父进程最后输出
AA_end
*/
// BB_end只输出了一次;AA_END输出了两次,也是说fork子进程,子进程的后续代码是完全遵循父进程的代码顺序的;此时就需要将return更改为exit 直接关闭就可以了
象下面代码可以直接return:
<?php
$sons = [
'son_a' => 1,
'son_b' => 2,
'son_c' => 3,
'son_d' => 4,
];
$pids = [];
foreach ($sons as $k => $v) {
$pids[$k] = pcntl_fork(); //1.为当前进程创建一个子进程,并且先运行父进程,返回的是子进程的PID;子/父进程都从此处开始执行
if ($pids[$k] == 0 ) { //2.父级先到达此处 跳转到2.1;子进程后到达 跳转到3处
$pid = posix_getpid(); //3。子进程进来
echo $k.'_'.$pid.PHP_EOL;
sonOut($v);
return; //4.在子进程的代码块中最好有exit/return语句,即执行完子进程后立即就结束。否则它也会执行 第一步中的创建进程
} elseif ($pids[$k] > 0) { //2.1 先执行父级代码代码
echo $k."_parent:".posix_getpid().PHP_EOL;
}
}
function sonOut($v) {
for ($i=1; $i <= $v; $i++){
echo $v.'_'.'_'.$i.PHP_EOL;
if($v == 4){
echo PHP_EOL;
}
sleep(1);
}
}
$status = '';
foreach ($pids as $pid) {
$res = pcntl_waitpid($pid, $status,0);//在父进程的代码中用 pcntl_waitpid($pid,&$status)阻塞父进程直到他的子进程有返回值
echo $res.'_'.$status.PHP_EOL;
}
exit('end');
[root@iZbp1hs2rode8m3mzs1jjpZ public]# php pcntl.php
son_a_parent:7972
son_a_7973
1__1
son_b_parent:7972
son_b_7974
2__1
son_c_parent:7972
son_d_parent:7972
son_c_7975
3__1
son_d_7976
4__1
2__2
3__2
4__2
7973_0
3__3
4__3
7974_0
4__4
7975_0
7976_0
进程信号:
pcntl_signal — 安装一个信号处理器
pcntl_signal ( int $signo , callback $handler [, bool $restart_syscalls = true ] ) : bool
pcntl_signal_dispatch — 调用每个等待信号通过pcntl_signal() 安装的处理器。
pcntl_signal_dispatch ( void ) : bool
pcntl_signal的实现原理是,触发信号后先将信号加入一个队列中。
然后在PHP的ticks回调函数中不断检查是否有信号,
如果有信号就执行PHP中指定的回调函数,如果没有则跳出函数。
ticks=1表示每执行1行PHP代码就回调此函数。
实际上大部分时间都没有信号产生,但ticks的函数一直会执行。
比较好的做法是去掉ticks,转而使用pcntl_signal_dispatch,在代码循环中自行处理信号。
比较两段代码
第一段
<?php
//使用ticks需要PHP 4.3.0以上版本
declare(ticks = 1);
echo microtime(true)."安装信号处理器...\n";
pcntl_signal(SIGHUP, function($signo) {
echo microtime(true)."信号处理器被调用\n";
});
echo "为自己生成SIGHUP信号...\n";
posix_kill(posix_getpid(), SIGHUP);
echo "分发...\n";
pcntl_signal_dispatch();
echo "完成\n";
[root@iZbp1hs2rode8m3mzs1jjpZ public]# php api.php
1585023396.5262_安装信号处理器...
为自己生成SIGHUP信号...
1585023396.5263_信号处理器被调用
分发...
完成
第二段
<?php
echo microtime(true)."_安装信号处理器...\n";
pcntl_signal(SIGHUP, function($signo) {
echo microtime(true)."_信号处理器被调用\n";
});
echo "为自己生成SIGHUP信号...\n";
posix_kill(posix_getpid(), SIGHUP);
sleep(3);
echo "分发...\n";
pcntl_signal_dispatch();
echo "完成\n";
1585023348.3601_安装信号处理器...
为自己生成SIGHUP信号...
分发...
1585023351.3603_信号处理器被调用
完成
仔细看信号调用的时间戳 使用declare(ticks = 1); 创建信号时候 立即就调用了;如果去掉declare(ticks = 1); 改用pcntl_signal_dispatch则在pcntl_signal_dispatch调用后执行调用函数