PHP扩展开发(七)Zend 线程安全

在PHP诞生的初期,它总是以单线程的CGI方式运行的,因此,根本不需要考虑多线程问题,因为进程的处理不会超过单个请求。

我们可以在内部的全局空间声明一个全局变量,并且可以随时访问或者修改该变量的值,而不用担心该变量是否是还没有定义。

在每个请求的CGI处理结束的时候,所有的资源都会被自动的释放。

后来,PHP嵌入到了多进程的web服务器,比如Apache之中运行,这时,因为请求到来时, 每个进程每次都会有一个独立的进程空间,如果在请求开始的时候进行了合适的初始化了, 在请求结束的时候进行了对应的清理工作,给定的内部变量或者全局变量依然可以被安全的访问。当然,在这时候, 需要添加对每个请求的内存管理,以防止资源泄漏导致服务器失控。

随着单进程多线程的web服务器的出现,我们急需要一种能够处理全局数据的方式,最终产生了TSRM(线程安全资源管理)。

线程安全和非线程安全声明

在非多线程的应用中,你可以在源文件的最顶部声明全局变量,编译器将会为应用中的数据分配内存空间块。

在多线程环境中,每个应用都需要他们自己的数据元素,对每个线程,都需要分配相隔离的内存块, 在需要访问数据的时候,给定的线程将会访问正确的内存块。

线程安全数据池

在扩展的 MINIT阶段,TSRM层将会通知扩展哪些数据需要使用一个或者多个ts_allocate_id()函数进行存储。 TSRM adds that byte count to its running total of data space requirements, and returns a new, unique identifier for that segment's portion of the thread's data pool.

typedef struct {
    int sampleint;
    char *samplestring;
} php_sample_globals;
int sample_globals_id;
PHP_MINIT_FUNCTION(sample)
{
    ts_allocate_id(&sample_globals_id,
        sizeof(php_sample_globals),
        (ts_allocate_ctor) php_sample_globals_ctor,
        (ts_allocate_dtor) php_sample_globals_dtor);
    return SUCCESS;
}

当一个需要访问这些数据段的请求到来的时候,扩展会从TSRM层请求一个当前线程资源池中 根据ts_allocate_id()函数返回的资源ID做一定偏移量的指针。

换句话说,在之前的模块相关的MINIT代码段中,你可能看到类似于SAMPLE_G(sampleint) = 5;这样的代码。 在线程安全方式的构建中,该短代码进行宏展开之后是如下代码:

(((php_sample_globals*)(*((void ***)tsrm_ls))[sample_globals_id-1])->sampleint = 5;

如果看不懂上面这段代码的话,请无视它,因为该部分已经整合到PHPAPI中,因此,大部分开发者可以不用知道它是如何工作的。

当在非多线程环境中

因为多线程的构建环境下,对于全局变量的访问,增加了很多从线程数据池中检索数据的步骤,与非多线程环境相比, 执行效率会产生一定的影响。

再次思考之前的程序,这次在单线程环境中构建:

typedef struct {
    int sampleint;
    char *samplestring;
} php_sample_globals;
php_sample_globals sample_globals;
PHP_MINIT_FUNCTION(sample)
{
    php_sample_globals_ctor(&sample_globals TSRMLS_CC);
    return SUCCESS;
}

可以看到,与之前需要先创建线程资源的ID,然后使用SAMPLE_G(sampleint) = 5;对数据进行访问不同, 这里可以直接使用sample_globals.sampleint = 5;,相比而言,代码更加简单,快速,高效。

单线程的构建中,因为进程是相互隔离的,如果一个进程进入了非预期的死循环,这样就不会导致整个web服务器挂掉。 Apache的MaxRequestsPerChild指令也是按照这种思路,经常故意的结束子进程然后重新创建以避免进程出现异常的。

封装全局访问

在创建扩展的时候,你不需要知道环境是否是线程安全的。幸运的是,你将会使用的大部分包含的文件中, 都会使用ZTS预处理指令。当PHP构建在线程安全的环境中时,或者是SAPI需要,或者是启用了maintainer-zts 选项,将会自动定义ZTS宏,程序中使用#ifdef ZTS指令检测是否定义了ZTS

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏测试开发架构之路

nginx配置文件nginx.conf超详细讲解

#nginx进程,一般设置为和cpu核数一样 worker_processes 4;                         #错误日志存放目录 er...

42340
来自专栏沈唁志

关于 64 位 PHP 使用 PHP_INT_SIZE 输出得到 4 的问题

很尴尬,为什么phpinfo()中的 PHP 是 64 位,而获取PHP_INT_SIZE的值确是 4

19910
来自专栏视频转码加密

视频转码加密 一直失败找不到原因 ,请求帮忙

echo $str = "https://vod.api.qcloud.com/v2/index.php?fileId=".$fileid."&transcod...

8600
来自专栏bisal的个人杂货铺

dbms_xplan.display_awr方式获取执行计划的实验和之前的误导

《查看Oracle执行计划的几种常用方法-系列1》(http://blog.csdn.net/bisal/article/details/38919181)这篇...

15650
来自专栏信安之路

代码审计之 UsualToolCMS

写到一半全部删了,觉得自己还不够经验写这样的东西,以免自己的文章对各位大佬带来误导。

22120
来自专栏沈唁志

怎么快速判断 PHP 是 32 位还是 64位

Swoole Compiler 加密是不支持使用 Debug 版本或 32 位的 PHP 的

61220
来自专栏horstxu的博客

深入理解Laravel定时任务调度机制

一个复杂的web系统后台当中,一定会有很多定时脚本或者任务要跑。例如爬虫系统需要定期去爬取一些网站数据,自动还贷系统需要每个月定时对用户账户扣款结算,会员系统需...

2.4K120
来自专栏网站漏洞修补

网站被黑提醒该站点可能受到黑客攻击,部分页面已被非法篡改

大清早的一上班收到3个网站客户的QQ联系,说是自己公司的网站被跳转到了北京sai车,cai票,du博网站上去了,我们SINE安全公司对3个网站进行了详细的安全检...

56110
来自专栏机器人课程与技术

专业工具软件课程说明与资料下载

分为2.0学分和1.5学分课程:2.0学分包括Matlab、Protel和AutoCAD;1.5学分包括Protel DXP。

8440
来自专栏有困难要上,没有困难创造困难也要上!

使用Web Console提供一个简单的远程命令行服务

今天调研基于Web的SSH的应用程序的时候无意间看到了一个Web Console的工具,虽然没有满足我的最终需求,但还是试了一下,可以当作一个受限的基于Web远...

23360

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励