这里阅读的php版本为PHP-7.1.0 RC3,阅读代码的平台为linux

ZTS

我们会看到文章中有很多地方是:

#ifdef ZTS
# define CG(v) ZEND_TSRMG(compiler_globals_id, zend_compiler_globals *, v)
#else
# define CG(v) (compiler_globals.v)
extern ZEND_API struct _zend_compiler_globals compiler_globals;
#endif

这里的ZTS是个什么概念呢。我们经常使用的php都是运行在单进程,单线程环境,比如cgi,都是一个请求进来,就一个进程为它服务,当请求结束了,进程也就结束了。所以比如像全局变量,php内核就没有考虑多线程同时修改获取的时候线程安全问题。后来,php渐渐也在往单进程多线程服务器方向发展。那么这个时候,就会需要有一个层来专门处理线程安全问题。这个就是TSRM(Thread Safe Resource Management)。

但是php默认是关闭线程安全的。在编译的时候,你可以指定参数开启编译一个线程安全版本的php。(--enable-maintainer-zts 选项, Windows 平台为 --enable-zts)这个就是这里的ZTS的由来。

比如上面的例子,CG(V) 在非线程安全下获取的是全局结构compiler_globals结构的v属性,在线程安全下获取的是通过ZEND_TSREMG方法来获取。

zend_try

我们会看到zend_try_catch相关的代码如下:

    zend_try {
...exec_try
} zend_catch {
...exec_catch
} zend_end_try();

把宏展开,我们可以看到大概代码如下:

{                                                            \
JMP_BUF *__orig_bailout = EG(bailout); \
JMP_BUF __bailout; \
\
EG(bailout) = &__bailout; \
if (SETJMP(__bailout)==0) {
{
...exec_try
}
} else { \
EG(bailout) = __orig_bailout;
{
...exec_catch
}
} \
EG(bailout) = __orig_bailout; \
}

这个是什么意思呢,需要先理解下setjmp和longjmp,这两个函数是linux提供的方法。他们是组合起来使用的,达到协同程序的功能

#include <stdio.h>
#include <setjmp.h>
jmp_buf env;
void foo() {
printf("before jmp\n");
int ret = setjmp(env);
if(ret == 0) {
return;
} else {
printf("return %d\n", ret);
}
printf("after jmp\n");
}
int main(int argc, char* argv[]) {
foo();
longjmp(env, 999);
return 0;
} // 输出:
/*
before jmp
return 999
after jmp
*/

上面的这个例子,setjmp的时候相当于程序片段1把主动权交出来,然后执行if(ret == 0)下面的程序,直到遇到longjmp,把执行权还给了片段1,并且设置jmp_buf为999,片段1继续执行,发现了ret!=0,就输出return 999。

好了,回到这个程序:

{                                                            \
JMP_BUF *__orig_bailout = EG(bailout); \
JMP_BUF __bailout; \
\
EG(bailout) = &__bailout; \
if (SETJMP(__bailout)==0) {
{
...exec_try
}
} else { \
EG(bailout) = __orig_bailout;
{
...exec_catch
}
} \
EG(bailout) = __orig_bailout; \
}

这个程序里面的exec_try代码段里面,在遇到错误的时候,需要返回的时候,就会包含一个longjmp函数的调用。这样,就形成了我们平时调用try...catch...finnal的功能:

1 先保存全局变量里面的bailout

2 使用setjmp来做跳转执行下面的程序

3 执行exec_try

4 如果exec_try这个代码段里面有longjmp,并且longjmp返回非0(一般也确实非0),就执行exec_catch

5 最后,把全局变量里面的bailout恢复

这里可能会有两个疑惑,如果exec_try里面没有longjmp怎么办,那就直接只执行了exec_try,就跳过exec_catch了。这个也是标准的用setjmp和longjmp实现try catch的写法。

这两个的实现弥补了goto关键字只能在函数内部进行跳转的限制。这个叫做“长跳转”。

所以在PHP代码中,如果你执行的函数有可能抛出异常。不妨使用这个方式把你要执行的程序放在里面。

参考

http://blog.lucode.net/skills/talk-about-setjmp-and-longjmp.html

php内核分析(二)-ZTS和zend_try的更多相关文章

  1. Linux内核启动代码分析二之开发板相关驱动程序加载分析

    Linux内核启动代码分析二之开发板相关驱动程序加载分析 1 从linux开始启动的函数start_kernel开始分析,该函数位于linux-2.6.22/init/main.c  start_ke ...

  2. Linux内核分析(二)----内核模块简介|简单内核模块实现

    原文:Linux内核分析(二)----内核模块简介|简单内核模块实现 Linux内核分析(二) 昨天我们开始了内核的分析,网上有很多人是用用源码直接分析,这样造成的问题是,大家觉得很枯燥很难理解,从某 ...

  3. “Linux内核分析”实验二报告

    张文俊 + 原创作品转载请注明出处 + <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 一.第二周学习内 ...

  4. Linux内核分析 笔记二 操作系统是如何工作的 ——by王玥

    一.知识要点 1.计算机是如何工作的?(总结)——三个法宝 存储程序计算机工作模型,计算机系统最最基础性的逻辑结构: 函数调用堆栈,高级语言得以运行的基础,只有机器语言和汇编语言的时候堆栈机制对于计算 ...

  5. Linux内核分析作业二

    贾瑗 + 原创作品转载请注明出处 + <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000  一.操作系统是如 ...

  6. 【MOOC EXP】Linux内核分析实验二报告

    程涵  原创博客 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000  [操作系统是如何工作的]   教学内 ...

  7. 《Linux及安全》期中总结&《Linux内核分析》期终总结

    [5216 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000] WEEK NINE ...

  8. Linux内核分析 第二周

    Linux内核分析——完成一个简单的时间片轮转多道程序内核代码 张潇月+<Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-100 ...

  9. Linux内核分析——汇编代码执行及堆栈变化

    张潇月<Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 一.实验步骤 首先借助实验楼这个平台进入Linux ...

  10. 《Linux内核分析》期中总结

    两个月Linux内核的学习,让我理解了Linux内核的基本工作原理,包括进程管理.内存管理.设备驱动.文件系统,从分析内核到了解整个系统是如何工作的.如何控制管理资源分配.进程切换并执行.各种策略和结 ...

随机推荐

  1. WPF 微信 MVVM 【续】发送部分QQ表情

    今天主要记录的就是发送QQ表情, WPF 微信 MVVM里写了,后期为了发送QQ表情,需要把TextBox替换为RichTextBox,接下来就说说替换的过程. 一.支持Binding的RichTex ...

  2. python与c互相调用

    虽然python开发效率很高,但作为脚本语言,其性能不高,所以为了兼顾开发效率和性能,通常把性能要求高的模块用c或c++来实现或者在c或c++中运行python脚本来处理逻辑,前者通常是python中 ...

  3. vs2010静态链接MFC库报链接错误

    由于需要将MFC程序在其它电脑上运行,所以需要将动态链接的MFC改成静态链接,本以为很简单,没想到链接的时候出现下面的链接错误: uafxcw.lib(afxmem.obj) : error LNK2 ...

  4. 玩转Vim 编辑器

    一:VIM快速入门 1.vim模式介绍 以下介绍内容来自维基百科Vim 从vi演生出来的Vim具有多种模式,这种独特的设计容易使初学者产生混淆.几乎所有的编辑器都会有插入和执行命令两种模式,并且大多数 ...

  5. 从啥也不会到可以胜任最基本的JavaWeb工作,推荐给新人的学习路线(二)

    在上一节中,主要阐述了JavaScript方面的学习路线.先列举一下我朋友的经历,他去过培训机构,说是4个月后月薪过万,虽然他现在还未达到这个指标. 培训机构一般的套路是这样:先教JavaSE,什么都 ...

  6. Python 正则表达式入门(中级篇)

    Python 正则表达式入门(中级篇) 初级篇链接:http://www.cnblogs.com/chuxiuhong/p/5885073.html 上一篇我们说在这一篇里,我们会介绍子表达式,向前向 ...

  7. Salesforce开发者学习笔记之一:基本知识

    本文介绍了Salesforce开发平台的基本知识, 包括如下内容: Salesforce平台介绍 Salesforce基本术语 定制和扩展Salesforce平台 创建一个简单的应用程序 Salesf ...

  8. hadoop 2.4 遇到的问题

    不管出什么问题,首先查看日志. 在启动过hadoop的前提下,打开浏览器,输入http://localhost:50070 点击Utilities下的logs,选择hadoop-root-datano ...

  9. .NET Core dotnet 命令大全

    dotnet 命令大全,让你理解dotnet 命令. 本文将以一个实例串起 dotnet 所有命令,让你玩转dotnet 命令. 本篇文章编写环境为windows 10 ,dotnet 命令同样适用于 ...

  10. ENode简介与各种教学视频资源汇总(要进群这篇文章必看)

    ENode是什么 ENode是一个.NET平台开源的应用开发框架,为开发人员提供了一套完整的基于DDD+CQRS+ES+(in-memory)+EDA架构风格的解决方案. ENode的特色是什么 解决 ...