淺談nginx記憶體池(四)
Nginx的緩衝區設計也是非常靈活的:
(1)可以自定義管理業務層面的緩衝區連結串列。
(2)可以將空閒的緩衝區連結串列交還給記憶體池pool->chain結構。
緩衝區ngx_buf_t是nginx處理大資料的關鍵資料結構,他既應用於記憶體資料,也應用於磁碟資料。
下面就是關於nginx中buf的實現
---------->buf分為兩種型別:
(1)一種是file;
(2)一種是memory;
我們傳送往套接字或者其他的裝置傳送東西時,我們是先將資料放大buf中,然後當裝置或者套接字準備好了,我們就會從buf中讀取。
ngx_buf_s具體結構如下:
struct ngx_buf_s {
u_char *pos;//待處理資料的開始標記(表示已經執行的資料的位置)
///last和上面記憶體池中last一樣,也就是使用的記憶體的最後一個位元組的指標
u_char *last;//待處理資料的結尾標記
///檔案指標(處理檔案時,待處理檔案的開始標記和結尾標記)
off_t file_pos;
off_t file_last;
///buf的開始指標(緩衝區開始的指標地址和緩衝區結尾的指標地址)
u_char *start; /* start of buffer */
u_char *end; /* end of buffer */
///這裡表示這個buf從屬於那個模組。
ngx_buf_tag_t tag;//緩衝區標記地址,是一個void*的指標
ngx_file_t *file;//引用的檔案
ngx_buf_t *shadow;
///一些標記
/* the buf's content could be changed */
unsigned temporary:1; //標誌位,為1時,可修改
///在記憶體中是不能改變的。
unsigned memory:1; //標誌位,為1時,記憶體只讀
///是否是mmap的記憶體
unsigned mmap:1; //標誌位,為1時,mmap對映過來的記憶體不可修改
unsigned recycled:1; //標誌位,為1時,可回收
///一些標誌位
unsigned in_file:1; //標誌位,為1時,表示處理的是檔案
unsigned flush:1; //標誌位,為1時,表示需要進行fflush操作
unsigned sync:1; //標誌位,為1時,表示可以進行同步操作,容易引起堵塞
unsigned last_buf:1;//標誌位,為1時,表示緩衝區連結串列ngx_chain_t上的最後一塊待處理緩衝區
unsigned last_in_chain:1; //標誌位,為1時,表示為緩衝區連結串列ngx_chain_t上的最後一塊緩衝區
unsigned last_shadow:1; //標誌位,為1時,表示是否是最後一個影子緩衝區
unsigned temp_file:1; //標誌位,為1時,表示當前緩衝區是否屬於臨時檔案
int num;
};
說明:
(1)從這個資料結構中,可以看出ngx_buf_s結構,既可以處理記憶體,也可以資料檔案。
(2)Nginx使用了位域的方法,節省儲存空間。
(3)每個buf都記錄了開始和結束點以及未處理的開始和結束點,因為緩衝區的記憶體申請了之後,是可以被複用的。
(4)所有緩衝區需要的資料結構以及緩衝區的buf記憶體塊都會被分配到記憶體池上面。
如圖:
(1)nginx的緩衝區資料結構主要包含了連結串列資料結構ngx_chain_t和buf資料結構ngx_buf_s。
(2)nginx可以在自定義的業務層面管理繁忙busy和空閒free的緩衝區連結串列結構,可以對緩衝區的連結串列結構和buf結構進行管理。
(3)如果緩衝區連結串列需要被回收,則會放到nginx記憶體池的pool->chain連結串列上。
(4)緩衝區是nginx用的非常多的一種資料結構,主要用於接收和輸出HTTP的資料資訊,所以對nginx的緩衝區的資料結構深入理解非常有必要。
下面就可以看看我們之前在ngx_pool_t中沒有說明的一個結構--->ngx_chain_t:
緩衝區連結串列結構 ngx_chain_t
typedef struct ngx_chain_s ngx_chain_t;
/*
* 緩衝區連結串列結構,放在pool記憶體池上面
*/
struct ngx_chain_s {
ngx_buf_t *buf;
ngx_chain_t *next;
};
1. 是否還記得記憶體池結構中,有一個數據結構pool->chain就是儲存空閒的緩衝區連結串列的。
2. Nginx的緩衝區ngx_buf_t,通過ngx_chain_t連結串列結構進行關聯和管理。
3. 通過連結串列的方式實現buf有一個非常大的好處:如果一次需要緩衝區的記憶體很大,那麼並不需要分配一塊完整的記憶體,只需要將緩衝區串起來就可以了。
2.接下來我們來看看如何建立一個buf,在nginx中一般都是呼叫ngx_create_temp_buf來建立buf。函式也比較簡單,就是從pool中分配記憶體然後初始化相關域。
具體程式碼如下:
ngx_buf_t * ngx_create_temp_buf(ngx_pool_t *pool, size_t size)
{
ngx_buf_t *b;
///calloc一個buf,可以看到它呼叫的是calloc,也就是說都會清0.
b = ngx_calloc_buf(pool);
if (b == NULL) {
return NULL;
}
///然後從記憶體池中分配一塊記憶體。並將這塊記憶體連結到b->start.
b->start = ngx_palloc(pool, size);
/*
最終呼叫的是記憶體池pool,開闢一段記憶體用作緩衝區,主要放置ngx_buf_t結構體:
//set by ngx_calloc_buf():
* b->file_pos = 0;
* b->file_last = 0;
* b->file = NULL;
* b->shadow = NULL;
* b->tag = 0;
* and flags
*/
if (b->start == NULL) {
return NULL;
}
///設定相關的域。
b->pos = b->start;
b->last = b->start;
///設定打消
b->end = b->last + size;
b->temporary = 1;
return b;
}