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