PHP TSRM

前言

昨天经过一天的学习,终于对PHP的TSRM有了初步了解,及时整理一下,加深理解。

主要参考文章

深入理解PHP内核
鸟哥的博客

关于PHP TSRM

这是啥

Thread Safe Resource Manager 线程安全的资源管理器

用来干嘛的

一些Global变量,在多线程的模型下,不同的线程会共享同一个进程的内存空间,也就是多个线程会共用一个全局变量。

也就是说『不是线程安全的』。

为了解决这个问题,咱们就引入了TSRM,给每一个新创建的线程,都整一份全局变量的副本。就是这么简单的核心思想。请记住我们的这个目标,后面的事情都是为了实现这个目标而已

(原谅我看了一天晕头转向没抓住重点)

怎么实现的

先直接看图:
来自深入了解PHP内核的网站截图
首先,看黄色注释。

这是两个线程、三个模块(我理解为全局变量,后面就都说全局变量了)的例子。

然后,这里面有关键的数据结构:

id_count

让我们记住一个关键的静态数据:id_count。表示当前有多少个全局变量。

resource_types_table

这是一个数组,数组的每i项,存储第i个全局变量的类型信息。

不同的数据类型的大小resource_types_table[i].size,构造函数resource_types_table[i].ctor,析构函数resource_types_table[i].dtor都不一样。

tsrm_tls_table

请记住这是一个哈希表。
干啥用的?用来真正存储每个线程的『全局变量副本』(请回想这个TSRM是为了干嘛!就是为了给每个线程存储副本!)。

再看这个哈希表怎么做的里面每个元素(Bucket)是tsrm_tls_entry,这个哈希表用链表法解决冲突(每个tsrm_tls_entrynext指针),哈希函数是用线程idthread_id对表大小取模计算的坑位。

接下来我们就可以看每个元素tsrm_tls_entry

tsrm_tls_entry

真正存储数据的是结构体里的storage

void **storage;// 本节点的全局变量数组

这是一个数组,数组里的每一项是个void *指针,指啥都行(实际上就是要指向这个全局变量的副本!再回忆我们的目标是存副本!)

实际上数组的第i项就是存的当前线程的第i个全局变量。

到这里实际上我们就实现了我们的目标『存副本』了!

角色扮演

为了加深理解,现在我们假装自己是一个线程,我的线程id是10,我现在要找到属于我自己的,id1的全局变量。

  1. 通过我自己的线程id : 10, 我可以通过哈希函数算出我自己在哈希表tsrm_tls_table中的槽位,假设表大小是10,取模正好是0,于是我的槽位是0。然后通过链表解决冲突的方法,沿着链表一个个遍历,发现tsrm_tls_entry->thread_id == 10,终于找到了属于我的节点。
  2. 然后我们再提取tsrm_tls_entry->storage[1](id为1的全局变量存在storage[1]里)
    注:这里面看源码实际上还会有些细节的处理,不是直接用id在storage里找,不过整体思路是一样的。有兴趣的童鞋可以看下源码。
    TSRM_SAFE_RETURN_RSRC(thread_resources->storage, id, thread_resources->count);

3.void ***tslm_ls就是tslm_tls_entry->storage[i]

这里面不知道为啥要void ***这么多星 求高手指教

全流程走一遍

这是array这个扩展从初始化开始的全流程
在这里插入图片描述

1.声明array这个扩展的全局变量

ZEND_DECLARE_MODULE_GLOBALS(array)
按照宏定义

#define ZEND_DECLARE_MODULE_GLOBALS(module_name)							\
	zend_##module_name##_globals module_name##_globals

展开后就是声明这个玩意儿:

zend_array_globals array_globals

2.初始化

在PHP模块初始化MINIT中会调用

ZEND_INIT_MODULE_GLOBALS(array, php_array_init_globals, NULL);

展开就是

ts_allocate_id(&array_globals_id, sizeof(zend_array_globals), (ts_allocate_ctor) globals_ctror, (ts_allocate_dtor) globals_dtor)

也就是说,会在MINIT阶段,调用这个函数ts_allocate_id(详见PHP内核)。

3.线程获取变量的地址

通过ARRAYG(v)获取

#ifdef ZTS
#define ARRAYG(v) TSRMG(array_globals_id, zend_array_globals *, v)
#else
#define ARRAYG(v) (array_globals.v)
#endif
#define TSRMG(id, type, element) (((type) (*((void ***) tsrm_ls))[TSRM_UNSHUFFLE_RSRC_ID(id)])->element)

展开后是

(((zend_array_glovals *) (*((void ***) tsrm_ls))[TSRM_UNSHUFFLE_RSRC_ID(array_globals_id)])->v)

解读(请看清楚括号关系):在tsrm_ls这个表里取第array_globals_id项,强制转换成一个zend_array_glovals *指针,指向这个结构体,然后取里面的成员v

感想

1.读各种宏真的太累了
2.纪念第一次如此认真看源码(同时对参考的两篇好文章深入地读)
3.但行好事,莫问前程

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值