深入PHP内核关于作者:王帅,腾讯企业QQ SaaS团队Leader。深入PHP内核(一)——弱类型变量原理探究摘要:PHP作为一门简单而强大的语言,能够提供很多Web适用的
深入PHP内核
如果你对弱类型原理理解不深刻,在变量比较时候,会出现“超出预期”的惊喜。
[php]
第二阶段是请求初始化阶段(RINT),在模块初始化并激活后,会创建PHP运行环境,同时调用所有模块注册的RINT函数,调用每个扩展的请求初始化函数 ,设定特定的环境变量、分配资源或执行其他任务,如审核。[php]
第三阶段,请求处理完成后,会调用PHP_RSHUTDOWN_FUNCTION进行回收,这是每个扩展的请求关闭函数,执行最后的清理工作。Zend引擎执行清理过程、垃圾收集、对之前的请求期间用到的每个变量执行unset。请求完成可能是执行到脚本完成,也可能是调用die()或exit()函数完成
第四阶段,当PHP生命周期结束时候,PHP_MSHUTDOWN_FUNCTION对模块进行回收处理,这是每个扩展的模块关闭函数,用于关闭自己的内核子系统。
[php]
1. CLI/CGI模式
CLI和CGI都属于单进程模式,PHP的生命周期在一次请求中完成。也就是说每次执行PHP脚本,都会执行第二部分讲的四个INT和Shutdown事件。
由上面代码可见,PHP的SAPI像是面向对象中基类,SAPI.h和SAPI.c包含的函数是抽象基类的声明和定义,各个服务器用的SAPI模式,则是继承了这个基类,并重新定义基类方法的子类。
总结PHP的SAPI是Zend引擎提供的一组标准交互接口,通过注册初始化、析构、输入、输出等接口,我们可以将应用程序运行在Zend引擎上,也可以把PHP嵌入到类似Apache的Web Server中。PHP常见的SAPI模式有五种,CGI/CLI模式、多进程模式、多线程模式、FastCGI模式和内嵌模式。
了解PHP的SAPI机制意义重大,帮助我们理解PHP的生命周期,并了解如何更好的通过C/C++为PHP编写扩展,并在生命周期中找到提高系统性能的方式。
深入PHP内核(三)——内核利器哈希表与哈希碰撞攻击
摘要:PHP作为一门简单而强大的语言,能够提供很多Web适用的语言特性。从实践出发,继弱类型变量原理探究后,王帅将继续带大家弄清PHP内核中的一些常用部分,本期则是内核利器哈希表与哈希碰撞攻击。
【导读】王帅在海量分布式Web系统有超过8年沉淀,主导过多个大型系统的架构设计,目前在腾讯企业SaaS团队。
PHP内核系列文章,是作者在PHP领域实践中,把相关原理性的知识,通过更便于理解的方式,系统整理出来分享给读者。希望通过PHP原理性的轻量解读,对这门Web领域最热门技术的优秀架构分析解构,让更多的人不断的深入了解语言的原理本身,更容易定位、理解一些问题背后原因,更游刃有余的做基础架构设计。同时希望影响更多的人才投入Web开源领域,不仅是应用和学习一门技术、组件,同样能够贡献更多高质量组件,像战国时期的百家争鸣一样,PHP开源界花开遍地。
作者一直倡导技术的深入学习就像职业篮球训练,80%的时间都是基本功的训练,球场上实际战术的练习只是基本功的应用。同样的,学习PHP语言本身的特性,应当是每个PHP领域工程师所掌握、理解的,至于系统的架构设计也是基于对Linux、Mysql、Nginx等原理机制足够理解后,战术性的使用。
1) *ht 是哈希表的指针,这里既可以传入一个已存在的HashTable, 也可以通过内核宏ALLOC_HASHTABLE(ht)来自动申请一块HashTable内存。ALLOC_HASHTABLE(ht)相当于ht=emalloc(sizeof(HashTable))
2) nSize 哈希表能拥有的最大数量。通过预先申请好内存的方式,减少哈希表rehash操作。
3) pHashFunction 自定义哈希函数的钩子
4) pDesctructor 哈希表析构的回调函数,当删除一个哈希表的时候,会调用。
5) persistent 对应HashTable.persistent,当设置为true的时候,不会在RSHUTDOWN阶段自动销毁。
我们通过更新哈希表的操作方式,来分析哈希表的操作机制:
[php]
1) 通过哈希算法 times33(Key) & (nTableSize-1) ,生成Key对应的哈希值A,获取arBuckets[A]的值2) 判断arBuckets[A]是否存在,如果存在而且没有哈希冲突,进行数据update(UPDATE_DATA)。如果存在但是Key不相同说明有哈希冲突,在arBuckets[A]链表中寻找Key是否存在,如果存在,执行update操作(UPDATE_DATA)
3) 如果arBuckets[A]不存在,创建新的arBucket[A](INIT_DATA)。或哈希冲突情况下,在arBuckets[A]的链表中找不到Key。创建新的bucket(INIT_DATA),并把新的buckets放在arBucket[A]链表头
4) 维护哈希表的逻辑链表(CONNECT_TO_GLOBAL_DLLIST)。
5) 如果发现新插入元素已经超过HashTable的nTableSize,自动扩容至2倍nTableSize,重新哈希后维护新的HashTable。
3. PHP使用的哈希函数PHP的哈希表是用Times33哈希算法,又称为DJBX33A。这是一个使用比较广泛的对字符串的哈希算法,计算速度快,散列均匀,Perl和Apache都使用了这个算法。算法原理就是不断的乘以33,其算法原型如下:
[php]
PHP在哈希算法上有所优化,使用了(hash<<5)+hash,效率有所提高。至于hash的初始值为什么为一个大素数5381,要数学上来解释了,不是很理解。
4. 操作哈希表的内部函数PHP的变量符号表是通过哈希表来维护,,首先介绍一下再PHP扩展中如何创建一个新的变量。PHP变量介绍,请看我上一篇文章,
这里的zend_hash_update会更新变量符号表。PHP的数组也是用哈希表来维护,下面通过操作一个array来解释如何使用哈希表来才做数组。增加一个关联数组:
[php]
增加一个索引数组:[php]
哈希表的增删改查
[php]
哈希表的遍历
[php]
数组操作函数reset(), each(), current(), next()会用这些函数来实现。
比较,排序
[php]
哈希表有一套排序算法。sort(), asort(), resort(), arsort(), ksort(), krsort()详细请见:
以下这个例子就是这个原理的实现,插入65535个数据需要消耗30秒,而正常情况下仅需要0.01秒。
[php]
结果是
[php]
文章来源:
对于哈希碰撞攻击有2中常见形式:通过POST攻击或通过反序列化攻击。PHP会自动把HTTP包中POST的数据解析成数组$_POST,如果我们构造一个无限大的哈希冲突的值,可以造成拒绝服务攻击。
PHP5.3.9+是通过增加一个限制来尽量避免被此类攻击影响:
[php]