c语言 - PHP 内部:TSRMLS_FETCH如何工作?



PHP 内部TSRMLS_FETCH宏是如何完成工作的?

根据 PHP 手册

在开发扩展时,包含"tsrm_ls未定义"的构建错误或与此相关的错误源于 TSRMLS 在当前范围内未定义的事实,要解决此问题,请使用适当的宏声明函数以接受 TSRMLS,如果无法更改相关函数的原型,则必须在函数体内调用 TSRMLS_FETCH。

我知道声明函数以接受具有适当宏的 TSRMLS意味着使用 TSRMLS_C、TSRMLS_D、TSRMLS_CC 和 TSRMLS_DC 来定义调用具有额外参数/参数的函数。

但是,如果无法更改相关函数的原型,则必须在函数体内调用TSRMLS_FETCH这让我有点困惑。 如果我在这里和这里都看 php-src,TSRMLS_FETCH似乎是一个空的宏。

这就给我留下了一个问题——TSRMLS_FETCH是如何工作的? 在编译时是否还有其他东西填充了此宏?

首先,我不会太关注手册中关于 PHP 内部的内容。它已经过时了,并且很有可能在不久的将来从手册中删除。目前有两个网站专门介绍PHP的内部结构:PHPInternalsBook.com 和 PHPInternals.net(我为后者创作内容)。还有一些很好的博客可以关注,包括Nikita和Julien的。

PHP 5.x系列中的TSRM具有很强的侵入性。当想要从函数内部访问任何 Zend 全局变量时,选择是从函数调用中获取 TLS 内存指针(例如相对昂贵的pthread_getspecific)或通过函数参数传播 TLS 内存指针(一个混乱且容易出错的事情,但更快的方法)。您提到的TSRMLS_FETCH宏用于前一种方法。

在 PHP 7.x 中,传播 TLS 内存指针(通过TSRMLS_[D|C]C?宏)已被完全删除(尽管它们的宏仍然是为向后兼容而定义的 - 它们不会做任何事情)。现在访问 TSRM TLS 的首选方法是通过其静态缓存。这基本上只是一个线程局部全局变量,用于保存当前的 TLS 内存指针。

以下是相关的宏:

#define TSRMLS_CACHE _tsrm_ls_cache // the TLS global variable
#define TSRMLS_CACHE_DEFINE() TSRM_TLS void *TSRMLS_CACHE = NULL; // define it
#define TSRMLS_CACHE_UPDATE() TSRMLS_CACHE = tsrm_get_ls_cache() // update it - i.e. calls pthread_getspecific()
#define TSRMLS_CACHE_RESET()  TSRMLS_CACHE = NULL // reset it

使用上述宏确实需要特别注意适当地更新静态缓存(通常在扩展的GINIT阶段,有时是RINIT阶段)。但是,这是一种更简洁的方法,可以提供对 TLS 内存指针的访问,而不会通过函数参数传播它或始终获取它(通过pthread_getspecific和类似方式)的性能影响。

额外阅读:

  • 原生 TLS(在 PHP 7.0 中引入此更改的 RFC)
  • 线程和PHP(Julien关于TSRM的博客文章)

看看该文件的旧版本:

#define TSRMLS_FETCH()            void ***tsrm_ls = (void ***) ts_resource_ex(0, NULL)
#define TSRMG(id, type, element)  (((type) (*((void ***) tsrm_ls))[TSRM_UNSHUFFLE_RSRC_ID(id)])->element)
#define TSRMLS_D  void ***tsrm_ls
#define TSRMLS_DC , TSRMLS_D
#define TSRMLS_C  tsrm_ls
#define TSRMLS_CC , TSRMLS_C

似乎在某些时候,PHP 删除了对这些宏的支持,但将它们保留为空,以避免将外部代码拆分为两个版本,一个用于新 PHP,一个用于旧 PHP。

这段代码

#if ZEND_DEBUG
...
#else
#define TSRMLS_FETCH()
...
#endif

正在执行以下操作:

如果不处于调试模式,请更改对宏TSRMLS_FETCH()的每个调用,而不执行任何操作。

在此示例中:

#if 0
#define TSRMLS_FETCH() printf("Bla");
#else
#define TSRMLS_FETCH()
#endif
int main(void) 
{
TSRMLS_FETCH()
return 0;
}

cpp demo.c(预处理器输出)返回:

int main(void) 
{
return 0;
}

在 PHP 8 中(在我写这篇文章的那一刻在 Alhpa fase 中),很多这些宏已被删除。见 https://github.com/php/php-src/blob/master/UPGRADING.INTERNALS

c. The following things have been removed from TSRM:
- TSRMLS_DC
- TSRMLS_D
- TSRMLS_CC
- TSRMLS_C
- TSRMLS_FETCH
- TSRMLS_FETCH_FROM_CTX
- TSRMLS_SET_CTX
- tsrm_new_interpreter_context
- tsrm_set_interpreter_context
- tsrm_free_interpreter_context
- support for GNUPTH, SGI ST, and BETHREADS

最新更新