摘要:相關(guān)系列前面分析了數(shù)組,現(xiàn)在看一下隊列和哈希表的實現(xiàn)。隊列是一個雙向鏈表,實現(xiàn)了一個隊列的操作邏輯。它們都將鏈表節(jié)點塞入數(shù)據(jù)結(jié)構(gòu)。對于常用的解決沖突的方法有線性探測二次探測和開鏈法等。
相關(guān)系列:http://www.codefrom.com/p/nginx
前面分析了ngx_array_t數(shù)組,現(xiàn)在看一下ngx_queue隊列和ngx_hash哈希表的實現(xiàn)。
ngx_queue 隊列ngx_queue_t是一個雙向鏈表,實現(xiàn)了一個隊列的操作邏輯。但是它的結(jié)構(gòu)只行指針的操作,因而在定義自己的節(jié)點時,需要自己定義數(shù)據(jù)結(jié)構(gòu)和分配空間,并包含一個ngx_queue_t類型的成員。
typedef struct ngx_queue_s ngx_queue_t; struct ngx_queue_s { ngx_queue_t *prev; ngx_queue_t *next; };
這和Linux內(nèi)核的數(shù)據(jù)結(jié)構(gòu)很像。它們都將鏈表節(jié)點塞入數(shù)據(jù)結(jié)構(gòu)。Linux內(nèi)核的鏈表這樣定義:
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; }
結(jié)構(gòu)如圖所示:
所以它用fox.list.next指向下一個節(jié)點,用fox.list.prev指向上一個節(jié)點。那我們?nèi)绾螐膌ist_head找到fox的地址呢。內(nèi)核提供了一個container_of()宏可以從鏈表指針找到父結(jié)構(gòu)中包含的任何變量。
#define container_of(ptr, type, member) ({ const typeof( ((type *)0)->member ) *__mptr = (ptr); (type *)( (char *)__mptr - offsetof(type,member) );)
而在Nginx也是效仿采用一樣的宏獲取父結(jié)構(gòu)地址。
#define ngx_queue_data(q, type, link) (type *) ((u_char *) q - offsetof(type, link))用法
它的API定義了初始化,插入,排序,找中位節(jié)點等一系列操作。
用法如下:
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); //初始化內(nèi)存池 int i; // 構(gòu)建隊列 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 ", guy->id, guy->name); } // 排序從頭部輸出 ngx_queue_sort(&yahoo->queue, yahoo_no_cmp); printf("sorting.... "); 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 ", guy->id, guy->name); } ngx_destroy_pool(pool); return 0; }ngx_hash 哈希表
ngx_hash表所用的hash算法為分桶后線性查找法,因而查找效率同key-value對成反比。對于常用的解決沖突的方法有線性探測、二次探測和開鏈法等。這里使用的是最常用的開鏈法(也是STL中使用的方法)。
哈希表整個結(jié)構(gòu)如圖所示:
哈希表用下列數(shù)據(jù)結(jié)構(gòu)進行管理
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思想,和內(nèi)核驅(qū)動的用法相同)一個hash_init。
然后對這個“對象”賦值。
hash = (ngx_hash_t*)ngx_pcalloc(pool, sizeof(hash)); hash_init.hash = hash; // hash結(jié)構(gòu) hash_init.key = &ngx_hash_key_lc; // hash算法函數(shù) hash_init.max_size = 1024*10; // max_size hash_init.bucket_size = 64; //桶的大小 hash_init.name = "yahoo_guy_hash"; hash_init.pool = pool; // 用到的內(nèi)存池 hash_init.temp_pool = NULL;
第一行分配了ngx_hash_t大小的內(nèi)存存儲如下hash結(jié)構(gòu)。
typedef struct { ngx_hash_elt_t **buckets; ngx_uint_t size; } ngx_hash_t;
然后創(chuàng)建一個需要加到hash table中的數(shù)組。
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數(shù)組放到hash_init結(jié)構(gòu)中,即將數(shù)組以hash table形式存儲。
這就是整個hash結(jié)構(gòu)的存儲過程,查找相對簡單,這里不再詳述。
參考Linux Kernel Development. Page71~72
http://www.embedu.org/Column/Column433.htm
tuzhi / 2015-05-31
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/39169.html
摘要:系列文章源碼分析第一篇源碼分析第二篇同步模式源碼分析第三篇異步狀態(tài)源碼分析第四篇歸納總結(jié)前言是在版本中的大更新,利用了閑余時間看了一些源碼,做個小記錄流程圖源碼分析調(diào)用時,會調(diào)用的方法,同時將新的作為參數(shù)傳進會先調(diào)用獲取一個維護兩個時間一個 系列文章 React Fiber源碼分析 第一篇 React Fiber源碼分析 第二篇(同步模式) React Fiber源碼分析 第三篇(...
摘要:與此同時,因新冠疫情的影響使得用戶對移動應(yīng)用程序的需求激增。調(diào)查報告顯示年移動應(yīng)用程序已經(jīng)產(chǎn)生了億美元的收入,預(yù)計到年將產(chǎn)生億美元的收入。 引言 計劃在2021年進...
摘要:月日,谷歌正式發(fā)布了的。到底能不能成為跨平臺開發(fā)終極之選是基于前端誕生的,但是對前端開發(fā)來說,的環(huán)境配置很麻煩,需要原生的平臺知識,還要擔(dān)心遇上網(wǎng)絡(luò)問題?,F(xiàn)在已經(jīng)不是曾經(jīng)的小眾框架,這兩年里它已經(jīng)逐步成長為主流的跨平臺開發(fā)框架之一。 ...
摘要:為什么網(wǎng)頁性能會變高要回答這個問題,需要回頭看是單線程的知識點。在分析的過程中,發(fā)現(xiàn)了的源碼中使用了很多鏈?zhǔn)浇Y(jié)構(gòu),回調(diào)鏈,任務(wù)鏈等,這個主要是為了增刪時性能比較高 系列文章 React Fiber源碼分析 第一篇 React Fiber源碼分析 第二篇(同步模式) React Fiber源碼分析 第三篇(異步狀態(tài)) React Fiber源碼分析 第四篇(歸納總結(jié)) 前言 Rea...
閱讀 2594·2023-04-25 22:09
閱讀 1113·2021-11-17 17:01
閱讀 1881·2021-09-04 16:45
閱讀 2681·2021-08-03 14:02
閱讀 870·2019-08-29 17:11
閱讀 3331·2019-08-29 12:23
閱讀 1152·2019-08-29 11:10
閱讀 3341·2019-08-26 13:48