php die 跳转,php源码-die、exit函数执行过程

在php代码中我们会通过 die、exit函数来中断程序的执行,但是在fpm sapi模式下,这两个函数的执行并不会导致进程的退出,仅仅是结束当前request,这是如何做到的呢,要解答这个问题就要了解die、exit函数的执行原理

下面以exit为例说明, die函数和exit编译完后是同一个opcode

首先,我们定位下exit 的opcode handler函数, 不知道如何查找opcode handler的参考下这篇文章 php源码-如何查看opcode源码

exit对应的 opcode handler是 ZEND_EXIT_SPEC_UNUSED_HANDLER

static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_EXIT_SPEC_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)

{

USE_OPLINE

SAVE_OPLINE();

if (IS_UNUSED != IS_UNUSED) {

zval *ptr = NULL;

do {

if (Z_TYPE_P(ptr) == IS_LONG) {

EG(exit_status) = Z_LVAL_P(ptr);

} else {

if ((IS_UNUSED & (IS_VAR|IS_CV)) && Z_ISREF_P(ptr)) {

ptr = Z_REFVAL_P(ptr);

if (Z_TYPE_P(ptr) == IS_LONG) {

EG(exit_status) = Z_LVAL_P(ptr);

break;

}

}

zend_print_variable(ptr);

}

} while (0);

}

zend_bailout(); //关键所在

ZEND_VM_NEXT_OPCODE(); /* Never reached */

}

这个函数里只有一句关键的代码,就是zend_bailout(), zend引擎就是通过这个来实现程序的中断的

#define zend_bailout() _zend_bailout(__FILE__, __LINE__)

ZEND_API ZEND_COLD void _zend_bailout(char *filename, uint lineno) /* {{{ */

{

if (!EG(bailout)) {

zend_output_debug_string(1, "%s(%d) : Bailed out without a bailout address!", filename, lineno);

exit(-1);

}

CG(unclean_shutdown) = 1;

CG(active_class_entry) = NULL;

CG(in_compilation) = 0;

EG(current_execute_data) = NULL;

LONGJMP(*EG(bailout), FAILURE); //关键点在这里,

}

# define SETJMP(a) setjmp(a)

# define LONGJMP(a,b) longjmp(a, b)

可以看到 zend_bailout 实际上执行的是 longjmp 。setjmp,longjmp 是C语言提供的标准异常处理模式,实现程序跳转。

那么 LONGJMP(*EG(bailout), FAILURE) 最终是跳到哪里去了呢 ?

以fpm sapi为例, 在 fpm_main.c中可以找到 php_execute_script() 函数的调用, 这个函数在fpm模式下,每个request都会调用一次

PHPAPI int php_execute_script(zend_file_handle *primary_file){

zend_try {

...

zend_execute_scripts()

...

}zend_end_try();

}

这里的关键就是 zend_try、zend_catch、zend_end_try 这三个宏是zend提供的处理异常和 die、exit这种函数的方法,其中zend_try 中就调用了 SETJMP 来设置 "跳转位置"

还有一个zend_first_try, 这个和zend_try的唯一区别是多了EG(bailout)=NULL;这么一句, 一般这个只在sapi启动的最开始调用一次

//zend.h 文件

#define zend_try \

{ \

JMP_BUF *__orig_bailout = EG(bailout); \

JMP_BUF __bailout; \

\

EG(bailout) = &__bailout; \

if (SETJMP(__bailout)==0) {

#define zend_catch \

} else { \

EG(bailout) = __orig_bailout;

#define zend_end_try() \

} \

EG(bailout) = __orig_bailout; \

}

#define zend_first_try EG(bailout)=NULL; zend_try

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值