php-src - ZEND_TSRMLS_CACHE_UPDATE - 它是什么以及何时需要它?

Mvo*_*sek 2 php c c++ windows thread-safety

骨架文件中的ZEND_TSRMLS_CACHE_UPDATE指令有什么作用?php/ext

何时以及为何需要它?

当另一个 init. PHP_MINIT_FUNCTION(%EXTNAME%)添加了类似函数,是否也ZEND_TSRMLS_CACHE_UPDATE需要将指令作为该函数中的第一条语句?

Vin*_*982 6

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

  1. 你必须提到你正在尝试理解 ZTS ie Zend Thread Safe with PHP

  2. 您文件编译或了解您的系统要求是什么,即您使用的操作系统以及您使用的线程类型。

第二件事你还没明白MINIT和RINIT的区别首先让我们了解一下两者的区别

MINIT 与 RINIT

您必须了解 C 编程中的全局变量。假设你对PHP的扩展编译有一定的了解。在 PHP 中,全局变量分为两种类型:

1. 真正的全球 / MINI

True Globals 是传统的 C 全局变量,因为它们在设计上还不错,但在 Threads 并发运行环境中无法受到保护。PHP 允许他们在 PHP 处理请求时进行读取。真正的全局变量可以在线程环境内部或外部更改。让我们看一个例子来进一步解释它:

static int variable; /* true global */

PHP_MINIT(my_ext) /* PHP Module initialization */
{
        if (something()) {
                variable = 3; /* writing to a true global */
        }
}
Run Code Online (Sandbox Code Playgroud)

上面的代码解释了每个 PHP 扩展的样子。所谓的 MINIT 钩子是关于 PHP 扩展初始化的。在这一步骤中,PHP 正在启动,然后可以安全地写入或读取全局变量,就像示例中所做的那样。

2. 线程全局变量/RINI

由于创建线程是为了同时处理多个请求,因此在该线程中保持每个变量值完整是非常重要的,这样值只能由该线程读取或写入。因此,线程请求的初始化是通过 RINIT 完成的。现在同样的例子将是这样的:

PHP_RINIT(my_ext) /* PHP Request initialization */
{
        if (something()) {
                MYEXT_G(variable) = 3; /* writing to a thread global */
        }
}
Run Code Online (Sandbox Code Playgroud)

只有 1 个不同之处,即使用 MYEXT_G 宏,我将在本答案稍后解释。

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

TSRM

因为 ZTS 是为使用线程安全资源管理器层而设计的,我们称之为 TSRM 层。这只是一些简单的 C 代码,对于线程而言,仅是一些理解事物所需的解释。它启用了一些低级线程库:

  1. Gnu 便携式线程,
  2. Posix 线程,
  3. 状态线程,
  4. Win32 线程或
  5. 线程。

TSRM 启动

AT 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;
}
Run Code Online (Sandbox Code Playgroud)

TSRM资源

是时候添加新的资源线程了。内存区域有大小,需要一些初始化(构造函数)和去初始化(析构函数)。该内存区域称为 TSRM 资源,将被 TSRM 层赋予一个唯一的资源 ID。调用者应该保存 ID,因为它需要从 TSRM 返回受保护的内存区域来读取或写入变量值。

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
}
Run Code Online (Sandbox Code Playgroud)

根据要求启动

在每个新请求的最开始,都会调用 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;
}
/* }}} */
Run Code Online (Sandbox Code Playgroud)

这是宏扩展:

#define ZEND_TSRMLS_CACHE_UPDATE() _tsrm_ls_cache = tsrm_get_ls_cache();
Run Code Online (Sandbox Code Playgroud)

对于 pthread 实现:

#define tsrm_get_ls_cache pthread_getspecific(tls_key)
Run Code Online (Sandbox Code Playgroud)

最后,您应该更好地了解现在如何使用宏在扩展中访问全局变量:

#ifdef ZTS
#define MYEXT_G(v) (((MYEXT_globals *) (*((void ***) _tsrm_ls_cache))[((MYEXT_globals_id)-1)])->(v))
Run Code Online (Sandbox Code Playgroud)

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