首先你的问题不清楚,原因有二:
您必须提到您正在尝试理解 ZTS,即 Zend
使用 PHP 实现线程安全
您的文件编译或了解您的系统是什么
要求,即您使用的操作系统以及您使用的线程类型。
第二件事你还没明白MINIT和RINIT的区别先让我们了解一下两者的区别
MINIT 与 RINIT
您必须了解 C 编程中的全局变量。假设你对 PHP 的扩展编译有一定的了解。在 PHP 中,全局变量分为两种类型:
1。 True Globals / MINI
真正的全局变量是传统的 C 全局变量,因为它们在设计上还不错,但在线程并发运行环境中无法保护。 PHP 允许他们在 PHP 处理请求时读取。真正的全局变量可以在 Threads 环境内部或外部进行更改。让我们看一个例子来进一步解释它:
static int variable; /* true global */
PHP_MINIT(my_ext) /* PHP Module initialization */
{
if (something()) {
variable = 3; /* writing to a true global */
}
}
上面的代码解释了每个 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 */
}
}
只有 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 中返回受保护的内存区域以读取或写入变量值。
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() 宏访问全局变量,当使用线程环境时,它将扩展以使用此扩展的 id 探测 _tsrm_ls_cache 区域:MYEXT_globals_id。