探究php底层运行机制
来源:网络 编辑:admin
概要
简介 先看看下面这个过程:
我们从未手动开启过PHP的相关进程,它是随着Apache的启动而运行的;
PHP通过mod_php5.so模块和Apache相连(具体说来是SAPI,即服务器应用程序编程接口);
PHP总共有三个模块:内核、Zend引擎、以及扩展层;
PHP内核用来处理请求、文件流、错误处理等相关操作;
Zend引擎(ZE)用以将源文件转换成机器语言,然后在虚拟机上运行它;
扩展层是一组函数、类库和流,PHP使用它们来执行一些特定的操作。比如,我们需要mysql扩展来连接MySQL数据库;
当ZE执行程序时可能会需要连接若干扩展,这时ZE将控制权交给扩展,等处理完特定任务后再返还;
最后,ZE将程序运行结果返回给PHP内核,它再将结果传送给SAPI层,最终输出到浏览器上。
深入探讨 等等,没有这么简单。以上过程只是个简略版,让我们再深入挖掘一下,看看幕后还发生了些什么。
Apache启动后,PHP解释程序也随之启动;
PHP的启动过程有两步;
第一步是初始化一些环境变量,这将在整个SAPI生命周期中发生作用;
第二步是生成只针对当前请求的一些变量设置。
PHP启动第一步 不清楚什么第一第二步是什么?别担心,我们接下来详细讨论一下。让我们先看看第一步,也是最主要的一步。要记住的是,第一步的操作在任何请求到达之前就发生了。
启动Apache后,PHP解释程序也随之启动;
PHP调用各个扩展的MINIT方法,从而使这些扩展切换到可用状态。看看php.ini文件里打开了哪些扩展吧;
MINIT的意思是“模块初始化”。各个模块都定义了一组函数、类库等用以处理其他请求。
一个典型的MINIT方法如下:PHP_MINIT_FUNCTION(extension_name){/* Initialize functions, classes etc */}PHP启动第二步
当一个页面请求发生时,SAPI层将控制权交给PHP层。于是PHP设置了用于回复本次请求所需的环境变量。同时,它还建立一个变量表,用来存放执行过程中产生的变量名和值。
PHP调用各个模块的RINIT方法,即“请求初始化”。一个经典的例子是Session模块的RINIT,如果在php.ini中启用了Session模块,那在调用该模块的RINIT时就会初始化$_SESSION变量,并将相关内容读入;
RINIT方法可以看作是一个准备过程,在程序执行之间就会自动启动。
一个典型的RINIT方法如下:PHP_RINIT_FUNCTION(extension_name) {/* Initialize session variables, pre-populate variables, redefine global variables etc */}PHP关闭第一步 如同PHP启动一样,PHP的关闭也分两步:
一旦页面执行完毕(无论是执行到了文件末尾还是用exit或die函数中止),PHP就会启动清理程序。它会按顺序调用各个模块的RSHUTDOWN方法。
RSHUTDOWN用以清除程序运行时产生的符号表,也就是对每个变量调用unset函数。
一个典型的RSHUTDOWN方法如下:PHP_RSHUTDOWN_FUNCTION(extension_name) {/* Do memory management, unset all variables used in the last PHP call etc */}PHP关闭第二步 最后,所有的请求都已处理完毕,SAPI也准备关闭了,PHP开始执行第二步:
PHP调用每个扩展的MSHUTDOWN方法,这是各个模块最后一次释放内存的机会。
一个典型的RSHUTDOWN方法如下:PHP_MSHUTDOWN_FUNCTION(extension_name) {/* Free handlers and persistent memory etc */} 这样,整个PHP生命周期就结束了。要注意的是,只有在服务器没有请求的情况下才会执行“启动第一步”和“关闭第二步”。
一、开篇
在开始这个专题之前,先说一点题外话。大多数人学习编程语言的时候,首先关注的是这种语言的语法及其常用函数。我学习C,Java,Php等语言就是按照这样的方式开始的。一般情况下,这个阶段需要一个月左右的时间就会完全掌握,并能基本熟练地使用。对于已有经验的同学,可能时间更短。其实各种语言的语法和常用函数都差别不大,有很多相通的地方。如果您在学习一种编程语言的时候,拿一些真正的项目任务作为实践,效果更佳,实践远胜于理论。
我们在掌握了一门编程语言之后,又会向两个方向发展:一个方向是向上延伸,从事系统框架结构的探索;另一方向是向下延伸,从事系统底层方面的研究,我大体画了一下这个学习演变过程的示意图。
注:虽然我的形象一直用着“高高手”,但我只是个菜鸟,如有雷同,纯属巧合,欢迎善意拍砖。
php的语法非常简单,正是它的简单性,使它成为了当前互联网第一编程语言。你不需要具备很多的知识就能上手,比如:你学习C语言,就必须非常了解各个变量如何定义,指针如何操作,内存如何创建销毁等等。再比如:你学习Java语言,就必须具有面向对象(OO)的基础,就必须清楚是什么时候需要封装,什么时候需要继承,什么时候需要多态,要做项目,怎么还得懂点SSH。Php的大部分使用者可能根本就没这么多讲究,有的人喜欢面向过程,那你就用面向过程的方式来写代码;有的人喜欢面向对象,那你就用面向对象的方式写代码。Php的产生缘于互联网,目前也是互联网Web2.0第一编程语言。满足用户需求永远是第一位的,可维护性暂且可以放在第二位。我们通常说Web应用永远是β版的,计划远没有变化快。
我们公司里有很多phper,我曾经问过他们:“php程序到底是如何被执行的?”,多数人似乎很难说得清楚。这种情况,其实并不奇怪,我曾经拿类似的问题问过Javaer,Javaer的回答也是如此。有的同学会问:“研究这样的问题有没有实际意义呢?”我说:“有!”。理解系统的底层,有助于你写出高效健壮的代码,你会更清楚程序的代码到底该怎么去写。另外,如果你有志去做php扩展,那就更不必说,责无旁贷。
要回答以上问题,我觉得最好的办法是阅读一下php的源码,从“根”上解决。近来我找了点时间,粗读了一遍,愿意与各位共享。
关于php的底层工作原理,一定绕不开webserver,象apache,lighttpd,nginx,iis等。我这里就选择apache为例吧。以下内容将结合apache的源码、工作原理和扩展来逐步切入php的解析过程。
二、Apache运行机制剖析
l B/S交互过程
浏览器(Browser)和服务器(Web Server)的交互过程:
1、 浏览器向服务器发出HTTP请求(Request)。
2、 服务器收到浏览器的请求数据,经过分析处理,向浏览器输出响应数据(Response)。
3、 浏览器收到服务器的响应数据,经过分析处理,将最终结果显示在浏览器中。
下图是一份浏览器请求数据和服务器响应数据的快照:
关于浏览器和服务器数据交互过程非常简单,很容易理解。我想从事Web开发的人员都很清楚,在此不再赘述,仅供参考。
l Apache概述
Apache是目前世界上使用最为广泛的一种Web Server,它以跨平台、高效和稳定而闻名。按照去年官方统计的数据,Apache服务器的装机量占该市场60%以上的份额。尤其是在X(Unix/Linux)平台上,Apache是最常见的选择。其它的Web Server产品,比如IIS,只能运行在Windows平台上,是基于微软.Net架构技术的不二选择。
Apache并不是没有缺点,它最为诟病的一点就是变得越来越重,被普遍认为是重量级的WebServer。所以,近年来又涌现出了很多轻量级的替代产品,比如lighttpd,nginx等等,这些WebServer的优点是运行效率很高,但缺点也很明显,成熟度往往要低于Apache,通常只能用于某些特定场合,。
l Apache组件逻辑图
Apache是基于模块化设计的,总体上看起来代码的可读性高于php的代码,它的核心代码并不多,大多数的功能都被分散到各个模块中,各个模块在系统启动的时候按需载入。你如果想要阅读Apache的源代码,建议你直接从main.c文件读起,系统最主要的处理逻辑都包含在里面。MPM(Multi -Processing Modules,多重处理模块)是Apache的核心组件之一,Apache通过MPM来使用操作系统的资源,对进程和线程池进行管理。Apache为了能够获得最好的运行性能,针对不同的平台(Unix/Linux、Window)做了优化,为不同的平台提供了不同的MPM,用户可以根据实际情况进行选择,其中最常使用的MPM有prefork和worker两种。至于您的服务器正以哪种方式运行,取决于安装Apache过程中指定的MPM编译参数,在X系统上默认的编译参数为prefork。由于大多数的Unix都不支持真正的线程,所以采用了预派生子进程(prefork)方式,象Windows或者Solaris这些支持线程的平台,基于多进程多线程混合的worker模式是一种不错的选择。对此感兴趣的同学可以阅读有关资料,此处不再多讲。Apache中还有一个重要的组件就是APR(Apache portable Runtime Library),即Apache可移植运行库,它是一个对操作系统调用的抽象库,用来实现Apache内部组件对操作系统的使用,提高系统的可移植性。Apache对于php的解析,就是通过众多Module中的php Module来完成的。
Apache的逻辑构成以及与操作系统的关系
l Apache的生命周期
这一节的内容会与php模块的载入有关,您可以略微关注一下。以下是Apache的生命周期(prefork模式)示意图。
l Apache的生命周期
这一节的内容将会阐述php模块的载入过程,请参考Apache的生命周期示意图(prefork模式下)。
Apache的运行分为启动阶段和运行阶段。
1. 启动阶段
在启动阶段,Apache主要进行配置文件解析(例如http.conf以及Include指令设定的配置文件等)、模块加载(例如mod_php.so,mod_perl.so等)和系统资源初始化(例