PHP 内部结构:TSRMLS_FETCH 是如何工作的?
Posted
技术标签:
【中文标题】PHP 内部结构:TSRMLS_FETCH 是如何工作的?【英文标题】:PHP Internals: How does TSRMLS_FETCH Work? 【发布时间】:2018-09-07 10:53:58 【问题描述】:php Internals TSRMLS_FETCH
宏是如何工作的?
根据PHP Manual
在开发扩展时,构建包含“tsrm_ls 未定义”的错误或导致该结果的错误源于 TSRMLS 在当前范围内未定义的事实,要解决此问题,请使用适当的宏声明函数以接受 TSRMLS,如果相关函数的原型无法更改,您必须在函数体内调用 TSRMLS_FETCH。
我了解使用适当的宏声明函数接受 TSRMLS 意味着使用 TSRMLS_C, TSRMLS_D, TSRMLS_CC, and TSRMLS_DC 定义或调用具有额外参数/参数的函数。
但是,如果相关函数的原型不能更改,则必须在函数体中调用 TSRMLS_FETCH 让我有点困惑。如果我同时查看 php-src here 和 here TSRMLS_FETCH
似乎是一个空宏。
所以这给我留下了一个问题——TSRMLS_FETCH
是如何工作的?在编译时还有其他东西填充这个宏吗?
【问题讨论】:
【参考方案1】:首先,我不会过多关注手册中关于 PHP 内部的内容。它非常过时,很有可能在不久的将来从手册中删除。目前有两个专门介绍 PHP 内部的网站:PHPInternalsBook.com 和 PHPInternals.net(我为后者编写内容)。还有几个不错的博客可供关注,包括 Nikita's 和 Julien's。
PHP 5.x 系列中的 TSRM 非常具有侵入性。当想要从函数中访问任何Zend globals 时,选择是从函数调用中获取 TLS 内存指针(例如 pthread_getspecific
,这相对昂贵)或通过函数参数传播 TLS 内存指针(a混乱和容易出错的事情,但更快的方法)。您提到的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
和类似方法)对性能造成影响。
补充阅读:
Native TLS(在 PHP 7.0 中引入此更改的 RFC) Threads and PHP(Julien 在 TSRM 上的博文)【讨论】:
谢谢@tpunt!今年早些时候,我偶然发现了 PHPInternals.net,它是一股清新的空气。我也不知道 Nikita 或 Julien 的网站,所以他们会排在我阅读列表的首位。【参考方案2】:看看older versions of that file:
#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。
【讨论】:
啊哈!看起来使用/需要这些宏的最后一个 PHP 版本是 PHP 5.6 -- github.com/php/php-src/blob/PHP-5.6/TSRM/TSRM.h。谢谢,这就是我要找的答案!【参考方案3】:这段代码
#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;
【讨论】:
感谢您的回复,并为有关宏#ifdef
处理的有用一般信息 +1。我很感激。但是——在这两种情况下,TSRMLS_FETCH
宏都解析为空文本,所以这并不能直接回答我的问题。
我明白了,也许这个宏是在另一个include
或旧版本中定义的,这个宏会阻止在 ZEND_DEBUG 模式下使用代码。
这是第二个——旧版本的 PHP =7.0 不需要。看起来内部人员对它们进行了定义,以便他们和拥有多版本 PHP 扩展的人不需要进行大的重构。【参考方案4】:
在 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
【讨论】:
以上是关于PHP 内部结构:TSRMLS_FETCH 是如何工作的?的主要内容,如果未能解决你的问题,请参考以下文章