php进程认识
1、基本认识
php系统提供了pcntl_fork 函数来操作进程。
pcntl_fork:当前进程位置产生分支(子进程)。fork是创建了一个子进程,父进程和子进程 都从fork的位置开始向下继续执行,不同的是父进程执行过程中,得到的fork返回值为子进程号,而子进程得到的是0。
$pid = pcntl_fork(); //创建子进程
if($pid < 0){
echo "子进程创建失败".PHP_EOL;
}elseif($pid){
echo "子进程pid -- ".$pid.PHP_EOL;
}else{
echo "执行子进程 ---".posix_getpid().PHP_EOL;
}
2、孤儿进程
孤儿进程:一个父进程退出,而它的子进程或多个子进程还在运行,那么那些子进程将成为孤儿进程,孤儿进程将被init进程(进程号为1)收养,并由init进程对他们完成状态收集。
孤儿进程是没有父进程的进程,孤儿进程这个重任就落到了init进程身上,init进程就好像是一个民政局,专门负责处理孤儿进程的善后工作。每当出现一个孤儿进程的时候,内核就把孤 儿进程的父进程设置为init,而init进程会循环地wait()它的已经退出的子进程。这样,当一个孤儿进程凄凉地结束了其生命周期的时候,init进程就会代表党和政府出面处理它的一切善后工作。因此孤儿进程并不会有什么危害。
$pid = pcntl_fork(); //创建子进程
if($pid < 0){
echo "子进程创建失败".PHP_EOL;
}elseif($pid){
echo "子进程pid -- ".$pid.PHP_EOL;
}else{
sleep(30);
echo "执行子进程 ---".posix_getpid().PHP_EOL;
}
3、僵尸进程
僵尸进程:一个进程使用fork创建子进程,如果子进程退出,父进程没有调用wait或 waitpid获取子进程的状态信息,那么子进程的描述符任然保存在系统中。这种进程称为僵尸进程。
任何一个子进程(init除外)在exit()之后,并非马上就消失掉,而是留下一个称为僵尸进程(Zombie)的数据结构,等待父进程处理。这是每个 子进程在结束时都要经过的阶段。如果子进程在exit()之后,父进程没有来得及处理,这时用ps命令就能看到子进程的状态是“Z”。如果父进程能及时 处理,可能用ps命令就来不及看到子进程的僵尸状态,但这并不等于子进程不经过僵尸状态。 如果父进程在子进程结束之前退出,则子进程将由init接管。init将会以父进程的身份对僵尸状态的子进程进行处理。
对于linux系统来说,一个长时间运行的多进程程序一定要回收子进程,因为系统的进程资源是有限的,僵尸进程会让系统的可用资源减少。
$pid = pcntl_fork(); //创建子进程
if($pid < 0){
echo "子进程创建失败".PHP_EOL;
}elseif($pid){
sleep(30); //主进程被挂起,30秒内为退出
echo "子进程pid -- ".$pid.PHP_EOL;
}else{
echo "执行子进程 ---".posix_getpid().PHP_EOL;
}
上边我们已经说了僵尸进程的危害,那么如何避免僵尸进程。使用
pcntl_wait($status); //该函数阻塞当前进程,只到当前进程的一个子进程退出或者收到一个结束当前进程的信号
pcntl_waitpid($pid, $status); //pcntl_waitpid()和pcntl_wait()功能相同。前者第一个参数支持指定pid参数,当指定-1作为pid的值等同于后者。
$pid = pcntl_fork(); //创建子进程
if($pid < 0){
echo "子进程创建失败".PHP_EOL;
}elseif($pid){
pcntl_wait($status); 或 pcntl_waitpid($pid, $status);
sleep(30); //主进程被挂起,30秒内为退出
echo "子进程pid -- ".$pid.PHP_EOL;
}else{
echo "执行子进程 ---".posix_getpid().PHP_EOL;
}
以上是单进程进程回收,下边我们看下多进程的进程回收,阻塞的回收方式和以上一样,下边我们使用非阻塞
pcntl_wait($status, WNOHANG);
pcntl_waitpid($pid, $status, WNOHANG);
$child_pids = [];
for($i=0;$i<3;$i++){
$pid = pcntl_fork();
if($pid<0){
exit('进程创建失败!!');
}elseif($pid){
//$res = pcntl_wait($status, WNOHANG);
//$res = pcntl_waitpid($pid, $status, WNOHANG);
echo "父进程pid --". posix_getppid()." 子进程pid ".$pid;
}else{
echo "执行子进程".posix_getpid();
exit(); //子进程需要exit,防止子进程也进入for循环。如果没有exit(),最终创建的子进程不只3个。
}
}
while(count($child_pids)){
foreach($child_pids as $key => $pid){
//$res = pcntl_wait($status, WNOHANG);
$res = pcntl_waitpid($pid, $status, WNOHANG);
//var_dump($res);
//exit();
if ($res == -1 || $res > 0){
echo time()." Child process exit,pid {$pid}\n";
unset($child_pids[$key]);
}else{
echo time()." Wait End,pid {$pid}\n";
}
}
}
以上及实现了进程的非阻塞(备足:若结果不明显则加大进程数)
学习编程就是升级打怪的过程。从最开始的静态页面到动态页面,从基本的curd到业务架构,数据存储到大数据处理,从基本的http请求到网络处理,每一次技能的上升都是一次打怪升级。