ZEND_TSRMLS_CACHE_UPDATE skeleton file中的php/ext指令做什么?

什么时候以及为什么需要它?

当另一个init。像PHP_MINIT_FUNCTION(%EXTNAME%)这样的函数被添加了,该函数的第一条语句也需要ZEND_TSRMLS_CACHE_UPDATE指令吗?

最佳答案

第一件事首先是您的问题不清楚,原因有两个:

  • 您必须提及您正在尝试了解ZTS,即Zend
    使用PHP的线程安全
  • 您进行文件编译或了解您的系统是什么
    要求,即您正在使用的操作系统以及所使用的线程类型。

  • 第二件事你还不了解MINIT和RINIT的区别首先让我们了解两者之间的区别

    MINIT与RINIT

    您必须了解C编程中的全局变量。假设您了解一些有关PHP扩展编译的知识。在PHP中,全局变量分为两种:

    1. True Globals / MINI

    True Globals是传统的C全局变量,因为它们在设计上不错,但无法在线程并发运行环境中得到保护。当PHP执行请求时,PHP允许他们阅读。可以在Threads环境内部或外部更改True Global变量。让我们看这个例子来进一步解释它:
    static int variable; /* true global */
    
    PHP_MINIT(my_ext) /* PHP Module initialization */
    {
            if (something()) {
                    variable = 3; /* writing to a true global */
            }
    }
    

    上面的代码说明了每个PHP扩展的外观。所谓的MINIT hooks是关于PHP扩展初始化的。在此步骤中,PHP正在启动,然后可以安全地写入或读取全局变量,如示例中所示。

    2.线程全局变量/ RINI

    由于创建线程可同时处理多个请求,因此在该线程内保持每个变量值的完整性非常重要,这样,值只能由该线程读取或写入。因此,线程请求的初始化是通过RINIT完成的。现在,相同的示例将如下所示:
    PHP_RINIT(my_ext) /* PHP Request initialization */
    {
            if (something()) {
                    MYEXT_G(variable) = 3; /* writing to a thread global */
            }
    }
    

    只有1个区别,即使用MYEXT_G宏,我将在此答案的后面解释。

    为了更好地了解事物,您可能还需要了解TSRM

    TSRM

    对于ZTS,设计为使用线程安全资源管理器层(我们称为TSRM层)。这只是一些普通的C代码,对于线程而言仅是一些理解事物所必需的解释。它启用了一些底层线程库:
  • Gnu便携式线程,
  • Posix线程,
  • 状态线程,
  • Win32线程或
  • BeThreads。

  • TSRM引导

    在TSRM引导PHP调用tsrm_startup()。作为PHP的开始,建立安全保护所需的线程或资源不会那么多。它将为要创建的每个线程准备一个具有检查表的表。此启动步骤也很重要,因为在这里,我们需要创建TLS密钥和同步所需的TLS互斥锁。
    static pthread_key_t tls_key;
    
    TSRM_API int tsrm_startup(int expected_threads,
                              int expected_resources, int debug_level, char *debug_filename)
    {
            pthread_key_create( &tls_key, 0 ); /* Create the key */
            ....
            ....
    }
    
    #define MUTEX_T pthread_mutex_t *
    
    TSRM_API MUTEX_T tsrm_mutex_alloc(void)
    {
            MUTEX_T mutexp;
            mutexp = (pthread_mutex_t *)malloc(sizeof(pthread_mutex_t));
            pthread_mutex_init(mutexp,NULL);
            return mutexp;
    }
    

    TSRM资源

    是时候添加新的资源线程了。内存区域有大小,并且需要一些初始化(构造函数)和反初始化(析构函数)。该存储区域(称为TSRM资源)将由TSRM层赋予唯一的资源ID。调用者应保存ID,因为需要从TSRM退还 protected 内存区域以读取或写入变量值。
    TSRM_API ts_rsrc_id ts_allocate_id(ts_rsrc_id *rsrc_id,
            size_t size, ts_allocate_ctor ctor, ts_allocate_dtor dtor)
    {
         ....
         //For allocation of memory saving constructor and destruction function
         // Saving Size of required memory allocated
         // Resources ID Returning back
    }
    

    根据要求启动

    在每个新请求的开始,都会调用ts_resource_ex()函数。此函数读取当前线程ID,并尝试获取为此线程分配的资源,也就是当前线程全局变量专用的内存区域。

    ZEND_TSRMLS_CACHE_UPDATE

    现在,您对有关ZEND_TSRMLS_CACHE_UPDATE的问题的回答。
    /* {{{ PHP_RINIT_FUNCTION
     */
    PHP_RINIT_FUNCTION(%EXTNAME%)
    {
    #if defined(ZTS) && defined(COMPILE_DL_%EXTNAMECAPS%)
        ZEND_TSRMLS_CACHE_UPDATE();
    #endif
    
        return SUCCESS;
    }
    /* }}} */
    

    这是宏扩展:
    #define ZEND_TSRMLS_CACHE_UPDATE() _tsrm_ls_cache = tsrm_get_ls_cache();
    

    对于pthread实现:
    #define tsrm_get_ls_cache pthread_getspecific(tls_key)
    

    最后,您应该更好地了解如何使用宏在扩展中访问全局变量:
    #ifdef ZTS
    #define MYEXT_G(v) (((MYEXT_globals *) (*((void ***) _tsrm_ls_cache))[((MYEXT_globals_id)-1)])->(v))
    

    使用MYEXT_G()宏访问全局变量,在使用线程环境时,它将扩展为使用此扩展名_tsrm_ls_cache的探测MYEXT_globals_id区域。

    10-05 20:49
    查看更多