php的多进程实验分析
最后更新于:2022-04-02 04:27:26
## php的多进程实验分析
>[tip] 由于时间久远,本文不再具有参考性,请参阅更为易读新文章:[进程 · php笔记 · 看云](https://www.kancloud.cn/xiak/php-node/785180)
pcntl_fork — 在当前进程当前位置产生分支(子进程)。译注:fork是创建了一个子进程,父进程和子进程 都从fork的位置开始向下继续执行,不同的是父进程执行过程中,得到的fork返回值为子进程 号,而子进程得到的是0。
```php
" . PHP_EOL.PHP_EOL);
$count = 3;
for ($i = 0; $i < $count; $i++) {
$pid = pcntl_fork();
if($pid == -1) {
fwrite(STDOUT, "Could not fork worker {$i}" . PHP_EOL);
die();
}
else if(!$pid) {
fun($i, $count);
// 如果不使用下面的方式退出,将会出现什么呢,经测试,下面是我们得出的结果。
// break;
// exit;
} else {
fwrite(STDOUT, "父进程 " . $i . PHP_EOL);
}
}
function fun($i, $count)
{
// global $num;
$num = $i;
$jc = exec('echo $$');
$time = date('Y-m-d H:i:s');
fwrite(STDOUT, "进程:$jc 计数:$num - $count 时间:$time" . PHP_EOL);
fwrite(STDOUT, "start > " . date('Y-m-d H:i:s') . PHP_EOL);
fwrite(STDOUT, "~ 3s ~" . PHP_EOL);
sleep(3);
fwrite(STDOUT, "enddd $i < " . date('Y-m-d H:i:s') . PHP_EOL.PHP_EOL);
}
```
父进程循环 从0开始 3次 创建3个子进程 产生下面第一部分
i = 0/1/2 结束循环
子0进程下面的循环 从1开始循环 能循环2次 创建2个子进程 产生下面第二部分(由第一子进程产生) 和 第三部分(由第二子进程产生)
子1进程下面的循环 从2开始循环 能循环1次 创建1个子进程 产生下面第四部分
子2进程下面的循环 没有循环 退出了(什么都没做)
由此分析可见PHP的这个子进程机制的特点:
继承父进程的“运行上下文”即变量和环境都是直接继承了,并且各个子进程相互,不受影响。
~~~
[root@iZ28yn5lehbZ x]# x=sf php d3.php
stat>
父进程 0
父进程 1
父进程 2
进程:25608 计数:0 - 3 时间:2016-08-15 08:35:47
start > 2016-08-15 08:35:47
~ 3s ~
进程:25610 计数:2 - 3 时间:2016-08-15 08:35:47
start > 2016-08-15 08:35:47
~ 3s ~
进程:25611 计数:1 - 3 时间:2016-08-15 08:35:47
start > 2016-08-15 08:35:47
~ 3s ~
enddd 1 < 2016-08-15 08:35:50
enddd 2 < 2016-08-15 08:35:53
enddd 0 < 2016-08-15 08:35:50
父进程 1
父进程 2
进程:25643 计数:1 - 3 时间:2016-08-15 08:35:50
start > 2016-08-15 08:35:50
~ 3s ~
进程:25644 计数:2 - 3 时间:2016-08-15 08:35:50
start > 2016-08-15 08:35:50
~ 3s ~
enddd 1 < 2016-08-15 08:35:53
enddd 2 < 2016-08-15 08:35:53
父进程 2
进程:25646 计数:2 - 3 时间:2016-08-15 08:35:50
start > 2016-08-15 08:35:50
~ 3s ~
enddd 2 < 2016-08-15 08:35:50
父进程 2
进程:25663 计数:2 - 3 时间:2016-08-15 08:35:53
start > 2016-08-15 08:35:53
~ 3s ~
enddd 2 < 2016-08-15 08:35:56
~~~
php获取子进程ID
```
$jc = exec('echo $$'); // 这方式获取的不是当前PHP脚本执行的进程ID
$child_id = getmypid(); // 这个才是当前PHP脚本执行的进程ID
```
参见:https://segmentfault.com/q/1010000004634861
```
父进程:17488 字进程:17489 计数:0
父进程:17488 字进程:17490 计数:1
进程:17489 计数:0 - 3 时间:2016-08-15 16:55:22
start > 2016-08-15 16:55:22
~ 3s ~
父进程:17488 字进程:17491 计数:2
进程:17490 计数:1 - 3 时间:2016-08-15 16:55:22
start > 2016-08-15 16:55:22
~ 3s ~
进程:17491 计数:2 - 3 时间:2016-08-15 16:55:22
start > 2016-08-15 16:55:22
~ 3s ~
[root@iZ28yn5lehbZ x]# enddd 0 < 2016-08-15 16:55:25
enddd 1 < 2016-08-15 16:55:25
enddd 2 < 2016-08-15 16:55:25
```
上面这个父进程执行部分是否先输出来,有时并无实际规律,理论上和实际上不一样,原因是可能需要考虑进程在CPU资源分配使用上面。(但好像父进程 计数:0永远在第一,也就是说在这个开辟子进程的结构中,父进程是首次执行的。)
```
$status = '';
$pid = pcntl_fork();
//父进程和子进程都会执行下面代码
if ($pid == -1) {
//错误处理:创建子进程失败时返回-1.
die('could not fork');
} else if ($pid) {
//父进程会得到子进程号,所以这里是父进程执行的逻辑
// pcntl_wait($status); //等待子进程中断,防止子进程成为僵尸进程。
fwrite(STDOUT, "父进程" . PHP_EOL);
} else {
//子进程得到的$pid为0, 所以这里是子进程执行的逻辑。
fwrite(STDOUT, "子进程" . PHP_EOL);
}
```
**分析**
1. 父进程执行完毕就退出了,但是这个退出不会结束可能还没执行完成的子进程,也就是说子进程会变成僵尸进程,这并不像结束了ssh就结束了bash,就结束了我们的脚本那样。(这个也确实是要成为僵尸进程,不然孩子刚生出来还没玩够就要死了)
2. 输出顺序为:
父进程
子进程
如果将pcntl_wait这一行注释掉,输出顺序就为
子进程
父进程
**测试:**
父进程和子进程 都从fork的位置开始向下继续执行,所以这个硬要说规律也不好说,这个是CPU指令执行资源的事了。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-17_57b4207ea01ac.png)
* * * * *
### 扩展
[从 0 到 1 优雅的实现PHP多进程管理](https://mp.weixin.qq.com/s/lbGCQu7zkKUfPhFMFbHooQ)
[TIGERB/naruto: An object-oriented multi process manager for PHP](https://github.com/TIGERB/naruto)
* * * * *
last update:2017-12-18 14:55:26
';