原创:PHP内核研究:HASH表和变量

PHP HASH表

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

先要说说 HASH表

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



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

[c]
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)
{
uint i = 3;
Bucket **tmp;

SET_INCONSISTENT(HT_OK);

if (nSize >= 0x80000000) { //HASH表大小大于0x8则初始化为0x8
/* prevent overflow */
ht->nTableSize = 0x80000000;
} else {
while ((1U << i) < nSize) { //调整为 2的n次方 i++; } ht->nTableSize = 1 << i;//HASH bucket大小 为 2的i次方 i=3 ,nTableSize最小值为8
}
//为了提高计算效率,系统自动会将nTableSize调整到最小一个不小于nTableSize的2的整数次方。也就是说,如果在初始化HashTable时指定一个nTableSize不是2的整数次方,系统将会自动调整nTableSize的值

ht->nTableMask = ht->nTableSize - 1;
ht->pDestructor = pDestructor;//一个函数指针,当HashTable发生增,删,改时调用
ht->arBuckets = NULL;
ht->pListHead = NULL;
ht->pListTail = NULL;
ht->nNumOfElements = 0;
ht->nNextFreeElement = 0;
ht->pInternalPointer = NULL;
ht->persistent = persistent;//如果persisient为TRUE,则使用操作系统本身的内存分配函数为Bucket分配内存,否则使用PHP的内存分配函数
ht->nApplyCount = 0;
ht->bApplyProtection = 1;

/* Uses ecalloc() so that Bucket* == NULL */
if (persistent) { //操作系统本身内存分配方式分配内存,calloc分配内存后自动初始化为0
tmp = (Bucket **) calloc(ht->nTableSize, sizeof(Bucket *));
if (!tmp) {
return FAILURE;
}
ht->arBuckets = tmp;
} else {//用PHP的内存管理机制分配内存
tmp = (Bucket **) ecalloc_rel(ht->nTableSize, sizeof(Bucket *));
if (tmp) {
ht->arBuckets = tmp;
}
}
//自动申请一块内存给arBuckets,该内存大小等于 nTableSize
return SUCCESS;
}

[/c]
  • 在读源码的时候 ,经常会看到 EG,PG,CG这样的宏

CG是 compile_global的简写

EG是excutor_global的简写

G就是全局变量的意思

我们就以EG宏为例:

[c]
#ifdef ZTS
# define EG(v) TSRMG(executor_globals_id, zend_executor_globals *, v)
#else
# define EG(v) (executor_globals.v)
extern ZEND_API zend_executor_globals executor_globals;
#endif
[/c]

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

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

在/Zend/zend.h里面定义

typedef struct _zend_executor_globals zend_executor_globals;

是一个 _zend_executor_globals的别名

同一个文件里找到它

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

[c]
struct _zend_executor_globals {
zval **return_value_ptr_ptr;

zval uninitialized_zval;
zval *uninitialized_zval_ptr;

zval error_zval;
zval *error_zval_ptr;

zend_ptr_stack arg_types_stack;

/* symbol table cache */
HashTable *symtable_cache[SYMTABLE_CACHE_SIZE];
HashTable **symtable_cache_limit;
HashTable **symtable_cache_ptr;

zend_op **opline_ptr;

HashTable *active_symbol_table; //局部变量
HashTable symbol_table; /* main symbol table */ //全局变量

HashTable included_files; /* files already included */ //include的文件

JMP_BUF *bailout;

int error_reporting;
int orig_error_reporting;
int exit_status;

zend_op_array *active_op_array;

HashTable *function_table; /* function symbol table */ //函数表
HashTable *class_table; /* class table */ //类表
HashTable *zend_constants; /* constants table */ //常量表

zend_class_entry *scope;
zend_class_entry *called_scope; /* Scope of the calling class */

zval *This;

long precision;

int ticks_count;

zend_bool in_execution;
HashTable *in_autoload;
zend_function *autoload_func;
zend_bool full_tables_cleanup;

/* for extended information support */
zend_bool no_extensions;

#ifdef ZEND_WIN32
zend_bool timed_out;
OSVERSIONINFOEX windows_version_info;
#endif

HashTable regular_list;
HashTable persistent_list;

zend_vm_stack argument_stack;

int user_error_handler_error_reporting;
zval *user_error_handler;
zval *user_exception_handler;
zend_stack user_error_handlers_error_reporting;
zend_ptr_stack user_error_handlers;
zend_ptr_stack user_exception_handlers;

zend_error_handling_t error_handling;
zend_class_entry *exception_class;

/* timeout support */
int timeout_seconds;

int lambda_count;

HashTable *ini_directives;
HashTable *modified_ini_directives;

zend_objects_store objects_store;
zval *exception, *prev_exception;
zend_op *opline_before_exception;
zend_op exception_op[3];

struct _zend_execute_data *current_execute_data;

struct _zend_module_entry *current_module;

zend_property_info std_property_info;

zend_bool active;

void *saved_fpu_cw;

void *reserved[ZEND_MAX_RESERVED_RESOURCES];
};
[/c]


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

  • PHP里最基本的单元 变量:
    在PHP里 定义一个变量 再简单不过了
[php]

[/php]

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

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

宏 MAKE_STD_ZVAL 定义如下

[c]
#define MAKE_STD_ZVAL(zv) \
ALLOC_ZVAL(zv); \ //它归根到底等于 (p) = (type *) emalloc(sizeof(type))
INIT_PZVAL(zv);
[/c]

INIT_PZVAL定义在

[c]
#define INIT_PZVAL(z) \ 看得出它是初始化参数
(z)->refcount__gc = 1; \
(z)->is_ref__gc = 0;
[/c]

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

[c]
typedef union _zvalue_value {
long lval; //保存long类型的数据
double dval; //保存 double类型的数据
struct {
char *val; //真正的值在这里
int len; //这里返回长度
} str;
HashTable *ht;
zend_object_value obj; //这是一个对象
} zvalue_value;

struct _zval_struct {
zvalue_value value; //保存的值
zend_uint refcount__gc;//被引用的次数 如果为1 则只被自己使用如果大于1 则被其他变量以&的形式引用.
zend_uchar type; //数据类型 这也是 为什么 PHP是弱类型的原因
zend_uchar is_ref__gc; //表示是否为引用
};
[/c]

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

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

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

[php]

[/php]

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


转载于:https://www.cnblogs.com/si-ren/archive/2012/03/10/2447628.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/297302.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Windows 11 小技巧- 安装

Windows 11 依赖于TPM 2.0&#xff0c;什么是TPM呢?TPM技术旨在提供基于硬件的与安全性相关的功能。TPM芯片是⼀个安全的加密处理器&#xff0c;有助于执⾏⽣成、存储和限制加密密钥的使用等操作。TPM芯片包含多重物理安全机制&#xff0c;具有防篡改功能&#xff0c;恶意软件…

如何判断一个人是不是值得深入交流?

全世界只有3.14 % 的人关注了爆炸吧知识每次翻看刚加好友的朋友圈时&#xff0c;都会对 ta 产生直观的判断。如果朋友圈很丰富&#xff0c;往往会觉得 ta 很有趣&#xff0c;会迫切想要和 ta 链接&#xff0c;而看到某些很单调的朋友圈&#xff0c;根本就没有深入沟通的欲望。真…

python 逆向生成正则表达式_用Python中的正则表达式生成lis

我有一个小脚本&#xff0c;用来产生一个预定义的字体和文本图像。我想将其更改为使用多种字体来呈现相同的文本&#xff0c;例如字母A以5种字体呈现。我将我的字体列表定义为&#xff1a;fonts [ Georgia, Consolas, Arial]然后我把它用在&#xff1a;for item in enumerate(…

Restive.js – 轻松让网站变成响应式和自适应

Restive.js 是一个 jQuery 插件&#xff0c;可以帮助您轻松快捷地添加响应式功能到你网站&#xff0c;适应几乎所有拥有 Web 功能的设备。使用设备检测&#xff0c;高级管理断点&#xff0c;以及方向管理的组合&#xff0c;Restive.js 会给你的网站提供一种不可思议的能力。 在…

消息分发的同步均衡策略

2019独角兽企业重金招聘Python工程师标准>>> TimeTunnel在做消息分发时有这样一个场景: A类消息需要做实时分析, 且量很大, 故它的消费者不会只是一台机器, 而是一组机器, 并要求这组中每台机器收到的消息量应该平均的, 即A消息在某个时刻有100条, 若有4台机器消费…

ssh长时间不操作便断开_连接SSH长时间不操作断开解决办法

经常连接ssh长时间不操作就断开&#xff0c;实在忍无可忍&#xff0c;每次都想解决这个问题&#xff0c;但是就是懒得搞&#xff0c;这次必须得一刀解决。解决方法一&#xff1a;服务器配置1、 连接SSHssh root192.168.0.1复制代码2、编辑sshd_configvim /etc/ssh/sshd_config复…

当下流行的直播技术demo演示

nginx-http-flv-module&#xff08;更新不是很频繁&#xff09; SRS: https://ossrs.net/lts/zh-cn/&#xff08;独立官网&#xff0c;目前最新稳定版version5&#xff09; 基于SRS搭建直播demo演示&#xff1a; 一、搭建流媒体服务器 参见官网&#xff1a;https://ossrs.ne…

SQL复制表

View Code --创建test_employee_info临时表结构&#xff0c;不保留关联关系select * into test_employee_info from employee_info where 1<>1;declare num intset num1while num<6begininsert into test_employee_info select employee_codepcast(num as varchar(1)…

Workflow Core + asp.net core 5.0 实现简单审批工作流

我们知道企业业务系统到处都可以审批工作流的&#xff0c;但也很少有像OA系统一样复杂多级多条件的审批工作流需要设计&#xff0c;所以我们需要一个轻量级的容易上手的workflow框架&#xff0c;通过GitHub,我发现danielgerlag/workflow-core 就非常合适&#xff0c;我下面我通…

python解析xml文件选用模块_python解析xml模块封装代码

{"moduleinfo":{"card_count":[{"count_phone":1,"count":1}],"search_count":[{"count_phone":4,"count":4}]},"card":[{"des":"阿里技术人对外发布原创技术内容的最大平台&…

超1亿人选择朋友圈三天可见,背后的原因值得深思

全世界只有3.14 % 的人关注了爆炸吧知识每次翻看刚加好友的朋友圈时&#xff0c;都会对 ta 产生直观的判断。如果朋友圈很丰富&#xff0c;往往会觉得 ta 很有趣&#xff0c;会迫切想要和 ta 链接&#xff0c;而看到某些很单调的朋友圈&#xff0c;根本就没有深入沟通的欲望。真…

基于HTML5手机上下滑动翻页特效

基于HTML5手机上下滑动翻页特效。这是一款手机移动端触屏滑动翻页代码下载。效果图如下&#xff1a; 在线预览 源码下载 实现的代码。 html代码&#xff1a; <section class"u-alert"><img style"display:none;" src"images/loading_larg…

在Centos中yum安装和卸载软件的使用方法

在Centos中yum安装和卸载软件的使用方法安装方法安装一个软件时yum -y install httpd安装多个相类似的软件时yum -y install httpd*安装多个非类似软件时yum -y install httpd php php-gd mysql卸载一个软件时yum -y remove httpd卸载多个相类似的软件时yum -y remove httpd*卸…

.NET跨平台实践:.NetCore、.Net5/6 Linux守护进程设计

几年前&#xff0c;我写过两篇关于用C#开发Linux守护进程的技术文章&#xff0c;分别是《.NET跨平台实践&#xff1a;用C#开发Linux守护进程.NET跨平台实践&#xff1a;再谈用C#开发Linux守护进程 — 完整篇这就是本文的初衷。关于Linux Daemon程序的原理之类的&#xff0c;已经…

代码优化从数据库里查数据

今天写了几行代码&#xff0c;都是从一个表里查数据。而我却查了三次数据库&#xff0c; 代码例子如下&#xff1a; dalclass.GetLie("userName","Student","userId"id); dalclass.GetLie("userClass","Student","user…

一寸照纯红色底图片_和记场下载

新能的事普及棘手源汽依然情车的是件&#xff0c;为王花燃油但和比车相&#xff0c;为王花底解电池和记场下载在没决续基础建设高额问题航、有彻以及前充电成本&#xff0c;它的体量不大依旧&#xff0c;新能形成正在逐渐之势推广尽管局部源汽全球车的所以。第一直接兼老个家话…

学点css基础

中午时间学点css&#xff0c;附带http://www.w3cschool.cc/css/css-tutorial.html这个链接&#xff01; 中午的时间学了这些东西&#xff01;如下图: 附带代码&#xff1a; 1 <!DOCTYPE html>2 <html lang"en">3 <head>4 <meta charset&quo…

套套原来是这样装袋的,40个机械动图看懂好像并不容易。。。

今天咱们来多方位的展示机械的魅力1、真在的柔性自动包装线&#xff0c;可以同时实现一条线上多种不同物体的包装&#xff1a;好强大&#xff0c;美中不足的是没有防震包装&#xff0c;容易压坏商品。另外从视频上可以看出来纸板和物品一块出来&#xff0c;纸板出来时已经裁剪好…

python运算符号输入_【Python】【运算符】

【取模】所谓取模运算&#xff0c;就是计算两个数相除之后的余数&#xff0c;符号是%。如a % b就是计算a除以b的余数。用数学语言来描述&#xff0c;就是如果存在整数n和m&#xff0c;其中0 < m < b&#xff0c;使得a n * b m&#xff0c;那么a % b a - n * b m.取模…

云原生开发框架dapr环境搭建:CLI安装和初始化

dapr 是微软的一个云原生&#xff08;Cloud Native&#xff09;开源项目&#xff0c;英文全称&#xff1a;Distributed Application Runtime&#xff0c;中文要翻译的话就是&#xff1a;分布式应用运行时。也就是一个运行时框架&#xff0c;面向云原生架构。dapr官网地址&#…