[nginx] 記憶體池與基本容器
一、nginx記憶體池
- 與STL的空間配置器類似,有兩級配置,僅提供了釋放大記憶體塊的介面,小記憶體只分配不釋放,要麼釋放整個記憶體池(nginx程序中有茫茫多記憶體池)
- 記憶體池由三部分組成:小塊記憶體形成的連結串列(並不是ngx_list_t)、大塊記憶體形成的連結串列、以及掛載了一些記憶體池釋放同時釋放的資源(到底是啥,待補充)
- 其中小塊記憶體連結串列每個格子大小相同,剩餘空間不足時再申請格子掛在該連結串列裡,這裡有個特別的點:每個當前格子有個失敗計數器,在給呼叫方分配記憶體時只有當計數器>4後再調整當前格子為下一個格子,目的是當前格子雖然不滿足分配需求,但也有剩餘,可能滿足下一次分配
二、ngx_array_t
// 動態陣列
struct ngx_array_s {
// elts指向陣列的首地址
void *elts;
// nelts是陣列中已經使用的元素個數
ngx_uint_t nelts;
// 每個陣列元素佔用的記憶體大小
size_t size;
// 當前陣列中能夠容納元素個數的總大小
ngx_uint_t nalloc;
// 記憶體池物件
ngx_pool_t *pool;
};
ngx_array_push(ngx_array_t *a)
三、ngx_list_t
nginx的連結串列容器,參考nginx原始碼分析—連結串列結構ngx_list_t,注意:連結串列中的元素是固定大小的陣列,但不是ngx_array_t物件
// ngx_list_part_s是代表ngx_list_t連結串列的一個節點。
// 它自身包含了一個數組,用來存放最終的元素
struct ngx_list_part_s {
void *elts; //連結串列元素elts陣列,陣列申請的空間大小為size*nalloc
ngx_uint_t nelts; //當前已使用的elts個數,一定要小於等於nalloc
ngx_list_part_t *next; //指向ngx_list_t中的下個連結串列part
};
// ngx_list_t結構是一個連結串列,連結串列中每個節點是ngx_list_part_t結構。
// 而ngx_list_part_t中有個elts是一個數組,儲存了任意大小固定的元素,它是由ngx_pool_t分配的連續空間
typedef struct {
ngx_list_part_t *last; //指向連結串列中最後一個元素,其作用相當於尾指標。插入新的節點時,從此開始。
ngx_list_part_t part; //連結串列中第一個元素,其作用相當於頭指標。遍歷時,從此開始。
size_t size; //連結串列中每個元素的大小
ngx_uint_t nalloc; //連結串列的每個ngx_list_part_t中elts陣列的所能容納的最大元素個數
ngx_pool_t *pool; //當前list資料存放的記憶體池
} ngx_list_t;
ngx_list_push(ngx_list_t *l)
連結串列已滿時,從記憶體池pool中新申請一個節點,再申請陣列元素
四、ngx_queue_t
nginx的雙向佇列容器,參考nginx原始碼分析—佇列結構ngx_queue_t,不負責存放資料元素,需要使用方組織資料,幾乎所有操作都由巨集來完成
// 節點既佇列,佇列既節點
struct ngx_queue_s {
ngx_queue_t *prev; //前一個
ngx_queue_t *next; //下一個
};
// 以下為自己組織的程式碼
typedef struct {
int data;
ngx_queue_t queue; //放在最後面
} my_queue_t;
- 支援頭/尾插入和遍歷
- 支援佇列拆分和連結
- 支援佇列排序,以及查詢中間
myque = ngx_palloc(pool, sizeof(ngx_queue_t)); //alloc a queue head
ngx_queue_init(myque); //init the queue
//insert some data into the queue
for (i = 0; i < 100; i++)
{
p = (my_queue_t*)ngx_palloc(pool, sizeof(my_queue_t));
p->data = i;
ngx_queue_init(&p->queue);
//insert this data into the points queue
ngx_queue_insert_head(myque, &p->queue);
}
五、nginx_hash_t
nginx的hash容器,開鏈法,但每個桶裡裝的不是連結串列,而是陣列(不是ngx_array_t容器),這與容器的使用場景有關:初始化時載入所有資料,只有檢索操作,沒有增刪改
//hash元素結構
typedef struct {
void *value; //value,即某個key對應的值,即<key,value>中的value
u_short len; //name長度
u_char name[1]; //<key,value>中的key,字串直接放在&name[0]中
} ngx_hash_elt_t;
//hash結構
typedef struct {
ngx_hash_elt_t **buckets; //hash桶(有size個桶)
ngx_uint_t size; //hash桶個數
} ngx_hash_t;
ngx_hash_init(ngx_hash_init_t *hinit, ngx_hash_key_t *names, ngx_uint_t nelts)
,hash容器初始化函式,其中ngx_hash_init_t
封裝了要初始化的hash物件、桶的最大數目,每個桶內陣列的最大長度bucket_size。初始化的names為所有(key, value),建立容器時會將桶數量從小到大遍歷,直到每個桶裡陣列記憶體<最大長度bucket_size,再將每個元素放入各個桶中。注意,這裡的桶內陣列沒有長度,以ngx_hash_elt_t.value=NULL
為結束條件
六、ngx_hash_wildcard_t
nginx處理字串萬用字元匹配的hash表,重要的應用場景是虛擬主機server name 的匹配,參考nginx原始碼分析之hash的實現,能處理如下模式:
- *.abc.com,匹配www.abc.com、qqq.www.abc.com之類,但不能匹配abc.com
- .abc.com,匹配abc.com、www.abc.com、qqq.www.abc.com之類
- www.abc.*,匹配www.abc.com、www.abc.com.cn
// 萬用字元匹配hash
typedef struct {
// 基本散列表
ngx_hash_t hash;
// 當使用這個ngx_hash_wildcard_t萬用字元散列表作為某容器的元素時,可以使用這個value指標指向使用者資料
void *value;
} ngx_hash_wildcard_t;
// 字串匹配hash集合,應用時使用該結構體
typedef struct {
// 用於精確匹配的基本散列表
ngx_hash_t hash;
// 用於查詢前置萬用字元的散列表
ngx_hash_wildcard_t *wc_head;
// 用於查詢後置萬用字元的散列表
ngx_hash_wildcard_t *wc_tail;
} ngx_hash_combined_t;
ngx_hash_keys_arrays_t
,收集所有的字串對,會按嚴格匹配串、字首串、字尾串存放,其中字首串會反向,然後前/字尾都去掉末尾最後一個字元。在收集字串時,為了判重,在該結構體內分別掛了對應簡易hash(桶為ngx_array_t
),支援add- 嚴格匹配串、字首串、字尾串分別初始化成
ngx_hash_t
放到ngx_hash_combined_t
,注意:其中字首串、字尾串在初始化前需要排序 ngx_hash_wildcard_init(ngx_hash_init_t *hinit, ngx_hash_key_t *names, ngx_uint_t nelts)
,初始化萬用字元hash,會將字串按.分割成多層hash,比如:
- {key = (“com.” , 4 ), key_hash = 0 , value = “220.181.111.147”}
- {key = (“cn.com.baidu.” , 13), key_hash = 0 , value = “220.181.111.147”}
- {key = (“com.baidu.” , 10), key_hash = 0 , value = “220.181.111.147”}
- {key = (“com.google.” , 11), key_hash = 0 , value = “58.63.236.35”}
- 為了標記(key, value)中value是指定向實際的value,還是指向下一級的hash地址,程式碼中實現得很巧妙。由於nginx記憶體是4位元組對齊,故記憶體地址能整除4,即低兩位都為0,故value指標的低兩位可用來標記。其中01表示後面無wildcard雜湊,10表示指向最後一級wildcard雜湊,11表示後面還有很多級wildcard雜湊
ngx_hash_find_combined(ngx_hash_combined_t *hash, ngx_uint_t key, u_char *name, size_t len)
,檢索hash表,順序:嚴格匹配表->字首表->字尾表,所以在(*.baidu.com,1)、(map.baidu.com,2)的集合中,map.baidu.com會匹配到2
ngx_hash_find(ngx_hash_t *hash, ngx_uint_t key, u_char *name, size_t len)
,嚴格匹配函式,桶對映,然後遍歷桶內陣列ngx_hash_find_wc_head(ngx_hash_wildcard_t *hwc, u_char *name, size_t len)
,字首匹配函式,將目標串分割,根據value的標記,遞迴呼叫ngx_hash_find_wc_tail(ngx_hash_wildcard_t *hwc, u_char *name, size_t len)
,字尾匹配函式,將目標串分割,根據value的標記,遞迴呼叫