• (转)php的扩展和嵌入--php的生命周期与变量详述


    本文转自http://blog.csdn.net/cedricliang/article/details/17247749?9435;这是在我想在js的循环中加入一段php,这段php代码会在每次执行时引用一个全局变量并++,但是我发现这个全局变量每次输出的结果相同,没有想象中的++,因此学习了一下php的生命周期,转文详细说明了php的生命周期期,特此转载以备忘。

    首先开始介绍php的生命周期,了解一个php程序从开始运行到最后结束究竟经过怎么样的过程,对学习php和平时php开发应该是很重要的。
    
    起始和关闭阶段:
    
    对于php的起始和关闭阶段可以分成两层,
    第一层是php解释器作为一个整体进行结构和值的初始化过程。
    第二层则是在每一个页面的请求过程中。
    对于每个扩展而言,都会有一个初始化MINT函数,这个过程会声明变量、类,注册资源、流和过滤处理器,这些操作在所有的请求中都是存在的,所以可以称为是Persistent的。一般进行如下的两步操作:
    REGISTER_INI_ENTRIES()
    初始化模块的全局变量
    而在页面发出请求了之后,PHP则会建立一个包括符号表和配置值在内的操作环境,然后这次php解释器会再次循环每一个扩展,调用每个扩展的RINIT初始化函数。一般RINT函数的操作如下:
    把globalvalue设成默认值,这些全局变量往往是每个请求都需要的,但是针对每一个请求而言都是相互独立的。
    那些需要用到的变量需要放到符号表中以备调用。
    在这个函数中还可以记录一下请求的相关信息
    完成了请求的处理之后,如果到达了脚本尾部或通过die() exit()推出了,那么php通过调用RSHUTDOWN()开始清理
    这时候每个符号表中的变量都会被unset掉
    而当所有的请求都被满足了之后,就开始针对于模块的MSHUTDOWN过程
    调用UNREGISTER_INI_ENTRYES(),与MINIT函数的初始化过程相互对应。
    php程序执行的生命周期:
    要了解生命周期,就必须对不同的执行方式有所涉及。php可以有几种不同的执行方式,每种方式都有其特定的生命周期。
    
    CLI:这个是从命令行执行php程序,它的生命周期最为简单。比如在执行test.php的时候,就经历了如下的过程
    
       图1 CLI进行php执行的过程
    注意到MINIT RINIT RSHUTDOWN MSHUTDOWN都只被调用过一次,这个比较类似于瀑布式的结构。
    多线程的模块方式:这是最常用的一种方式,php作为APXS的模块对apache进行配合。在apache启动的时候会fork很多的子进程。针对于多个不同的请求,配合的是多个不同的初始化与结束过程。但是对每一个线程来说,只有一个MINIT和MSHUTDOWN的调用。而每个请求都对应着自己单独的RINIT和RSHUTDOWN。
    多线程的模块方式:采用多线程的方法可以避免不同的线程重复的调用MINIT/MSHUTDOWN.它具有的好处是多个请求可以共享信息,但是请求之间的隔离要求比较高,不然容易出现变量的访问出错。
    
    
    Zend 线程安全
    php对线程安全的处理有专门的机制,称为Thread Safe Resource Management(TSRM)。在进行线程安全和非线程安全声明的时候,明显有一些不同之处:
    
    线程安全的变量声明:
    typedef struct {
        int sampleint;
        char *samplestring;
    } php_sample_globals;
    int sample_globals_id;
    PHP_MINIT_FUNCTION(sample)
    {
           ts_allocate_id(&sample_globals_id,
            sizeof(php_sample_globals),
            (ts_allocate_ctor) php_sample_globals_ctor,
            (ts_allocate_dtor) php_sample_globals_dtor);
        return SUCCESS;
    }
    从这段代码中可以看到,在MINIT阶段,需要通过ts_allocate_id函数来通知TSRM这个程序需要多少空间,TSRM会增加当前的空间消耗,并返回一个id指向线程数据池的相应部分。
    而当请求要访问数据的时候,就首先从TSRM层找到当前线程的资源池的指针,然后加上ts_allocate_id()返回的资源id作为offset。
    非线程安全的变量声明:
    typedef struct {
        int sampleint;
        char *samplestring;
    } php_sample_globals;
    php_sample_globals sample_globals;
    PHP_MINIT_FUNCTION(sample)
    {
        php_sample_globals_ctor(&sample_globals TSRMLS_CC);
        return SUCCESS;
    }
    在非线程安全的情况下,只需要简单的声明变量,更加快速有效,数据的地址在编译阶段就可以确定,而不是像在线程安全的情况下需要运行时计算。同时它还有一个好处就是一段程序出了bug不会让整个webserver坏掉。因此在不需要用到线程安全的时候,还是应该尽量使用非线程安全声明方式。
    为了构建线程安全的php,必须在编译的时候加上--enable-maintainer-zts选项。而在程序中检测的时候可以用#ifdef ZTS,通过这种检测可以实现对全局变量声明的不同处理,入下例所示:
    #ifdef ZTS
    #define HELLO_G(v) TSRMG(hello_globals_id, zend_hello_globals *, v)
    #else
    #define HELLO_G(v) (hello_globals.v)
    #endif
    
    通过判断是否启动了线程安全,对变量进行了不同的声明和访问方式指定。
    在启用了线程安全之后,为了在不同的线程之间对数据进行区分,PHP会自动启用一个tsrm_ls指针。在这个指针的帮助下,各个线程的函数才能找到对应的符号表进行相应的变量读取操作。也正是这种机制使得多个线程一起工作的时候,数据空间不会乱掉。
    
    
    通过对php的起始和结束阶段、生命周期和Zend线程安全的机制的了解,有利于后续对php扩展编译的研究。
  • 相关阅读:
    POJ
    POJ-2253 Frogger(最短路)
    背包问题(转自背包九讲+对应题目)
    POJ-1860 Currency Exchange (最短路)
    Bellman-Ford 最短路径算法
    POJ-3295 Tautology (构造)
    POJ-2586 Y2K Accounting Bug 贪心
    POJ 2965 The Pilots Brothers' refrigerator (暴力枚举)
    python(pymysql操作数据库)
    python复习概念__oop中
  • 原文地址:https://www.cnblogs.com/jazzone/p/3472105.html
Copyright © 2020-2023  润新知