PHP HASH表

在PHP中,所有的数据 无论变量,常量,类,属性 都用Hash表来实现.

先要说说 HASH表

  1. typedef struct bucket {
  2. ulong h;                                                /* Used for numeric indexing */
  3. uint nKeyLength; //key长度
  4. void *pData; //指向 Bucke保存的数据 指针
  5. void *pDataPtr; //指针数据
  6. struct bucket *pListNext; //下一个元素指针
  7. struct bucket *pListLast;//上一个元素指针
  8. struct bucket *pNext;
  9. struct bucket *pLast;
  10. char arKey[1]; /* Must be last element */
  11. } Bucket;
  12. typedef struct _hashtable {
  13. uint nTableSize;//HashTable的大小
  14. uint nTableMask;//等于nTableSize-1
  15. uint nNumOfElements;//对象个数
  16. ulong nNextFreeElement;//指向下一个空元素位置 nTableSize+1
  17. Bucket *pInternalPointer;       /* Used for element traversal *///保存当前遍历的指针
  18. Bucket *pListHead;//头元素指针
  19. Bucket *pListTail;//尾元素指针
  20. Bucket **arBuckets;//存储hash数组数据
  21. dtor_func_t pDestructor;//类似于析构函数
  22. zend_bool persistent;//用哪种方法分配内存空间 PHP统一管理内存还是用普通的malloc
  23. unsigned char nApplyCount;//当前hash bucket被访问的次数,是否遍历过数据,防止无限递归循环
  24. zend_bool bApplyProtection;
  25. #if ZEND_DEBUG
  26. int inconsistent;
  27. #endif
  28. } HashTable;

我们结合 HASH表初始化函数来说

  1. ZEND_API int _zend_hash_init(HashTable *ht, uint nSize, hash_func_t pHashFunction, dtor_func_t pDestructor, zend_bool persistent ZEND_FILE_LINE_DC)
  2. {
  3. uint i = 3;
  4. Bucket **tmp;
  5. SET_INCONSISTENT(HT_OK);
  6. if (nSize >= 0x80000000) { //HASH表大小大于0x8则初始化为0x8
  7. /* prevent overflow */
  8. ht->nTableSize = 0x80000000;
  9. } else {
  10. while ((1U << i) < nSize) { //调整为 2的n次方          i++;        }        ht->nTableSize = 1 << i;//HASH bucket大小   为 2的i次方  i=3 ,nTableSize最小值为8
  11. }
  12. //为了提高计算效率,系统自动会将nTableSize调整到最小一个不小于nTableSize的2的整数次方。也就是说,如果在初始化HashTable时指定一个nTableSize不是2的整数次方,系统将会自动调整nTableSize的值 <!--EndFragment-->
  13. ht->nTableMask = ht->nTableSize - 1;
  14. ht->pDestructor = pDestructor;//一个函数指针,当HashTable发生增,删,改时调用
  15. ht->arBuckets = NULL;
  16. ht->pListHead = NULL;
  17. ht->pListTail = NULL;
  18. ht->nNumOfElements = 0;
  19. ht->nNextFreeElement = 0;
  20. ht->pInternalPointer = NULL;
  21. ht->persistent = persistent;//如果persisient为TRUE,则使用操作系统本身的内存分配函数为Bucket分配内存,否则使用PHP的内存分配函数
  22. ht->nApplyCount = 0;
  23. ht->bApplyProtection = 1;
  24. /* Uses ecalloc() so that Bucket* == NULL */
  25. if (persistent) {  //操作系统本身内存分配方式分配内存,calloc分配内存后自动初始化为0
  26. tmp = (Bucket **) calloc(ht->nTableSize, sizeof(Bucket *));
  27. if (!tmp) {
  28. return FAILURE;
  29. }
  30. ht->arBuckets = tmp;
  31. } else {//用PHP的内存管理机制分配内存
  32. tmp = (Bucket **) ecalloc_rel(ht->nTableSize, sizeof(Bucket *));
  33. if (tmp) {
  34. ht->arBuckets = tmp;
  35. }
  36. }
  37. //自动申请一块内存给arBuckets,该内存大小等于 nTableSize
  38. return SUCCESS;
  39. }

在读源码的时候 ,经常会看到 EG,PG,CG这样的宏

CG是 compile_global的简写

EG是excutor_global的简写

G就是全局变量的意思

我们就以EG宏为例

  1. #ifdef ZTS
  2. # define EG(v) TSRMG(executor_globals_id, zend_executor_globals *, v)
  3. #else
  4. # define EG(v) (executor_globals.v)
  5. extern ZEND_API zend_executor_globals executor_globals;
  6. #endif

很简单 只是一个获取全局变量的宏

那么我们看看 zend_executor_globals这个结构体

在/Zend/zend.h里面定义

typedef struct _zend_executor_globals zend_executor_globals;

是一个 _zend_executor_globals的别名

同一个文件里找到它

PHP的所有 局部变量,全局变量,函数,类的 Hash表 都在这里定义了

  1. struct _zend_executor_globals {
  2. zval **return_value_ptr_ptr;
  3. zval uninitialized_zval;
  4. zval *uninitialized_zval_ptr;
  5. zval error_zval;
  6. zval *error_zval_ptr;
  7. zend_ptr_stack arg_types_stack;
  8. /* symbol table cache */
  9. HashTable *symtable_cache[SYMTABLE_CACHE_SIZE];
  10. HashTable **symtable_cache_limit;
  11. HashTable **symtable_cache_ptr;
  12. zend_op **opline_ptr;
  13. HashTable *active_symbol_table;  //局部变量
  14. HashTable symbol_table; /* main symbol table */ //全局变量
  15. HashTable included_files; /* files already included */ //include的文件
  16. JMP_BUF *bailout;
  17. int error_reporting;
  18. int orig_error_reporting;
  19. int exit_status;
  20. zend_op_array *active_op_array;
  21. HashTable *function_table; /* function symbol table */ //函数表
  22. HashTable *class_table; /* class table */ //类表
  23. HashTable *zend_constants; /* constants table */ //常量表
  24. zend_class_entry *scope;
  25. zend_class_entry *called_scope; /* Scope of the calling class */
  26. zval *This;
  27. long precision;
  28. int ticks_count;
  29. zend_bool in_execution;
  30. HashTable *in_autoload;
  31. zend_function *autoload_func;
  32. zend_bool full_tables_cleanup;
  33. /* for extended information support */
  34. zend_bool no_extensions;
  35. #ifdef ZEND_WIN32
  36. zend_bool timed_out;
  37. OSVERSIONINFOEX windows_version_info;
  38. #endif
  39. HashTable regular_list;
  40. HashTable persistent_list;
  41. zend_vm_stack argument_stack;
  42. int user_error_handler_error_reporting;
  43. zval *user_error_handler;
  44. zval *user_exception_handler;
  45. zend_stack user_error_handlers_error_reporting;
  46. zend_ptr_stack user_error_handlers;
  47. zend_ptr_stack user_exception_handlers;
  48. zend_error_handling_t error_handling;
  49. zend_class_entry *exception_class;
  50. /* timeout support */
  51. int timeout_seconds;
  52. int lambda_count;
  53. HashTable *ini_directives;
  54. HashTable *modified_ini_directives;
  55. zend_objects_store objects_store;
  56. zval *exception, *prev_exception;
  57. zend_op *opline_before_exception;
  58. zend_op exception_op[3];
  59. struct _zend_execute_data *current_execute_data;
  60. struct _zend_module_entry *current_module;
  61. zend_property_info std_property_info;
  62. zend_bool active;
  63. void *saved_fpu_cw;
  64. void *reserved[ZEND_MAX_RESERVED_RESOURCES];
  65. };

这里先简单看看,以后用到的时候再细说,

    • PHP里最基本的单元 变量:
      在PHP里 定义一个变量 再简单不过了
  1. <?php
  2. $a=1;
  3. ?>

但是在内核中 它是用一个 zval结构体实现的
如上面定义变量 在内核中则执行了下面这些代码

  1. zval *val;
  2. MAKE_STD_ZVAL(val);  //申请一块内存
  3. ZVAL_STRING(val,"hello",1);//用ZVAL_STRING设置它的值为 "hello"
  4. ZEND_SET_SYMBOL(EG(active_symbol_table),"a",val));//将  val指针加入到符号表里面去

宏 MAKE_STD_ZVAL 定义如下

  1. #define MAKE_STD_ZVAL(zv)                                \
  2. ALLOC_ZVAL(zv); \  //它归根到底等于 (p) = (type *) emalloc(sizeof(type))
  3. INIT_PZVAL(zv);

INIT_PZVAL定义在

  1. #define INIT_PZVAL(z)           \ 看得出它是初始化参数
  2. (z)->refcount__gc = 1;  \
  3. (z)->is_ref__gc = 0;

那么 zval到底是什么呢
在zend/zend.h里面
typedef struct _zval_struct zval; //原来它是 _zval_struct 的别名
_zval_struct 定义如下

  1. typedef union _zvalue_value {
  2. long lval;  //保存long类型的数据
  3. double dval; //保存 double类型的数据
  4. struct {
  5. char *val; //真正的值在这里
  6. int len;   //这里返回长度
  7. } str;
  8. HashTable *ht;
  9. zend_object_value obj; //这是一个对象
  10. } zvalue_value;
  11. struct _zval_struct {
  12. zvalue_value value;             //保存的值
  13. zend_uint refcount__gc;//被引用的次数 如果为1 则只被自己使用如果大于1 则被其他变量以&的形式引用.
  14. zend_uchar type;       //数据类型 这也是 为什么 PHP是弱类型的原因
  15. zend_uchar is_ref__gc;  //表示是否为引用
  16. };

如果还是不够清楚..那么我们实战一下..用C来创建一个PHP变量
这里需要一个扩展,PHP如果用C扩展模块 这里就不说了
关键代码

  1. PHP_FUNCTION(test_siren){
  2. zval *value;
  3. char *s="create a php variable";
  4. value=(zval*)malloc(sizeof(zval));
  5. memset(value,0,sizeof(value));
  6. value->is_ref__gc=0; //非引用变量
  7. value->refcount__gc=1;//引用次数 只有自己
  8. value->type=IS_STRING;//类型为字符串
  9. value->value.str.val=s;//值
  10. value->value.str.len=strlen(s);//长度
  11. ZEND_SET_SYMBOL(EG(active_symbol_table),"a",value);
  12. }

第三行和第四行的作用 与MAKE_STD_ZVAL的作用相同,给value分配内存空间
第5-9行 的作用与ZVAL_STRING的作用相同,
最后一行 是将value创建一个 在PHP里叫$a的变量..并添加到局部Hash表里..
这样 在PHP里

  1. <?php
  2. test_siren(1);
  3. echo $a;
  4. ?>

就会输出 “create a php variable”
OK,
大功告成
注意,我是为了让大家看到PHP内部创建变量的流程 才采用C的形式创建变量,
绝对不推荐大家这样做.
还是一定要用PHP内部的内存管理机制分配并处理内存。

来自:http://blog.51cto.com/imsiren/1128808

PHP内核研究:HASH表和变量 【转】的更多相关文章

  1. 内核中hash表(以net_device为例)

    下边函数实现将新的 net_device 设备插入到内核链表中 /* * Device list insertion */ static void list_netdevice(struct net_ ...

  2. PHP数组/Hash表的实现/操作、PHP变量内核实现、PHP常量内核实现 - [ PHP内核学习 ]

    catalogue . PHP Hash表 . PHP数组定义 . PHP变量实现 . PHP常量实现 1. PHP Hash表 0x1: 基本概念 哈希表在实践中使用的非常广泛,例如编译器通常会维护 ...

  3. 十一、从头到尾彻底解析Hash 表算法

    在研究MonetDB时深入的学习了hash算法,看了作者的文章很有感触,所以转发,希望能够使更多人受益! 十一.从头到尾彻底解析Hash 表算法 作者:July.wuliming.pkuoliver  ...

  4. Linux内核哈希表分析与应用

        目录(?)[+]   Linux内核哈希表分析与应用 Author:tiger-johnTime:2012-12-20mail:jibo.tiger@gmail.comBlog:http:// ...

  5. xenomai内核解析---内核对象注册表—xnregistry(重要组件)

    1. 概述 上篇文章xenomai内核解析--同步互斥机制(一)--优先级倒置讲到,对于所有内核对象: xnregistry:保存内核对象,提供内核对象存储和快速检索. xnsynch:资源抽象,提供 ...

  6. [源码解析] NVIDIA HugeCTR,GPU版本参数服务器--- (5) 嵌入式hash表

    [源码解析] NVIDIA HugeCTR,GPU版本参数服务器--- (5) 嵌入式hash表 目录 [源码解析] NVIDIA HugeCTR,GPU版本参数服务器--- (5) 嵌入式hash表 ...

  7. [源码解析] NVIDIA HugeCTR,GPU版本参数服务器--- (6) --- Distributed hash表

    [源码解析] NVIDIA HugeCTR,GPU版本参数服务器--- (6) --- Distributed hash表 目录 [源码解析] NVIDIA HugeCTR,GPU版本参数服务器--- ...

  8. 6.数组和Hash表

    当显示多条结果时,存储在变量中非常智能,变量类型会自动转换为一个数组. 在下面的例子中,使用GetType()可以看到$a变量已经不是我们常见的string或int类型,而是Object类型,使用-i ...

  9. Java提高篇——通过分析 JDK 源代码研究 Hash 存储机制

    HashMap 和 HashSet 是 Java Collection Framework 的两个重要成员,其中 HashMap 是 Map 接口的常用实现类,HashSet 是 Set 接口的常用实 ...

随机推荐

  1. partial分部类

    意义 1.源代码控制 2.将一个类或结构分成不同的逻辑单元 3.代码拆分

  2. hdu2516-取石子游戏 (斐波那契博弈)【博弈 二分查找】

    http://acm.hdu.edu.cn/showproblem.php?pid=2516 取石子游戏 Time Limit: 2000/1000 MS (Java/Others)    Memor ...

  3. java动态规划导弹问题

    这是一道动态规划题,和昨天的取硬币还有最长公共字串有点类似. 1.题目描述:                        某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统.但是这种导弹拦截系统有一 ...

  4. 关于IOS新手在安装cocoa pods失败,因为ruby版本过低的解决方法+ (void) {升级ruby}

    http://blog.csdn.net/zhaoen95/article/details/51995520     现在: 使用 OS 系统, 正在学习iOS 需要配置cocoapods 命令行中显 ...

  5. Python操作Excel删除一个Sheet

    在使用Python进行数据分析处理,操作Excel,有时需要删除某个Excel里的某个sheet,这里记录一个我测试成功的一个办法 软件环境: 1.OS:Win 10 64位 2.Python 3.7 ...

  6. Oracle表格字段采用sequence进行自增长时,采用Dbutils进行insert或update数据时的处理技巧

    // 定义插入记录的方法 public Teacher insert(String name, String gender, Double score) { // 获得连接 Connection co ...

  7. redis 面试题2

    使用过Redis分布式锁么,它是什么回事? 先拿setnx来争抢锁,抢到之后,再用expire给锁加一个过期时间防止锁忘记了释放. 这时候对方会告诉你说你回答得不错,然后接着问如果在setnx之后执行 ...

  8. C#中不同格式数据校验的正则表达式

    网上经常看到用正则表达式校验数据的文章,有的虽然总结得很全,但是大多数都没有经过严格验证,错误较多. 本文包含三十余条不同格式数据校验的C#正则表达式,一般均附有说明,且在Visual Studio里 ...

  9. Fix: The account is not authorized to log in from this station

    If you have more the one computers running Windows, then its possible to connect them using HomeGrou ...

  10. nyoj746 整数划分

    nyoj746 http://acm.nyist.net/JudgeOnline/problem.php?pid=746 一道区间dp的题目: 设:a[i][j]为那一串数字中从第i位到第j位的数是多 ...