forked from walu/phpbook
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
104 changed files
with
3,149 additions
and
2,914 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
# 1.1 PHP的生命周期 | ||
|
||
我们平时接触的最多的是web模式下的php,当然你也肯定知道php还有个CLI模式。其实无论哪种模式,PHP的工作原理都是一样的,都是作为一种SAPI在运行(Server Application Programming Interface: the API used by PHP to interface with Web Servers)。当我们在终端敲入php这个命令时候,它使用的是"command line sapi"!它就像一个mimi的web服务器一样来支持php完成这个请求,请求完成后再重新把控制权交给终端。 | ||
SAPI的介绍: [PHP的SAPI是个么东东](http://www.laruence.com/2008/08/12/180.html) | ||
|
||
|
||
## links | ||
* 1 [PHP的生命周期](<1.md>) | ||
* 1.2 [PHP的启动与终止</title>](<1.2.md>) | ||
|
||
## LastModified | ||
* $Id$ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
# 1.2 PHP的生命周期 | ||
|
||
PHP程序的启动可以看作有两个概念上的启动,终止也有两个概念上的终止。其中一个是PHP作为Apache(拿它举例,板砖勿扔)的一个模块的启动与终止,这次启动php会初始化一些必要数据,比如与宿主Apache有关的,<b>并且这些数据是常驻内存的!</b>,终止与之相对。还有一个概念上的启动就是当Apache分配过一个页面请求来的时候,PHP会有一次启动与终止,这也是我们最常讨论的一种。 | ||
现在我们主要来看一个PHP扩展的生命旅程是怎样走完这四个过程的。 | ||
在最初的初始化时候,就是PHP随着Apache的启动而诞生在内存里的时,它会把自己所有已加载扩展的MINIT方法(俗称Module Initialization,模块初始化?其实是个函数)都执行一遍。在这个时间里,扩展可以定义一些自己的常量、类、资源等所有会被用户端的PHP脚本用到的东西。但你要记住,这里定义的东东都会随着Apache常驻内存,可以被所有请求使用,直到Apache卸载掉PHP模块! | ||
内核中预置了PHP_MINIT_FUNCTION宏函数,来帮助我们实现这个功能: | ||
````c | ||
//抛弃作者那个例子,书才看两页整那样的例子太复杂了! | ||
//walu是我扩展的名称 | ||
int time_of_minit;//在MINIT()中初始化,在每次页面请求中输出,看看是否变化 | ||
PHP_MINIT_FUNCTION(walu) | ||
{ | ||
time_of_minit=time(NULL);//我们在MINIT启动中对他初始化 | ||
return SUCCESS;//返回SUCCESS代表正常,返回FALIURE就不会加载这个扩展了。 | ||
} | ||
|
||
```` | ||
当一个页面请求到来时候,PHP会打了鸡血似的马上开辟一个新的环境,并重新扫描自己的各个扩展,挨个执行它们各自的RINIT方法(俗称Request Initialization),这时候一个扩展可能会初始化自己扩展使用的变量啊,初始化等会用户端即PHP脚本中的变量啊之类的,内核预置了PHP_RINIT_FUNCTION()这个宏函数来帮我们实现这个功能: | ||
````c | ||
int time_of_rinit;//在RINIT里初始化,看看每次页面请求的时候变不。 | ||
PHP_RINIT_FUNCTION(walu) | ||
{ | ||
rinit=time(NULL); | ||
return SUCCESS; | ||
} | ||
```` | ||
好了,现在这个页面请求执行的差不多了,可能是顺利的走到了自己文件的最后,也可能是出师未捷,半道被用户给die或者exit了,这时候PHP便会启动回收程序,收拾这个请求留下的烂摊子。它这次会执行所有已加载扩展的RSHUTDOWN(俗称Request Shutdown)方法,这时候扩展可以抓紧利用内核中的变量表啊之类的做一些事情,因为一旦PHP把所有扩展的RSHUTDOWN方法执行完,便会释放掉这次请求使用过的所有东西,包括变量表的所有变量、所有在这次请求中申请的内存等等。 | ||
内核预置了PHP_RSHUTDOWN_FUNCTION宏函数来帮助我们实现这个功能 | ||
````c | ||
PHP_RSHUTDOWN_FUNCTION(walu) | ||
{ | ||
FILE *fp=fopen("time_rshutdown.txt","a+"); | ||
fprintf(fp,"%ld\n",time(NULL));//让我们看看是不是每次请求结束都会在这个文件里追加数据 | ||
fclose(fp); | ||
return SUCCESS; | ||
} | ||
|
||
```` | ||
前面该启动的也启动了,该结束的也结束了,现在该Apache老人家歇歇的时候,当Apache通知PHP自己要Stop的时候,PHP便进入MSHUTDOWN(俗称Module Shutdown)阶段。这时候PHP便会给所有扩展下最后通喋,如果哪个扩展还有未了的心愿,就放在自己MSHUTDOWN方法里,这可是最后的机会了,一旦PHP把扩展的MSHUTDOWN执行完,便会进入自毁程序,这里一定要把自己擅自申请的内存给释放掉,否则就杯具了。 | ||
内核中预置了PHP_MSHUTDOWN_FUNCTION宏函数来帮助我们实现这个功能: | ||
````c | ||
PHP_MSHUTDOWN_FUNCTION(walu) | ||
{ | ||
FILE *fp=fopen("time_mshutdown.txt","a+"); | ||
fprintf(fp,"%ld\n",time(NULL)); | ||
return SUCCESS; | ||
} | ||
```` | ||
这四个宏都是在walu.c里完成最终实现的,而他们的则是在/main/php.h里被定义的(其实也是调用的别的宏,本节最后我把这几个宏给展开了,供有需要的人查看)。 | ||
<b>好了,现在我们本节内容说完了,下面我们把所有的代码合在一起,并预测一下应该出现的结果:</b> | ||
````c | ||
//这些代码都在walu.c里面,不再.h里 | ||
|
||
int time_of_minit;//在MINIT中初始化,在每次页面请求中输出,看看是否变化 | ||
PHP_MINIT_FUNCTION(walu) | ||
{ | ||
time_of_minit=time(NULL);//我们在MINIT启动中对他初始化 | ||
return SUCCESS; | ||
} | ||
|
||
int time_of_rinit;//在RINIT里初始化,看看每次页面请求的时候变不。 | ||
PHP_RINIT_FUNCTION(walu) | ||
{ | ||
time_of_rinit=time(NULL); | ||
return SUCCESS; | ||
} | ||
|
||
PHP_RSHUTDOWN_FUNCTION(walu) | ||
{ | ||
FILE *fp=fopen("/cnan/www/erzha/time_rshutdown.txt","a+");//请确保文件可写,否则apache会莫名崩溃 | ||
fprintf(fp,"%d\n",time(NULL));//让我们看看是不是每次请求结束都会在这个文件里追加数据 | ||
fclose(fp); | ||
return SUCCESS; | ||
} | ||
|
||
PHP_MSHUTDOWN_FUNCTION(walu) | ||
{ | ||
FILE *fp=fopen("/cnan/www/erzha/time_mshutdown.txt","a+");//请确保文件可写,否则apache会莫名崩溃 | ||
fprintf(fp,"%d\n",time(NULL)); | ||
return SUCCESS; | ||
} | ||
|
||
//我们在页面里输出time_of_minit和time_of_rinit的值 | ||
PHP_FUNCTION(walu_test) | ||
{ | ||
php_printf("%d<br />",time_of_minit); | ||
php_printf("%d<br />",time_of_rinit); | ||
return; | ||
} | ||
|
||
|
||
```` | ||
<ul> | ||
<li>time_of_minit的值每次请求都不变。</li> | ||
<li>time_of_rinit的值每次请求都改变。</li> | ||
<li>每次页面请求都会往time_rshutdown.txt中写入数据。</li> | ||
<li>只有在apache结束后time_mshutdown.txt才写入有数据。</li> | ||
</ul> | ||
<div class="tip-common">多谢 [闸北陆小洪](http://weibo.com/showz) 指出的有关time_of_rinit的笔误。</div> | ||
上面便是PHP中典型的启动-终止模型,实际情况可能因为模式不同而有所变化,到底PHP的启动-终止会有多少中不同变化方式,请看下一节。 | ||
## links | ||
* 1.1 [1.1 让我们从SAPI开始](<1.1.md>) | ||
* 1.3 [PHP的生命周期](<1.3.md>) | ||
## LastModified | ||
* $Id$ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
# 1.3 PHP的生命周期 | ||
|
||
一个PHP实例,无论是从init脚本中调用的,还是从命令行启动的,都会向我们上一节说的那样,依次进行Module init、Request init、Request Shutdown、Module shutdown四个过程,当然之间还会执行脚本自己的逻辑。两种init和两种shutdown各会执行多少次、各自的执行频率有多少,这取决与PHP是用什么sapi与宿主通信的。最常见的四种方式如下所列: | ||
<ul> | ||
<li>直接以CLI/CGI模式调用</li> | ||
<li>多进程模块</li> | ||
<li>多线程模块</li> | ||
<li>Embedded(嵌入式,在自己的C程序中调用Zend Engine)</li> | ||
</ul> | ||
## 1、CLI/CGI时的周期 | ||
CLI和CGI的SAPI是相当特殊的,因为这时PHP的生命周期完全在一个单独的请求中完成。虽然简单,不过我们以前提过的两种init和两种shutdown仍然都会被执行。图1.1展示了PHP在这种模式下是怎么工作的。 | ||
<p style="text-align:center"><img src="image/01fig01.jpg" | ||
## 2、多进程模式下 | ||
<span class="ps">[ps:书是2006年出版的,所以你应该理解作者说多进程是主流]</span>PHP最常见的工作方式便是编译成为Apache2 的Pre-fork MPM或者Apache1 的APXS 模式,其它web服务器也大多用相同的方式工作,在本书后面,把这种方式统一叫做多进程方式。给它起这个名字是有原因的,不是随便拍拍屁股拍拍脑袋定下来的。当Apache启动的时候,会立即把自己fork出好几个子进程,每一个进程都有自己独立的内存空间,也就代表了有自己独立的变量、函数等。在每个进程里的PHP的工作方式如下图所示: | ||
<p style="text-align:center"><img src="image/01fig02.jpg" | ||
因为是fork出来的,所以各个进程间的数据是无法直接相互影响的,无法读也无法写<span class="ps">(ps:fork后可以用管道等方式实现进程间通信)</span>。它允许每个子进程几乎可以做任何事情,玩七十码、躲猫猫都没人管,办公室拿砍刀玩自杀也没事,但是人家不一样的是人家有个前提:不能影响其它进程的稳定!下图展示了从apache的视角来看多进程工作模式下的PHP: | ||
<p style="text-align:center"><img src="image/01fig03.jpg" | ||
## 3、多线程模式下 | ||
随着时代的进步,PHP越来越多的在多线程模式下工作,就像IIS的isapi和Apache MPM worker<span class="ps">(支持混合的多线程多进程的多路处理模块)</span>。在这种模式下,只有一个服务器进程在运行着,但会同时运行很多线程,这样可以减少一些资源开销,向Module init和Module shutdown就只需要运行一遍就行了,一些全局变量也只需要初始化一次,因为线程独具的特质,使得各个请求之间方便的共享一些数据成为可能。下图展示了在这种模式下PHP的工作流程: | ||
<p style="text-align:center"><img src="image/01fig04.jpg" | ||
## 4、Embed | ||
Embed SAPI是一种比较特殊的sapi,容许你在C/C++语言中调用PHP/ZE提供的函数。并且这种sapi和上面的三种一样,按Module Init、Request Init、Rshutdown、mshutdown的流程执行着。 当然,这只是其中一种情况。因为特定的应用由自己特殊的需求,只是在处理PHP脚本这个环节基本一致。 | ||
真正令emebed模式独特的是因为它可能随时嵌入到某个程序里面去(<span class="ps">比如你的test.exe里</span>),然后被当作脚本的一部分在一个请求的时候执行。控制权在PHP和原程序间来回传递。关于嵌入式的PHP在第20章会有应用,到时我们再用实例介绍这个不经常使用的sapi。 | ||
|
||
<aside> | ||
<hr /> | ||
关于Embed SAPI应用的文章 | ||
<ul> | ||
<li> [Laruence大哥的使用PHP Embed SAPI实现Opcodes查看器](http://www.laruence.com/2008/09/23/539.html) </li> | ||
</ul> | ||
</aside> | ||
|
||
|
||
## links | ||
* 1.2 [PHP的启动与终止</title>](<1.2.md>) | ||
* 1.4 [线程安全](<1.4.md>) | ||
|
||
## LastModified | ||
* $Id$ |
Oops, something went wrong.