上次“闲言之PHP不支持多线程”时,提到了PHP的原生fork进程函数pcntl_fork,其手册中官方文档介绍如下:
pcntl_fork (PHP 4 >= 4.1.0, PHP 5, PHP 7)
"在当前进程当前位置产生分支(子进程),创建成功时,在父进程执行线程内返回产生的子进程的PID,在子进程执行线程内返回0,创建失败时,在父进程上下文返回-1,并且引发一个PHP错误。【tips: 子进程随时可通过getpid()以及getppid()获取自己和父进程的pid】
子进程创建成功后,父进程和子进程 都从fork的位置继续/开始向下执行。。。"
为了探究pcntl_fork对Web环境中对PHP解释执行的影响,我想有必要先了解下子进程和父进程在执行过程中有什么样的交互影响?
首先介绍下创建子进程的三种重要技术:
Fork,写时复制以及vfork技术的区别
转自:http://www.cnblogs.com/wanpengcoder/articles/5310331.html
在理解子进程和父进程的交互影响前,先要有一个实现上的意识即:进程——>虚拟地址空间——>物理地址空间。用户进程能感知的是进程的虚拟地址空间,而虚拟地址空间——>物理地址空间则是由底层的内核实现的。一个进程在地址空间上的表现形式就是:【主要部分】正文段、数据段、堆、栈,内核,内核会为这四部分分配相应的载体,即真正的物理存储。
Fork子进程
现有一个父进程P1,这是一个主体,虚拟地址空间与物理地址空间具有,现在在其虚拟地址空间(有相应的数据结构表示)上有:正文段,数据段,堆,栈这四个部分,相应的,内核要为这四个部分分配各自的物理块,即:正文段块,数据段块,堆块,栈块。至于内核如何分配,此处并不详述。
1. 现P1用fork函数为进程创建一个子进程P2,内核:i:复制P1的正文段,数据段,堆和栈四部分,注意其内容相同;ii:为这四个部分分配物理块;
P2:正文段->PI的正文段的物理块,即暂时不为P2分配正文段块;数据段->P2自己的数据段块(为其分配对应的块),堆->P2自己的堆块,栈->P2自己的栈块。如下图所示:同左到右大的方向箭头表示复制内容。
写时复制技术
内核只为新生成的子进程创建虚拟空间结构,它们复制于父进程的虚拟空间结构,但不为这些段分配物理内存,它们共享父进程的物理空间;当父子进程中有更改相应段的行为发生时,再为子进程相应的段分配物理空间;
vfork()
此做法更是凶残,内核连子进程的虚拟地址空间结构也不创建了,直接共享父进程的虚拟空间和父进程的物理空间。
书接上文,fork子进程除了正文段【或代码空间】共享外,其他的物理空间都是独立的,在操作系统眼里它们更像是“兄弟关系”,各分炉灶,至于运行的具体先后情况就要看操作系统的调度算法了,如果父子进程间需要通信,比如同步协调啦,那么就要牵涉到进程通信方式:管道,消息队列,信号量,共享内存以及套接字等,甚至比如pcntl类的posix信号量。
讲到这里,pcntl_fork不适合用在Web环境下的原因应该也就清晰了,个人猜测有如下几点:
1. 对于访问情况不确定的Web环境而言,访问量、高并发的情况尚未可知,fork进程数不好控制,总不能把服务器进程搞炸了吧;
2. 如若访问及进程数量可控,纯粹是为了提升响应效率而采用多进程,那么由于fork的子进程和父进程异步执行,如若没有进程间通信控制,“僵尸进程”的出现也就不言而喻了;
3. 保持简单,PHP之所以成功就是其简单的每一个请求一次加载所有资源,处理完就释放所有资源,并发上来后应该通过缓存解决问题,任何性能相关的计算,可以转接到更为合适的语言上,比如Java。