浅析PHP7的多进程及实例源码


Posted in PHP onApril 14, 2019

准备

我们都知道PHP是单进程执行的,PHP处理多并发主要是依赖服务器或PHP-FPM的多进程及它们进程的复用,但PHP实现多进程也意义重大,尤其是在后台Cli模式下处理大量数据或运行后台DEMON守护进程时,多进程的优势不用多说。

PHP的多线程也曾被人提及,但进程内多线程资源共享和分配的问题难以解决。PHP也有多线程想关的扩展 pthreads ,但据说不太稳定,且要求环境为线程安全,所用不多。

以前PHP群里的一位大神曾指导说后台PHP想进阶必然避不开多进程,正好公司里的守护进程也应用了PHP的多进程,结合着谷哥的各种资料和手册,总算理解了多进程,并自己写了一个小demo(在linux系统上实现的),用此文总结一下,如有错漏,谢谢提出。

要实现PHP的多进程,我们需要两个扩展 pcntl 和 posix,安装方法这里不再赘述。

在php中我们使用pcntl_fork()来创建多进程(在*NIX系统的C语言编程中,已有进程通过调用fork函数来产生新的进程)。fork出来新进程则成为子进程,原进程则成为父进程,子进程拥有父进程的副本。这里要注意:

• 子进程与父进程共享程序正文段

• 子进程拥有父进程的数据空间和堆、栈的副本,注意是副本,不是共享

• 父进程和子进程将继续执行fork之后的程序代码

• fork之后,是父进程先执行还是子进程先执行无法确认,取决于系统调度(取决于信仰)

这里说子进程拥有父进程数据空间以及堆、栈的副本,实际上,在大多数的实现中也并不是真正的完全副本。更多是采用了COW(Copy On Write)即写时复制的技术来节约存储空间。简单来说,如果父进程和子进程都不修改这些 数据、堆、栈 的话,那么父进程和子进程则是暂时共享同一份 数据、堆、栈。只有当父进程或者子进程试图对 数据、堆、栈 进行修改的时候,才会产生复制操作,这就叫做写时复制。

在调用完pcntl_fork()后,该函数会返回两个值。在父进程中返回子进程的进程ID,在子进程内部本身返回数字0。由于多进程在apache或者fpm环境下无法正常运行,所以大家一定要在php cli环境下执行代码。

创建子进程

创建PHP子进程是多进程的开始,我们需要pcntl_fork()函数;

fork函数详解

pcntl_fork() — 在当前进程当前位置产生分支(子进程)。此函数创建了一个新的子进程后,子进程会继承父进程当前的上下文,和父进程一样从pcntl_fork() 函数处继续向下执行,只是获取到的pcntl_fork() 的返回值不同,我们便能从判断返回值来区分父进程和子进程,分配父进程和子进程去做不同的逻辑处理。

pcntl_fork() 函数成功执行时会在父进程返回子进程的进程id(pid),因为系统的初始进程init进程的pid为1,后来产生进程的pid都会大于此进程,所以我们可以通过判断pcntl_fork()的返回值大于1来确实当前进程是父进程;而在子进程中,此函数的返回值会是固定值0,我们也可以通过判断pcntl_fork()的返回值为0来确定子进程;而pcntl_fork()函数在执行失败时,会在父进程返回-1,当然也不会有子进程产生。

fork进程实例

fork子进程

$ppid = posix_getpid();

$pid = pcntl_fork();

if ($pid == -1) {

  throw new Exception('fork child process fail');

} elseif ($pid > 0) {

  cli_set_process_title("我是父 process,pid is : {$ppid}.");

  sleep(30);

} else {

  $cpid = posix_getpid();

  cli_set_process_title("我是 {$ppid} 子的 process,我的 process pid is : {$cpid}.");

  sleep(30);

}

说明:

posix_getpid():返回当前进程 id

cli_set_process_title('进程名称'):为当前进程取一个响亮的名字。

运行这个例子,我们便能看到当前两个PHP进程了。

www@iZ2zec3dge6rwz2uw4tveuZ:~/test$ ps aux|grep -v grep |grep 我

www   18026 0.5 1.2 204068 25772 pts/0  S+  14:08  0:00 我是父 process,pid is : 18026.

www   18027 0.0 0.3 204068 6640 pts/0  S+  14:08  0:00 我 18026 子的 process,我的 process pid is : 18027. 

第一段代码,在程序从pcntl_fork()后父进程和子进程将各自继续往下执行代码:

$pid = pcntl_fork();

if( $pid > 0 ){

 echo "我是父亲".PHP_EOL;

} else if( 0 == $pid ) {

 echo "我是儿子".PHP_EOL;

} else {

 echo "fork失败".PHP_EOL;

} 

结果:

www@iZ2zec3dge6rwz2uw4tveuZ:~/test$ php 123.php

我是父亲

我是儿子

第二段代码,用来说明子进程拥有父进程的数据副本,而并不是共享:

// 初始化一个 number变量 数值为1

$number = 1;

$pid = pcntl_fork();

if ($pid > 0) {

  $number += 1;

  echo "我是父亲,number+1 : { $number }" . PHP_EOL;

} else if (0 == $pid) {

  $number += 2;

  echo "我是儿子,number+2 : { $number }" . PHP_EOL;

} else {

  echo "fork失败" . PHP_EOL;

}

结果

www@iZ2zec3dge6rwz2uw4tveuZ:~/test$ php 1234.php

我是父亲,number+1 : { 2 }

我是儿子,number+2 : { 3 }
PHP 相关文章推荐
用PHP实现登陆验证码(类似条行码状)
Oct 09 PHP
php+jquery编码方面的一些心得(utf-8 gb2312)
Oct 12 PHP
PHP 杂谈《重构-改善既有代码的设计》之五 简化函数调用
May 07 PHP
php实现多张图片上传加水印技巧
Apr 18 PHP
深入理解:XML与对象的序列化与反序列化
Jun 08 PHP
PHP取二进制文件头快速判断文件类型的实现代码
Aug 05 PHP
修改apache配置文件去除thinkphp url中的index.php
Jan 17 PHP
深入解析WordPress中加载模板的get_template_part函数
Jan 11 PHP
thinkPHP5.0框架引入Traits功能实例分析
Mar 18 PHP
ThinkPHP 3.2.3实现页面静态化功能的方法详解
Aug 03 PHP
PHP双向链表定义与用法示例
Jan 31 PHP
使用PHPExcel导出Excel表
Sep 08 PHP
什么是PHP7中的孤儿进程与僵尸进程
Apr 14 #PHP
php intval函数用法总结
Apr 14 #PHP
PHP中上传文件打印错误错误类型分析
Apr 14 #PHP
PHP扩展Swoole实现实时异步任务队列示例
Apr 13 #PHP
php+ajax实现商品对比功能示例
Apr 13 #PHP
PHP开发的文字水印,缩略图,图片水印实现类与用法示例
Apr 12 #PHP
详解PHP素材图片上传、下载功能
Apr 12 #PHP
You might like
php+mysqli数据库连接的两种方式
2015/01/28 PHP
Swoole-1.7.22 版本已发布,修复PHP7相关问题
2015/12/31 PHP
简单谈谈PHP中strlen 函数
2016/02/27 PHP
PHP编写学校网站上新生注册登陆程序的实例分享
2016/03/21 PHP
Yii2使用$this->context获取当前的Module、Controller(控制器)、Action等
2017/03/29 PHP
javascript编程起步(第五课)
2007/01/10 Javascript
基于jquery的使ListNav兼容中文首字拼音排序的实现代码
2011/07/10 Javascript
jquery focus(fn),blur(fn)方法实例代码
2011/12/16 Javascript
javascript获取ckeditor编辑器的值(实现代码)
2013/11/18 Javascript
form表单action提交的js部分与html部分
2014/01/07 Javascript
JS中怎样判断undefined(比较不错的方法)
2014/03/27 Javascript
你未必知道的JavaScript和CSS交互的5种方法
2014/04/02 Javascript
jquery在ie7下选择器的问题导致append失效的解决方法
2016/01/10 Javascript
JavaScript事件学习小结(一)事件流
2016/06/09 Javascript
实例讲解JavaScript中的this指向错误解决方法
2016/06/13 Javascript
EditPlus 正则表达式 实战(3)
2016/12/15 Javascript
jQuery+Ajax请求本地数据加载商品列表页并跳转详情页的实现方法
2017/07/12 jQuery
原生js实现简单的模态框示例
2017/09/08 Javascript
在 Node.js 中使用原生 ES 模块方法解析
2017/09/19 Javascript
Vue.js递归组件构建树形菜单
2017/12/24 Javascript
解决Vue中mounted钩子函数获取节点高度出错问题
2018/05/18 Javascript
解析vue路由异步组件和懒加载案例
2018/06/08 Javascript
[02:43]DOTA2英雄基础教程 德鲁伊
2014/01/13 DOTA
python创建线程示例
2014/05/06 Python
python求解水仙花数的方法
2015/05/11 Python
Python 文件管理实例详解
2015/11/10 Python
Python在信息学竞赛中的运用及Python的基本用法(详解)
2017/08/15 Python
Python实现合并两个有序链表的方法示例
2019/01/31 Python
Linux上使用Python统计每天的键盘输入次数
2019/04/17 Python
利用python-docx模块写批量生日邀请函
2019/08/26 Python
PyTorch之nn.ReLU与F.ReLU的区别介绍
2020/06/27 Python
详解numpy1.19.4与python3.9版本冲突解决
2020/12/15 Python
在canvas上实现元素图片镜像翻转动画效果的方法
2018/03/20 HTML / CSS
欧洲最大的拼图游戏商店:JigsawPuzzle.co.uk
2018/07/04 全球购物
教师竞聘上岗演讲稿
2014/09/03 职场文书
五星级酒店宣传口号
2015/12/25 职场文书