|
本章簡要介紹一些Zend引擎的內部機制,這些知識和Extensions密切相關,同時也可以幫助我們寫出更加高效的php代碼。
1.1 php變量的存儲
1.1.1 zval結構
Zend使用zval結構來存儲php變量的值,該結構如下所示:
復制代碼 代碼如下:
typedef union _zvalue_value {
long lval; /* long value */
double dval; /* double value */
struct {
char *val;
int len;
} str;
HashTable *ht; /* hash table value */
zend_object_value obj;
} zvalue_value;
struct _zval_struct {
/* Variable information */
zvalue_value value; /* value */
zend_uint refcount;
zend_uchar type; /* active type */
zend_uchar is_ref;
};
typedef struct _zval_struct zval;
<span id="more-597"></span>Zend根據type值來決定訪問value的哪個成員,可用值如下:
IS_NULLN/A
IS_LONG對應value.lval
IS_DOUBLE對應value.dval
IS_STRING對應value.str
IS_ARRAY對應value.ht
IS_OBJECT對應value.obj
IS_BOOL對應value.lval.
IS_RESOURCE對應value.lval
根據這個表格可以發現兩個有意思的地方:首先是php的數組其實就是一個HashTable,這就解釋了為什么php能夠支持關聯數組了;其次,Resource就是一個long值,它里面存放的通常是個指針、一個內部數組的index或者其它什么只有創建者自己才知道的東西,可以將其視作一個handle
1.1.1 引用計數
引用計數在垃圾收集、內存池以及字符串等地方應用廣泛,Zend就實現了典型的引用計數。多個php變量可以通過引用計數機制來共享同一份zval,zval中剩余的兩個成員is_ref和refcount就用來支持這種共享。
很明顯,refcount用于計數,當增減引用時,這個值也相應的遞增和遞減,一旦減到零,Zend就會回收該zval。
那么is_ref呢?
1.1.2 zval狀態
在php中,變量有兩種――引用和非引用的,它們在Zend中都是采用引用計數的方式存儲的。對于非引用型變量,要求變量間互不相干,修改一個變量時,不能影響到其他變量,采用Copy-On-Write機制即可解決這種沖突――當試圖寫入一個變量時,Zend若發現該變量指向的zval被多個變量共享,則為其復制一份refcount為1的zval,并遞減原zval的refcount,這個過程稱為“zval分離”。然而,對于引用型變量,其要求和非引用型相反,引用賦值的變量間必須是捆綁的,修改一個變量就修改了所有捆綁變量。
可見,有必要指出當前zval的狀態,以分別應對這兩種情況,is_ref就是這個目的,它指出了當前所有指向該zval的變量是否是采用引用賦值的――要么全是引用,要么全不是。此時再修改一個變量,只有當發現其zval的is_ref為0,即非引用時,Zend才會執行Copy-On-Write。
1.1.3 zval狀態切換
當在一個zval上進行的所有賦值操作都是引用或者都是非引用時,一個is_ref就足夠應付了。然而,世界總不會那么美好,php無法對用戶進行這種限制,當我們混合使用引用和非引用賦值時,就必須要進行特別處理了。
情況I、看如下php代碼:
<!--p $a = 1; $b = &$a; $c = &$b; $d = $c; // 在一堆引用賦值中,插入一個非引用-->
全過程如下所示:
這段代碼的前三句將把a、b和c指向一個zval,其is_ref=1, refcount=3;第四句是個非引用賦值,通常情況下只需要增加引用計數即可,然而目標zval屬于引用變量,單純的增加引用計數顯然是錯誤的, Zend的解決辦法是為d單獨生成一份zval副本。
全過程如下所示:

1.1.1 參數傳遞
php函數參數的傳遞和變量賦值是一樣的,非引用傳遞相當于非引用賦值,引用傳遞相當于引用賦值,并且也有可能會導致執行zval狀態切換。這在后面還將提到。
1.2 HashTable結構
HashTable是Zend引擎中最重要、使用最廣泛的數據結構,它被用來存儲幾乎所有的東西。
1.1.1 數據結構
HashTable數據結構定義如下:
復制代碼 代碼如下:
typedef struct bucket {
ulong h; // 存放hash
uint nKeyLength;
void *pData; // 指向value,是用戶數據的副本
void *pDataPtr;
struct bucket *pListNext; // pListNext和pListLast組成
struct bucket *pListLast; // 整個HashTable的雙鏈表
struct bucket *pNext; // pNext和pLast用于組成某個hash對應
struct bucket *pLast; // 的雙鏈表
char arKey[1]; // key
} Bucket;
typedef struct _hashtable {
uint nTableSize;
uint nTableMask;
uint nNumOfElements;
ulong nNextFreeElement;
Bucket *pInternalPointer; /* Used for element traversal */
Bucket *pListHead;
Bucket *pListTail;
Bucket **arBuckets; // hash數組
dtor_func_t pDestructor; // HashTable初始化時指定,銷毀Bucket時調用
zend_bool persistent; // 是否采用C的內存分配例程
unsigned char nApplyCount;
zend_bool bApplyProtection;
#if ZEND_DEBUG
int inconsistent;
#endif
} HashTable;
總的來說,Zend的HashTable是一種鏈表散列,同時也為線性遍歷進行了優化,圖示如下:

關于該數據結構的幾點解釋:
l 鏈表散列中為什么使用雙向鏈表?
一般的鏈表散列只需要按key進行操作,只需要單鏈表就夠了。但是,Zend有時需要從鏈表散列中刪除給定的Bucket,使用雙鏈表可以非常高效的實現。
l nTableMask是干什么的?
這個值用于hash值到arBuckets數組下標的轉換。當初始化一個HashTable,Zend首先為arBuckets數組分配nTableSize大小的內存,nTableSize取不小于用戶指定大小的最小的2^n,即二進制的10*。nTableMask = nTableSize 主站蜘蛛池模板: 天天色影院 | 91刘亦菲精品福利在线 | 91网址在线播放 | 麻豆国产| 国产精品亚洲片夜色在线 | 思思久久99热免费精品6 | 成人国产在线视频 | 免费a级欧美黑寡妇a观看 | 浮力影院第一页小视频国产在线观看免费 | 亚洲一区二区免费视频 | 在线视频观看一区 | 最近中文字幕无吗免费视频 | www色视频在线观看 www色在线 | 激情影院免费看 | 97国产精品视频观看一 | 日韩一区二区国色天香 | 久久久网久久久久合久久久久 | 免费看的www视频网站视频 | 欧美猛妇色xxxxxbbbb | 亚洲成人一区二区 | 精品成人乱色一区二区 | 一区视频在线播放 | 四虎免费网址 | 黄黄的网站在线观看 | 人成精品视频三区二区一区 | 欧美色涩 | 欧美一区二区三区婷婷月色 | 国产精品自拍亚洲 | 日本在线www | 成人网在线免费观看 | 好吊操这里只有精品 | 亚洲国产最新在线一区二区 | 欧美成年黄网站色视频 | 一区二区三区鲁丝不卡麻豆 | 九色91精品国产网站 | 欧美日韩一区二区三在线 | 97国内免费久久久久久久久久 | 91在线成人 | 天天舔天天色 | 国产三级网站在线观看 | 免费大学生国产在线观看p 免费的成人a视频在线观看 |