💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
## php的多进程实验分析 >[tip] 由于时间久远,本文不再具有参考性,请参阅更为易读新文章:[进程 · php笔记 · 看云](https://www.kancloud.cn/xiak/php-node/785180) pcntl_fork — 在当前进程当前位置产生分支(子进程)。译注:fork是创建了一个子进程,父进程和子进程 都从fork的位置开始向下继续执行,不同的是父进程执行过程中,得到的fork返回值为子进程 号,而子进程得到的是0。 ```php <?php fwrite(STDOUT, "stat>" . 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://box.kancloud.cn/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