相關係列:http://www.codefrom.com/p/nginxnode
前面分析了ngx_array_t
數組,如今看一下ngx_queue
隊列和ngx_hash哈希表的實現。nginx
ngx_queue_t
是一個雙向鏈表,實現了一個隊列的操做邏輯。可是它的結構只行指針的操做,於是在定義本身的節點時,須要本身定義數據結構和分配空間,幷包含一個ngx_queue_t
類型的成員。算法
typedef struct ngx_queue_s ngx_queue_t; struct ngx_queue_s { ngx_queue_t *prev; ngx_queue_t *next; };
這和Linux內核的數據結構很像。它們都將鏈表節點塞入數據結構。Linux內核的鏈表這樣定義:數組
struct list_head { struct list_head *next; struct list_head *prev; }
使用的時候數據結構
struct fox { unsigned long tail_length; unsigned long weight; bool is_fantastic; struct list_head list; }
結構如圖所示:函數
因此它用fox.list.next指向下一個節點,用fox.list.prev指向上一個節點。那咱們如何從list_head找到fox的地址呢。內核提供了一個container_of()
宏能夠從鏈表指針找到父結構中包含的任何變量。ui
#define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr);\ (type *)( (char *)__mptr - offsetof(type,member) );)
而在Nginx也是效仿採用同樣的宏獲取父結構地址。spa
#define ngx_queue_data(q, type, link) \ (type *) ((u_char *) q - offsetof(type, link))
它的API定義了初始化,插入,排序,找中位節點等一系列操做。指針
用法以下:code
typedef struct yahoo_s { ngx_queue_t queue; } yahoo_t; typedef struct yahoo_guy_s { ngx_uint_t id; u_char* name; ngx_queue_t queue; } yahoo_guy_t; ngx_int_t yahoo_no_cmp(const ngx_queue_t* p, const ngx_queue_t* n) { yahoo_guy_t *pre, *next; pre = (yahoo_guy_t*) ngx_queue_data(p, yahoo_guy_t, queue); next = (yahoo_guy_t*) ngx_queue_data(n, yahoo_guy_t, queue); return ((pre->id > next->id) ? 1:0); } int main() { ngx_pool_t* pool; yahoo_guy_t* guy; ngx_queue_t* q; yahoo_t* yahoo; pool= ngx_create_pool(1024*10, NULL); //初始化內存池 int i; // 構建隊列 const ngx_str_tnames[] = { ngx_string("rainx"), ngx_string("xiaozhe"), ngx_string("zhoujian")} ; const in ids[] = {4611, 8322, 6111}; yahoo = ngx_palloc(pool, sizeof(yahoo_t)); ngx_queue_init(&yahoo->queue); //初始化queue for(i = 0; i < 3; i++) { guy = (yahoo_guy_t*) ngx_palloc(pool, sizeof(yahoo_guy_t)); guy->id = ids[i]; guy->name = (u_char*) ngx_pstrdup(pool, (ngx_str_t*) &(names[i]) ); ngx_queue_init(&guy->queue); // 從頭部進入隊列 ngx_queue_insert_head(&yahoo->queue, &guy->queue); } // 從尾部遍歷輸出 for(q = ngx_queue_last(&yahoo->queue); q != ngx_queue_sentinel(&yahoo->queue); q = ngx_queue_prev(q) ) { guy = ngx_queue_data(q, yahoo_guy_t, queue); printf("No. %d guy in yahoo is %s \n", guy->id, guy->name); } // 排序從頭部輸出 ngx_queue_sort(&yahoo->queue, yahoo_no_cmp); printf("sorting....\n"); for(q = ngx_queue_prev(&yahoo->queue); q != ngx_queue_sentinel(&yahoo->queue); q = ngx_queue_last(q) ) { guy = ngx_queue_data(q, yahoo_guy_t, queue); printf("No. %d guy in yahoo is %s \n", guy->id, guy->name); } ngx_destroy_pool(pool); return 0; }
ngx_hash表所用的hash算法爲分桶後線性查找法,於是查找效率同key-value對成反比。對於經常使用的解決衝突的方法有線性探測、二次探測和開鏈法等。這裏使用的是最經常使用的開鏈法(也是STL中使用的方法)。
哈希表整個結構如圖所示:
哈希表用下列數據結構進行管理
typedef struct { ngx_hash_t *hash; ngx_hash_key_pt key; ngx_uint_t max_size; ngx_uint_t bucket_size; char *name; ngx_pool_t *pool; ngx_pool_t *temp_pool; } ngx_hash_init_t;
在使用過程當中,先會用ngx_hash_init_t
實例化(相似於OOP思想,和內核驅動的用法相同)一個hash_init
。
而後對這個「對象」賦值。
hash = (ngx_hash_t*)ngx_pcalloc(pool, sizeof(hash)); hash_init.hash = hash; // hash結構 hash_init.key = &ngx_hash_key_lc; // hash算法函數 hash_init.max_size = 1024*10; // max_size hash_init.bucket_size = 64; //桶的大小 hash_init.name = "yahoo_guy_hash"; hash_init.pool = pool; // 用到的內存池 hash_init.temp_pool = NULL;
第一行分配了ngx_hash_t
大小的內存存儲以下hash結構。
typedef struct { ngx_hash_elt_t **buckets; ngx_uint_t size; } ngx_hash_t;
而後建立一個須要加到hash table中的數組。
ngx_hash_key_t* arr_node; //存儲鍵值對+hash值 elements = ngx_array_create(pool, 32, sizeof(ngx_hash_key_t)); for(i = 0; i < 3; i++) { arr_node = (ngx_hash_key_t*) ngx_array_push(elements); arr_node->key = (names[i]); arr_node->key_hash = ngx_hash_key_lc(arr_node->key.data, \ arr_node->key.len); arr_node->value = (void*)descs[i]; } ngx_hash_init(&hash_init, (ngx_hash_key_t*) elements->elts, \ elements->nelts)
最後將elements數組放到hash_init結構中,即將數組以hash table形式存儲。
這就是整個hash結構的存儲過程,查找相對簡單,這裏再也不詳述。
tuzhi / 2015-05-31