亚洲韩日午夜视频,欧美日韩在线精品一区二区三区,韩国超清无码一区二区三区,亚洲国产成人影院播放,久草新在线,在线看片AV色

您好,歡迎來到思海網絡,我們將竭誠為您提供優質的服務! 誠征網絡推廣 | 網站備案 | 幫助中心 | 軟件下載 | 購買流程 | 付款方式 | 聯系我們 [ 會員登錄/注冊 ]
促銷推廣
客服中心
業務咨詢
有事點擊這里…  531199185
有事點擊這里…  61352289
點擊這里給我發消息  81721488
有事點擊這里…  376585780
有事點擊這里…  872642803
有事點擊這里…  459248018
有事點擊這里…  61352288
有事點擊這里…  380791050
技術支持
有事點擊這里…  714236853
有事點擊這里…  719304487
有事點擊這里…  1208894568
有事點擊這里…  61352289
在線客服
有事點擊這里…  531199185
有事點擊這里…  61352288
有事點擊這里…  983054746
有事點擊這里…  893984210
當前位置:首頁 >> 技術文章 >> 文章瀏覽
技術文章

PHP內核介紹及擴展開發指南

添加時間:2014-7-9 17:40:12  添加: 思海網絡 

   一、 基礎知識

  本章簡要介紹一些Zend引擎的內部機制,這些知識和Extensions密切相關,同時也可以幫助我們寫出更加高效的PHP代碼。

  1.1 PHP變量的存儲

  1.1.1 zval結構

  Zend使用zval結構來存儲PHP變量的值,該結構如下所示:

  1. typedef union _zvalue_value { 
  2.     long lval;              /* long value */ 
  3.     double dval;                /* double value */ 
  4.     struct { 
  5.         char *val; 
  6.         int len; 
  7.     } str; 
  8.     HashTable *ht;              /* hash table value */ 
  9.     zend_object_value obj; 
  10. } zvalue_value; 
  11.  
  12. struct _zval_struct { 
  13.     /* Variable information */ 
  14.     zvalue_value value;     /* value */ 
  15.     zend_uint refcount; 
  16.     zend_uchar type;            /* active type */ 
  17.     zend_uchar is_ref; 
  18. }; 
  19.  
  20. typedef struct _zval_struct zval; 
  21. <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代碼:

  1. <!--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數據結構定義如下:

  1. typedef struct bucket { 
  2.     ulong h;                // 存放hash 
  3.     uint nKeyLength; 
  4.     void *pData;            // 指向value,是用戶數據的副本 
  5.     void *pDataPtr; 
  6.     struct bucket *pListNext;   // pListNext和pListLast組成 
  7.     struct bucket *pListLast;   // 整個HashTable的雙鏈表 
  8.     struct bucket *pNext;       // pNext和pLast用于組成某個hash對應 
  9.     struct bucket *pLast;       // 的雙鏈表 
  10.     char arKey[1];              // key 
  11. } Bucket; 
  12.  
  13. typedef struct _hashtable { 
  14.     uint nTableSize; 
  15.     uint nTableMask; 
  16.     uint nNumOfElements; 
  17.     ulong nNextFreeElement; 
  18.     Bucket *pInternalPointer;   /* Used for element traversal */ 
  19.     Bucket *pListHead; 
  20.     Bucket *pListTail; 
  21.     Bucket **arBuckets;         // hash數組 
  22.     dtor_func_t pDestructor;    // HashTable初始化時指定,銷毀Bucket時調用 
  23.     zend_bool persistent;       // 是否采用C的內存分配例程 
  24.     unsigned char nApplyCount; 
  25.     zend_bool bApplyProtection; 
  26. #if ZEND_DEBUG 
  27.     int inconsistent; 
  28. #endif 
  29. } HashTable; 

  總的來說,Zend的HashTable是一種鏈表散列,同時也為線性遍歷進行了優化,圖示如下:


  HashTable中包含兩種數據結構,一個鏈表散列和一個雙向鏈表,前者用于進行快速鍵-值查詢,后者方便線性遍歷和排序,一個Bucket同時存在于這兩個數據結構中。

  關于該數據結構的幾點解釋:

  l 鏈表散列中為什么使用雙向鏈表?

  一般的鏈表散列只需要按key進行操作,只需要單鏈表就夠了。但是,Zend有時需要從鏈表散列中刪除給定的Bucket,使用雙鏈表可以非常高效的實現。

  l nTableMask是干什么的?

  這個值用于hash值到arBuckets數組下標的轉換。當初始化一個HashTable,Zend首先為arBuckets數組分配 nTableSize大小的內存,nTableSize取不小于用戶指定大小的最小的2^n,即二進制的10*。nTableMask = nTableSize – 1,即二進制的01*,此時h & nTableMask就恰好落在 [0, nTableSize – 1] 里,Zend就以其為index來訪問arBuckets數組。

  l pDataPtr是干什么的?

  通常情況下,當用戶插入一個鍵值對時,Zend會將value復制一份,并將pData指向value副本。復制操作需要調用Zend內部例程 emalloc來分配內存,這是個非常耗時的操作,并且會消耗比value大的一塊內存(多出的內存用于存放cookie),如果value很小的話,將 會造成較大的浪費。考慮到HashTable多用于存放指針值,于是Zend引入pDataPtr,當value小到和指針一樣長時,Zend就直接將其 復制到pDataPtr里,并且將pData指向pDataPtr。這就避免了emalloc操作,同時也有利于提高Cache命中率。

  arKey大小為什么只有1?為什么不使用指針管理key?

  arKey是存放key的數組,但其大小卻只有1,并不足以放下key。在HashTable的初始化函數里可以找到如下代碼:

  1p = (Bucket *) pemalloc(sizeof(Bucket) - 1 + nKeyLength, ht->persistent);

  可見,Zend為一個Bucket分配了一塊足夠放下自己和key的內存,

  l 上半部分是Bucket,下半部分是key,而arKey“恰好”是Bucket的最后一個元素,于是就可以使用arKey來訪問key了。這種手法在內 存管理例程中最為常見,當分配內存時,實際上是分配了比指定大小要大的內存,多出的上半部分通常被稱為cookie,它存儲了這塊內存的信息,比如塊大 小、上一塊指針、下一塊指針等,baidu的Transmit程序就使用了這種方法。

  不用指針管理key,是為了減少一次emalloc操作,同時也可以提高Cache命中率。另一個必需的理由是,key絕大部分情況下是固定不 變的,不會因為key變長了而導致重新分配整個Bucket。這同時也解釋了為什么不把value也一起作為數組分配了——因為value是可變的。

  1.2.2 PHP數組

  關于HashTable還有一個疑問沒有回答,就是nNextFreeElement是干什么的?

  不同于一般的散列,Zend的HashTable允許用戶直接指定hash值,而忽略key,甚至可以不指定key(此 時,nKeyLength為0)。同時,HashTable也支持append操作,用戶連hash值也不用指定,只需要提供value,此時,Zend 就用nNextFreeElement作為hash,之后將nNextFreeElement遞增。

  HashTable的這種行為看起來很奇怪,因為這將無法按key訪問value,已經完全不是個散列了。理解問題的關鍵在于,PHP數組就是 使用HashTable實現的——關聯數組使用正常的k-v映射將元素加入HashTable,其key為用戶指定的字符串;非關聯數組則直接使用數組下 標作為hash值,不存在key;而當在一個數組中混合使用關聯和非關聯時,或者使用array_push操作時,就需要用 nNextFreeElement了。

  再來看value,PHP數組的value直接使用了zval這個通用結構,pData指向的是zval*,按照上一節的介紹,這個zval*將直接存儲在pDataPtr里。由于直接使用了zval,數組的元素可以是任意PHP類型。

  數組的遍歷操作,即foreach、each等,是通過HashTable的雙向鏈表來進行的,pInternalPointer作為游標記錄了當前位置。

  1.2.3 變量符號表

  除了數組,HashTable還被用來存儲許多其他數據,比如,PHP函數、變量符號、加載的模塊、類成員等。

  一個變量符號表就相當于一個關聯數組,其key是變量名(可見,使用很長的變量名并不是個好主意),value是zval*。

  在任一時刻PHP代碼都可以看見兩個變量符號表——symbol_table和active_symbol_table——前者用于存儲全局變 量,稱為全局符號表;后者是個指針,指向當前活動的變量符號表,通常情況下就是全局符號表。但是,當每次進入一個PHP函數時(此處指的是用戶使用PHP 代碼創建的函數),Zend都會創建函數局部的變量符號表,并將active_symbol_table指向局部符號表。Zend總是使用 active_symbol_table來訪問變量,這樣就實現了局部變量的作用域控制。

  但如果在函數局部訪問標記為global的變量,Zend會進行特殊處理——在active_symbol_table中創建symbol_table中同名變量的引用,如果symbol_table中沒有同名變量則會先創建。

  1.3 內存和文件

  程序擁有的資源一般包括內存和文件,對于通常的程序,這些資源是面向進程的,當進程結束后,操作系統或C庫會自動回收那些我們沒有顯式釋放的資源。

  但是,PHP程序有其特殊性,它是基于頁面的,一個頁面運行時同樣也會申請內存或文件這樣的資源,然而當頁面運行結束后,操作系統或C庫也許不 會知道需要進行資源回收。比如,我們將php作為模塊編譯到apache里,并且以prefork或worker模式運行apache。這種情況下 apache進程或線程是復用的,php頁面分配的內存將永駐內存直到出core。

  為了解決這種問題,Zend提供了一套內存分配API,它們的作用和C中相應函數一樣,不同的是這些函數從Zend自己的內存池中分配內存,并 且它們可以實現基于頁面的自動回收。在我們的模塊中,為頁面分配的內存應該使用這些API,而不是C例程,否則Zend會在頁面結束時嘗試efree掉我 們的內存,其結果通常就是crush。

  emalloc()

  efree()

  estrdup()

  estrndup()

  ecalloc()

  erealloc()

  另外,Zend還提供了一組形如VCWD_xxx的宏用于替代C庫和操作系統相應的文件API,這些宏能夠支持PHP的虛擬工作目錄,在模塊代 碼中應該總是使用它們。宏的具體定義參見PHP源代碼”TSRM/tsrm_virtual_cwd.h”。可能你會注意到,所有那些宏中并沒有提供 close操作,這是因為close的對象是已打開的資源,不涉及到文件路徑,因此可以直接使用C或操作系統例程;同理,read/write之類的操作 也是直接使用C或操作系統的例程。

關鍵字:PHP、模塊、內核

分享到:

頂部 】 【 關閉
版權所有:佛山思海電腦網絡有限公司 ©1998-2024 All Rights Reserved.
聯系電話:(0757)22630313、22633833
中華人民共和國增值電信業務經營許可證: 粵B1.B2-20030321 備案號:粵B2-20030321-1
網站公安備案編號:44060602000007 交互式欄目專項備案編號:200303DD003  
察察 工商 網安 舉報有獎  警警  手機打開網站