【问题标题】:PHP Internals: How does TSRMLS_FETCH Work?PHP 内部结构:TSRMLS_FETCH 是如何工作的?
【发布时间】: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 herehere TSRMLS_FETCH 似乎是一个空宏。

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

【问题讨论】:

    标签: php c macros php-internals


    【解决方案1】:

    首先,我不会过多关注手册中关于 PHP 内部的内容。它非常过时,很有可能在不久的将来从手册中删除。目前有两个专门介绍 PHP 内部的网站:PHPInternalsBook.comPHPInternals.net(我为后者编写内容)。还有几个不错的博客可供关注,包括 Nikita'sJulien'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 和类似方法)对性能造成影响。

    补充阅读:

    【讨论】:

    • 谢谢@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。

    【讨论】:

    【解决方案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
    

    【讨论】:

      猜你喜欢
      • 2022-01-06
      • 1970-01-01
      • 2012-01-24
      • 2018-05-08
      • 2014-08-24
      • 2011-02-01
      • 2019-12-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多