[php-src]理解Php内核中的函数

内容均以php-5.6.14为例.

一. 函数结构

内核中定义一个php函数使用 PHP_FUNCTION 宏 包装,扩展也不例外,该宏在 main/php.h 第343行定义;

有着一系列类似以 PHP 命名的 Zend 宏包装器,它们是:

/* PHP-named Zend macro wrappers */
/* 以PHP命名的Zend宏包装器 */

#define PHP_FN                  ZEND_FN
#define PHP_MN                  ZEND_MN
#define PHP_NAMED_FUNCTION      ZEND_NAMED_FUNCTION
#define PHP_FUNCTION            ZEND_FUNCTION             /* PHP_FUNCTION 就是 ZEND_FUNCTION */
#define PHP_METHOD              ZEND_METHOD

#define PHP_RAW_NAMED_FE ZEND_RAW_NAMED_FE
#define PHP_NAMED_FE    ZEND_NAMED_FE
#define PHP_FE          ZEND_FE
#define PHP_DEP_FE      ZEND_DEP_FE
#define PHP_FALIAS      ZEND_FALIAS
#define PHP_DEP_FALIAS  ZEND_DEP_FALIAS
#define PHP_ME          ZEND_ME
#define PHP_MALIAS      ZEND_MALIAS
#define PHP_ABSTRACT_ME ZEND_ABSTRACT_ME
#define PHP_ME_MAPPING  ZEND_ME_MAPPING
#define PHP_FE_END      ZEND_FE_END

#define PHP_MODULE_STARTUP_N    ZEND_MODULE_STARTUP_N
#define PHP_MODULE_SHUTDOWN_N   ZEND_MODULE_SHUTDOWN_N
#define PHP_MODULE_ACTIVATE_N   ZEND_MODULE_ACTIVATE_N
#define PHP_MODULE_DEACTIVATE_N ZEND_MODULE_DEACTIVATE_N
#define PHP_MODULE_INFO_N       ZEND_MODULE_INFO_N

#define PHP_MODULE_STARTUP_D    ZEND_MODULE_STARTUP_D
#define PHP_MODULE_SHUTDOWN_D   ZEND_MODULE_SHUTDOWN_D
#define PHP_MODULE_ACTIVATE_D   ZEND_MODULE_ACTIVATE_D
#define PHP_MODULE_DEACTIVATE_D ZEND_MODULE_DEACTIVATE_D
#define PHP_MODULE_INFO_D       ZEND_MODULE_INFO_D

/* Compatibility macros */
/* 兼容性宏 */
#define PHP_MINIT       ZEND_MODULE_STARTUP_N
#define PHP_MSHUTDOWN   ZEND_MODULE_SHUTDOWN_N
#define PHP_RINIT       ZEND_MODULE_ACTIVATE_N
#define PHP_RSHUTDOWN   ZEND_MODULE_DEACTIVATE_N
#define PHP_MINFO       ZEND_MODULE_INFO_N
#define PHP_GINIT       ZEND_GINIT
#define PHP_GSHUTDOWN   ZEND_GSHUTDOWN

#define PHP_MINIT_FUNCTION      ZEND_MODULE_STARTUP_D      /* 可用来定义模块初始时执行一些操作 */
#define PHP_MSHUTDOWN_FUNCTION  ZEND_MODULE_SHUTDOWN_D      /* 可用来定义模块卸载时执行一些操作 */
#define PHP_RINIT_FUNCTION      ZEND_MODULE_ACTIVATE_D      /* 可用来定义请求初始化时执行一些操作 */
#define PHP_RSHUTDOWN_FUNCTION  ZEND_MODULE_DEACTIVATE_D     /* 可用来定义请求结束时执行一些操作 */
#define PHP_MINFO_FUNCTION      ZEND_MODULE_INFO_D        /* 用来定义模块的信息,如phpinfo中的 */
#define PHP_GINIT_FUNCTION      ZEND_GINIT_FUNCTION
#define PHP_GSHUTDOWN_FUNCTION  ZEND_GSHUTDOWN_FUNCTION

#define PHP_MODULE_GLOBALS      ZEND_MODULE_GLOBALS

ZEND_FUNCTION 在 Zend/zend_API.h 第68行,它们是:

#define ZEND_FN(name) zif_##name
#define ZEND_MN(name) zim_##name
#define ZEND_NAMED_FUNCTION(name)       void name(INTERNAL_FUNCTION_PARAMETERS)
#define ZEND_FUNCTION(name)             ZEND_NAMED_FUNCTION(ZEND_FN(name))
#define ZEND_METHOD(classname, name)    ZEND_NAMED_FUNCTION(ZEND_MN(classname##_##name))

函数参数 INTERNAL_FUNCTION_PARAMETERS 在 Zend/zend.h 第290行,它们是:

#define INTERNAL_FUNCTION_PARAMETERS int ht, zval *return_value, zval **return_value_ptr, zval *this_ptr, int return_value_used TSRMLS_DC
#define INTERNAL_FUNCTION_PARAM_PASSTHRU ht, return_value, return_value_ptr, this_ptr, return_value_used TSRMLS_CC

也就是说,使用 PHP_FUNCTION(abc) 定义一个abc函数,预处理结果是 zif_abc(INTERNAL_FUNCTION_PARAMETERS),下面做个试验:

/* 1.c */
#include <stdio.h>

#define ZEND_FN(name) zif_##name
#define ZEND_MN(name) zim_##name
#define ZEND_NAMED_FUNCTION(name)       void name(INTERNAL_FUNCTION_PARAMETERS)
#define ZEND_FUNCTION(name)             ZEND_NAMED_FUNCTION(ZEND_FN(name))
#define ZEND_METHOD(classname, name)    ZEND_NAMED_FUNCTION(ZEND_MN(classname##_##name))

#define INTERNAL_FUNCTION_PARAMETERS int ht, zval *return_value, zval **return_value_ptr, zval *this_ptr, int return_value_used TSRMLS_DC

int main()
{
    ZEND_FUNCTION(abc); 

    return 0;
}

使用 `gcc -E -o 1.i 1.c` 生成预处理文件 1.i ,`tail 1.i` 看文件最后的结果,内核中一个函数的形式如下:

int main()
{
    void zif_abc(int ht, zval *return_value, zval **return_value_ptr, zval *this_ptr, int return_value_used TSRMLS_DC);

    return 0;
}

上面 zif_abc 函数参数中还有一个看起来奇怪的东西 TSRMLS_DC,线程安全资源管理的宏;

在 TSRM/TSRM.h 第166行,它们是:

#ifdef ZTS

..........

#define TSRMLS_FETCH()          void ***tsrm_ls = (void ***) ts_resource_ex(0, NULL)
#define TSRMLS_FETCH_FROM_CTX(ctx)  void ***tsrm_ls = (void ***) ctx
#define TSRMLS_SET_CTX(ctx)     ctx = (void ***) tsrm_ls
#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

#else    /* non ZTS */

#define TSRMLS_FETCH()
#define TSRMLS_FETCH_FROM_CTX(ctx)
#define TSRMLS_SET_CTX(ctx)
#define TSRMLS_D    void
#define TSRMLS_DC
#define TSRMLS_C
#define TSRMLS_CC

#endif    /* ZTS */

TSRMLS_DC 就是 , TSRMLS_D 了,也就是 , void ***tsrm_ls

TSRMLS_CC就是 , TSRMLS_C 了,也就是 , tsrm_ls

这里有篇关于TSRM的靠谱文章:揭秘TSRM(Introspecting TSRM)

二. 参数

int ht

zval *return_value, 函数内部修改指针,函数执行完成,内核把指针指向的zval返回给用户端函数调用者。

zval **return_value_ptr,

zval *this_ptr, 如果此函数是类的方法,相当于$this

int return_value_used, 用户端调用此函数是有没有用到函数返回值,用到就是1

三. zend_API.h 中的宏函数

我们追溯该文件中定义的两个宏函数 array_init(),php_error_docref().

array_init() 在 zend_API.h 第363行:

#define array_init(arg)         _array_init((arg), 0 ZEND_FILE_LINE_CC)
#define array_init_size(arg, size) _array_init((arg), (size) ZEND_FILE_LINE_CC)
#define object_init(arg)        _object_init((arg) ZEND_FILE_LINE_CC TSRMLS_CC)
#define object_init_ex(arg, ce) _object_init_ex((arg), (ce) ZEND_FILE_LINE_CC TSRMLS_CC)
#define object_and_properties_init(arg, ce, properties) _object_and_properties_init((arg),  (ce), (properties) ZEND_FILE_LINE_CC TSRMLS_CC)

ZEND_API int _array_init(zval *arg, uint size ZEND_FILE_LINE_DC);

_array_init() 的实现在 zend_API.c 第1009行:

/* Argument parsing API -- andrei */
ZEND_API int _array_init(zval *arg, uint size ZEND_FILE_LINE_DC) /* {{{ */
{
    ALLOC_HASHTABLE_REL(Z_ARRVAL_P(arg));

    _zend_hash_init(Z_ARRVAL_P(arg), size, ZVAL_PTR_DTOR, 0 ZEND_FILE_LINE_RELAY_CC);
    Z_TYPE_P(arg) = IS_ARRAY;
    return SUCCESS;
}
/* }}} */

这些API函数的存在,能让我们更方便在内核中完成某项功能。

顺藤摸瓜,ZEND_FILE_LINE_DC 在 zend.h 第217行:

#if ZEND_DEBUG
#define ZEND_FILE_LINE_D                const char *__zend_filename, const uint             __zend_lineno
#define ZEND_FILE_LINE_DC               , ZEND_FILE_LINE_D
#define ZEND_FILE_LINE_ORIG_D           const char *__zend_orig_filename, const uint        __zend_orig_lineno
#define ZEND_FILE_LINE_ORIG_DC          , ZEND_FILE_LINE_ORIG_D
#define ZEND_FILE_LINE_RELAY_C          __zend_filename, __zend_lineno
#define ZEND_FILE_LINE_RELAY_CC         , ZEND_FILE_LINE_RELAY_C
#define ZEND_FILE_LINE_C                __FILE__, __LINE__
#define ZEND_FILE_LINE_CC               , ZEND_FILE_LINE_C
#define ZEND_FILE_LINE_EMPTY_C          NULL, 0
#define ZEND_FILE_LINE_EMPTY_CC         , ZEND_FILE_LINE_EMPTY_C
#define ZEND_FILE_LINE_ORIG_RELAY_C     __zend_orig_filename, __zend_orig_lineno
#define ZEND_FILE_LINE_ORIG_RELAY_CC    , ZEND_FILE_LINE_ORIG_RELAY_C
#define ZEND_ASSERT(c)                  assert(c)
#else
#define ZEND_FILE_LINE_D
#define ZEND_FILE_LINE_DC
#define ZEND_FILE_LINE_ORIG_D
#define ZEND_FILE_LINE_ORIG_DC
#define ZEND_FILE_LINE_RELAY_C
#define ZEND_FILE_LINE_RELAY_CC
#define ZEND_FILE_LINE_C
#define ZEND_FILE_LINE_CC
#define ZEND_FILE_LINE_EMPTY_C
#define ZEND_FILE_LINE_EMPTY_CC
#define ZEND_FILE_LINE_ORIG_RELAY_C
#define ZEND_FILE_LINE_ORIG_RELAY_CC
#define ZEND_ASSERT(c)
#endif  /* ZEND_DEBUG */

上面 __file_name 就是函数一个形参名,没特殊含义,用两个下划线有非常局部(私有)的感觉。

php_error_docref() 在 main/php.h 第322行:

BEGIN_EXTERN_C()
......

/* PHPAPI void php_error(int type, const char *format, ...); */
PHPAPI void php_error_docref0(const char *docref TSRMLS_DC, int type, const char *format, ...)
    PHP_ATTRIBUTE_FORMAT(printf, PHP_ATTR_FMT_OFFSET + 3, PHP_ATTR_FMT_OFFSET + 4);
PHPAPI void php_error_docref1(const char *docref TSRMLS_DC, const char *param1, int type, const char *format, ...)
    PHP_ATTRIBUTE_FORMAT(printf, PHP_ATTR_FMT_OFFSET + 4, PHP_ATTR_FMT_OFFSET + 5);
PHPAPI void php_error_docref2(const char *docref TSRMLS_DC, const char *param1, const char *param2, int type, const char *  format, ...)
    PHP_ATTRIBUTE_FORMAT(printf, PHP_ATTR_FMT_OFFSET + 5, PHP_ATTR_FMT_OFFSET + 6);
#ifdef PHP_WIN32
PHPAPI void php_win32_docref2_from_error(DWORD error, const char *param1, const char *param2 TSRMLS_DC);
#endif
END_EXTERN_C()

#define php_error_docref php_error_docref0

例子:两步写一个color扩展的welcome函数

/* {{{ 自定义welcome */
PHP_FUNCTION(welcome)
{
    php_printf("It works, Welcome!\n");
}
/* }}} */

/* {{{ color_functions[]
 *
 * Every user visible function must have an entry in color_functions[]. 可用的函数都加到里面
 */
const zend_function_entry color_functions[] = {
    PHP_FE(confirm_color_compiled,  NULL)       /* For testing, remove later. */
    PHP_FE(welcome, NULL)
    PHP_FE_END  /* Must be the last line in color_functions[] */
};
/* }}} */

make && sudo make install

运行 `php5.6.14 -r ‘welcome();‘` 就会输出:It works, Welcome!

Link: http://www.cnblogs.com/farwish/p/5248686.html

@黑眼诗人  <www.farwish.com>

时间: 2024-08-21 03:39:17

[php-src]理解Php内核中的函数的相关文章

linux2.6.30.4内核中platform_get_resource函数

今天看到下面这两个函数: struct resource *platform_get_resource(struct platform_device *dev, unsigned int type, unsigned int num){ int i; for (i = 0; i < dev->num_resources; i++) { struct resource *r = &dev->resource[i]; if (type == resource_type(r) &

Linux内核中kzalloc函数详解

***************************************************************************************************************************作者:EasyWave                                                                                 时间:2013.02.06 类别:Linux 内核驱动源码分析    

内核中通过函数指针打印出具体的函数

内核中函数指针用的很多,在debug 的时候能直接打印出一个函数指针对应的函数就会很方便. 打印裸指针(raw pointer)用 %p,%p除了可以用来打印指针外还可以打印其它的信息 %pF可打印函数指针的函数名和偏移地址,%pf只打印函数指针的函数名,不打印偏移地址. 如 printk("%pf %pF\n", ptr, ptr) will print: module_start module_start+0x0/0x62 [hello] 但是为了支持这个功能你需要开启CONFIG

深入理解Solaris内核中互斥锁(mutex)与条件变量(condvar)之协同工作原理

在Solaris上写内核模块总是会用到互斥锁(mutex)与条件变量(condvar), 光阴荏苒日月如梭弹指一挥间,Solaris的大船说沉就要沉了,此刻心情不是太好(Orz).每次被年轻的有才华的同事们(比如Letty同学)问起mutex和cv怎么协同工作的,我总是不能给出一个非常清晰的解释.直到今天,看了cv_wait()的源代码之后,我终于可以给他们一个清楚明白的回答了. Solaris的源码无法被公开粘贴出来,幸好还有OpenSolaris的继承者illumos. 先贴cv_wait(

从两个角度理解为什么 JS 中没有函数重载

函数重载是指在同一作用域内,可以有一组具有相同函数名,不同参数列表(参数个数.类型.顺序)的函数,这组函数被称为重载函数.重载函数通常用来声明一组功能相似的函数,这样做减少了函数名的数量,避免了名字空间的污染,对于程序的可读性有很大的好处. 但是在 JS 如果不通过一些方法是无法实现重载的,可以从以下两个角度去理解. 1. 方法签名 方法签名指的是函数的名称加形参列表,并且通过函数的名称或者形参列表都可以区分出是不同的函数. JS 中通过形参是没有办法区分出不同的函数的,只能通过函数的名称区分出

Android中回调函数的理解---本人Android纯新手

本人大二,刚刚接触Android,也刚刚申请的cnblog博客,说一下对Android中回调函数的理解,Android中回调函数和C++.JAVA中的默认构造函数差不多,即运行到了一定的代码时自动调用的代码,而Android中的回调函数和C++.JAVA中的默认构造函数的区别在于:C++.JAVA中的默认构造函数在创建一个对象时自动调用,而Android中的回调函数的自动调用是在比如按了HOME键之后.

深入理解Linux内核day08--进程线性地址空间

进程地址空间 内核中的函数以相当直截了当的方式获得动态内存: 1.__get_free_pages()和alloc_pages()从分区页框分配器中获得页框. 2.kmem_cache_alloc()和kmalloc()使用slab分配器为专门或通用对象分配快. 3.vmalloc()和vmalloc_32()获得一块非连续的内存区. 使用这些简单方法是基于以下两个原因: 内核是操作系统中优先级最高的成分. 内核信任自己. 当给用户态进程分配内存时,情况完全不同: 进程对动态内存的请求被认为是不

经典]Linux内核中ioremap映射的透彻理解【转】

转自:http://blog.csdn.net/lanyang123456/article/details/7403514 几乎每一种外设都是通过读写设备上的寄存器来进行的,通常包括控制寄存器.状态寄存器和数据寄存器三大类,外设的寄存器通常被连续地编址.根据CPU体系结构的不同,CPU对IO端口的编址方式有两种: (1)I/O映射方式(I/O-mapped) 典型地,如X86处理器为外设专门实现了一个单独的地址空间,称为"I/O地址空间"或者"I/O端口空间",CP

关于在C#中对函数重载理解

函数重载是个什么概念,才接触的这个概念的时候我也是完全昏了,还在自己看看了书后就理解了.那什么是函数重载呢?我个人理解的是在同一个作用域下有多个同名的函数,但是他们的形参的类型是不同的,或者参数个数是不同的.当我们调用这些函数时,怎么判断我们调用的是那一个函数呢,这个就要看你在使用重载函数时所传参数的类型或者参数个数.好了,话不说.看看代码就知道了. using System;namespace overload{    class a    {        public void print