導語:通過網上閱讀查閱,盡量把nginx源碼這一部分整理完善,讓以後想要學習nginx的同學也可以快速上手。這一節涉及src/core/ngx_buf.h|c的資料結構,其次,看一些大牛文章時候 get 了一個好的畫圖工具- graphviz 和一個拍照app-掃描全能王,可以幫助理清源碼的思路。
一、結構體、相關宏定義、函數聲明梳理
分析采用nginx-1.6.2源碼。在src/core/ngx_buf.h|c中的ngx_buf_t 結構體還是很重要,看源碼的過程中遇到幾次,确實有必要好好理理。ngx_buf_t 包含于ngx_chain_t 結構體中。
// 這個就是連結清單的形式,還是把ngx_buf_t串起來,
// 主要作用就是接受nginx伺服器的http請求標頭、包體、以及響應用戶端的應答標頭、包體
// 都會放在chain連結清單緩沖區
struct ngx_chain_s {
ngx_buf_t *buf;
ngx_chain_t *next;
};
接着是ngx_buf_t結構體,ngx_buf_t 就是ngx_chain_t連結清單每個節點的實際資料,緩沖區ngx_buf_t是nginx處理大資料的關鍵資料結構,它既可以應用于記憶體也可以用于磁盤資料注釋。裡面的參數設定,考慮的情況真多,而且最後标志位都隻占1位,可以說對記憶體利用锱铢必較,值得借鑒學習。
typedef void * ngx_buf_tag_t;
typedef struct ngx_buf_s ngx_buf_t;
// ngx_buf_t 就是ngx_chain_t連結清單每個節點的實際資料,
// 緩沖區ngx_buf_t是nginx處理大資料的關鍵資料結構,它既可以應用于記憶體也可以用于磁盤資料
struct ngx_buf_s {
// pos指向的是這段資料在記憶體中的開始位置
u_char *pos;
// last指向的是這段資料在記憶體中的結束位置
u_char *last;
// file_pos指向這段資料的開始位置在檔案中的偏移量
off_t file_pos;
// file_last指向這段資料的結束位置在檔案中的偏移量
off_t file_last;
// 如果ngx_buf_t 緩沖區用于記憶體,那麼start指向這段記憶體的起始位址
u_char *start; /* start of buffer */
// 如果ngx_buf_t 緩沖區用于記憶體,那麼end指向這段記憶體的結束位址
u_char *end; /* end of buffer */
// 實際上就是一個void *類型的指針。使用者可以關聯任何對象上去
// 由哪個子產品使用就執行哪個子產品的ngx_module_t結構
ngx_buf_tag_t tag;
// 當buf所包含的内容在檔案中時,file字段指向對應的檔案對象上去
ngx_file_t *file;
// 當這個buf完整copy了另外一個buf的所有字段的時候,那麼這個兩個buf指向的實際上是同一塊記憶體,或者是
// 同一個檔案的同一部分,此時這兩個buf的shadow字段指向對方的。釋放的時候特别注意。
ngx_buf_t *shadow;
// 為1表示該buf所包含的内容是一個使用者建立的記憶體塊中,并且可以被在filter處理的過程中進行變更,
// 不會造成問題
unsigned temporary:1;
/*the buf's content is in a memory cache or in a read only memory
* and must not be changed
*
* 為1表示該buf所包含的内容是在記憶體中,但是這些内容卻不能被進行處理的filter進行變更
*/
unsigned memory:1;
/* the buf's content is mmap()ed and must not be changed
* 為1表示該buf所包含的内容是在記憶體中,是通過mmap使用記憶體映射從檔案中映射到記憶體中的,
* 這些内容不能進行變更
*/
unsigned mmap:1;
// 可以回收,也就是這個buf是可以釋放的
unsigned recycled:1;
// 為1時表示該buf所包含的内容是在檔案中
unsigned in_file:1;
// 遇到有flush字段被設定為1的buf的chain,則該chain的資料即便不是最後結束的資料,也會輸出 ??
unsigned flush:1;
/* 對于操作這塊緩沖區是否使用同步方式,需要謹慎考慮,這可能會阻塞nginx程序,nginx中所有的操作
* 幾乎都是異步的,這是它支援高并發的關鍵。
*/
unsigned sync:1;
// 資料被多個chain傳遞給過濾器,此字段為1表明這是最後一個buf
unsigned last_buf:1;
// 在目前的chain裡面,此buf是最後一個。
unsigned last_in_chain:1;
// 在建立一個buf的shadow的時候,通常将新建立的一個buf的last_shadow置為1
unsigned last_shadow:1;
// 由于記憶體使用的限制,有時候一些buf的内容需要被寫到磁盤上的臨時檔案中去,這個時候就設定次标志
unsigned temp_file:1;
// 這個參數還沒有弄清楚 ????
/* STUB */ int num;
};
來一張手繪圖清晰明了,這個就是用掃描全能王app拍的,我畫的太醜了,能了解清除意思就可以
ngx_bufs_t結構體
// ngx_bufs_t起到了一個管理作用,用于說明目前使用的bufs的數量和每個buf的存儲空間
// 在ngx_create_chain_of_bufs可以看到怎麼使用
typedef struct {
ngx_int_t num;
size_t size;
} ngx_bufs_t;
宏定義和部分函數聲明解釋,一些函數涉及body filter(包體過濾)http的東西先不管,主要說資料結構。nginx代碼看起來就很舒服,很多資料類型,宏定義,哪怕一個很簡單的操作都要用nginx的标準封裝,也是值得學習的地方
#define NGX_CHAIN_ERROR (ngx_chain_t *) NGX_ERROR
// 傳回這個buf裡面的内容是否在記憶體裡
#define ngx_buf_in_memory(b) (b->temporary || b->memory || b->mmap)
// 傳回這個buf裡面的内容是否僅僅在記憶體裡,并且沒有在檔案裡
#define ngx_buf_in_memory_only(b) (ngx_buf_in_memory(b) && !b->in_file)
// 傳回這個buf是否是一個特殊的buf,隻含有特殊的标志和沒有包含真正的資料
#define ngx_buf_special(b) \
((b->flush || b->last_buf || b->sync) \
&& !ngx_buf_in_memory(b) && !b->in_file)
// 傳回這個buf是否是一個之包含sync标志而不包含真正資料的特殊buf
#define ngx_buf_sync_only(b) \
(b->sync \
&& !ngx_buf_in_memory(b) && !b->in_file && !b->flush && !b->last_buf)
// 傳回該buf所包含資料的大小,不管這個資料是在檔案裡還是記憶體裡
#define ngx_buf_size(b) \
(ngx_buf_in_memory(b) ? (off_t) (b->last - b->pos): \
(b->file_last - b->file_pos))
// 對于建立temporary字段為1的buf(内容可以被後續的filter子產品進行修改),直接用ngx_create_temp_buf個函數建立
// 其中pool配置設定該buf和buf使用的記憶體,size是該buf使用的記憶體大小
ngx_buf_t *ngx_create_temp_buf(ngx_pool_t *pool, size_t size);
ngx_chain_t *ngx_create_chain_of_bufs(ngx_pool_t *pool, ngx_bufs_t *bufs);
#define ngx_alloc_buf(pool) ngx_palloc(pool, sizeof(ngx_buf_t))
#define ngx_calloc_buf(pool) ngx_pcalloc(pool, sizeof(ngx_buf_t))
// 該函數建立一個ngx_chain_t對象,并傳回指向對象的指針,失敗傳回NULL
ngx_chain_t *ngx_alloc_chain_link(ngx_pool_t *pool);
// 該函數釋放一個ngx_chain_t的對象,如果要釋放整個chain,則疊代此連結清單
// 對ngx_chain_t的釋放,并不是真正的釋放,而是把這個對象挂在pool對象一個叫chain的
// 字段對應的chain上,以供下次可以快速取到
#define ngx_free_chain(pool, cl) \
cl->next = pool->chain; \
pool->chain = cl
二、函數的解釋 , 函數在/src/core/ngx_buf.c檔案中。裡面的函數都比較簡單,說的徹底一點,還是指針指來指去
#include <ngx_config.h>
#include <ngx_core.h>
// 用ngx_create_temp_buf函數在記憶體池上建立一個臨時緩沖區
ngx_buf_t *ngx_create_temp_buf(ngx_pool_t *pool, size_t size)
{
ngx_buf_t *b;
b = ngx_calloc_buf(pool); // 從記憶體池pool中建立一個ngx_buf_t的大小的記憶體,在ngx_buf.h中宏定義了
if (b == NULL) {
return NULL;
}
b->start = ngx_palloc(pool, size);// 開辟緩沖區空間
if (b->start == NULL) {
return NULL;
}
/*
* set by ngx_calloc_buf():
*
* b->file_pos = 0;
* b->file_last = 0;
* b->file = NULL;
* b->shadow = NULL;
* b->tag = 0;
* and flags
*/
// 起始位置pos和start
b->pos = b->start;
b->last = b->start;
b->end = b->last + size; // end為緩沖區末尾
b->temporary = 1; // 臨時緩沖區,記憶體可以被修改
return b;
}
// 建立緩沖區連結清單,如果有直接拿,麼有從pool請求一段空間
ngx_chain_t *ngx_alloc_chain_link(ngx_pool_t *pool)
{
ngx_chain_t *cl;
cl = pool->chain;
if (cl) {
//因為c1将會被使用,是以指向c1的下一個
pool->chain = cl->next;
return cl;
}
cl = ngx_palloc(pool, sizeof(ngx_chain_t));
if (cl == NULL) {
return NULL;
}
return cl;
}
// ngx_create_temp_buf隻是建立了單個緩沖區空間。如果要建立一片連續的緩沖區,
// 就由連結清單統一管理,則需要使用ngx_create_chain_of_bus函數
// 從記憶體池pool中建立一個bufs->num長度的buf->size大小的連結清單,傳回ngx_chain_t連結清單頭節點
ngx_chain_t *ngx_create_chain_of_bufs(ngx_pool_t *pool, ngx_bufs_t *bufs)
{
u_char *p;
ngx_int_t i;
ngx_buf_t *b;
ngx_chain_t *chain, *cl, **ll;
p = ngx_palloc(pool, bufs->num * bufs->size); // 上面描述了
if (p == NULL) {
return NULL;
}
// 這裡用一個二級指針儲存第一個節點指針位址,同樣也儲存了後面節點指針位址,這麼做
// 少了一次循環内的判斷,否則我們需要判斷出第一次循環,并給chain指派
ll = &chain;
for (i = 0; i < bufs->num; i++) {
b = ngx_calloc_buf(pool); // 在記憶體池中建立ngx_buf_t對象,在ngx_buf.h頭檔案宏定義了
if (b == NULL) {
return NULL;
}
/*
* set by ngx_calloc_buf():
*
* b->file_pos = 0;
* b->file_last = 0;
* b->file = NULL;
* b->shadow = NULL;
* b->tag = 0;
* and flags
*
*/
// 初始化每個buf緩沖區的起始位置
b->pos = p;
b->last = p;
b->temporary = 1;
b->start = p;
// 此時p會指向下一個緩沖區
p += bufs->size;
b->end = p;
// 建立一個ngx_chain_t節點
cl = ngx_alloc_chain_link(pool);
if (cl == NULL) {
return NULL;
}
// 将ngx_chain_t與buf關聯起來
cl->buf = b;
// ll儲存的上一個節點指針的指針,指派相當于給上一個節點next指針指派
*ll = cl;
// 取本節點next指針位址,用于下次循環使用
ll = &cl->next;
}
*ll = NULL;
// 傳回ngx_chain_t連結清單的頭節點
return chain;
}
// 将其他緩沖區拷貝到已有緩沖區末尾,也就是in連結清單插入到chain連結清單末尾
ngx_int_t ngx_chain_add_copy(ngx_pool_t *pool, ngx_chain_t **chain, ngx_chain_t *in)
{
ngx_chain_t *cl, **ll;
ll = chain;
// 找到chain連結清單的結尾,然後給ll
for (cl = *chain; cl; cl = cl->next) {
ll = &cl->next;
}
// 周遊in,并且建立配置設定chain的基本節點,并将其buf指向in的部分
while (in) {
cl = ngx_alloc_chain_link(pool);
if (cl == NULL) {
return NGX_ERROR;
}
cl->buf = in->buf; // 開始複制
*ll = cl; // 将next指向c1
ll = &cl->next; // 将ll指針移向c1的next
in = in->next; // in向下移動
}
*ll = NULL; // 最後一個節點指向NULL
return NGX_OK;
}
// 擷取一個可用的ngx_chain_t節點,上面需要有一個可用的ngx_buf_t
ngx_chain_t *ngx_chain_get_free_buf(ngx_pool_t *p, ngx_chain_t **free)
{
ngx_chain_t *cl;
// 若有空閑連結清單中有節點,直接傳回
if (*free) {
cl = *free;
*free = cl->next; // 不太明白為什麼再移動指針
cl->next = NULL;
return cl;
}
// 程式移動到這裡表示free是個空指針,需要申請空間
cl = ngx_alloc_chain_link(p);
if (cl == NULL) {
return NULL;
}
cl->buf = ngx_calloc_buf(p); // 申請新的buf空間
if (cl->buf == NULL) {
return NULL;
}
cl->next = NULL;
return cl;
}
// ngx_chain_update_chains函數會将busy連結清單中的空閑節點回收到free連結清單中
// 将out連結清單插入到busy連結清單尾部,同時将合并後的連結清單從頭開始的所有沒有使用的節點,插入到空閑連結清單
// 合并連結清單後,out為NULL
void
ngx_chain_update_chains(ngx_pool_t *p, ngx_chain_t **free, ngx_chain_t **busy,
ngx_chain_t **out, ngx_buf_tag_t tag)
{
ngx_chain_t *cl;
if (*busy == NULL) {
*busy = *out;
} else {
for (cl = *busy; cl->next; cl = cl->next) { /* void */ }
cl->next = *out;
}
*out = NULL;
while (*busy) {
cl = *busy;
// 這個節點記憶體占有,不滿足條件,直接退出
if (ngx_buf_size(cl->buf) != 0) {
break;
}
// 緩沖區類型不為void *,直接釋放,在循環開始
if (cl->buf->tag != tag) {
*busy = cl->next;
ngx_free_chain(p, cl);
continue;
}
// 重置緩沖區所有空間都可用
cl->buf->pos = cl->buf->start;
cl->buf->last = cl->buf->start;
// 将該空閑區加入到free連結清單表頭
*busy = cl->next;
cl->next = *free;
*free = cl;
}
}
三、graphviz畫圖工具
graphviz采用dot語言,就感覺像描述一樣,随便畫出來的圖就是這個樣子了
看源碼梳理函數完全可以用這個,我貼出來一些連結
1.windows下Graphviz安裝及入門教程 http://m.blog.csdn.net/article/details?id=49472949
2.利用Graphviz 畫結構圖
http://www.cnblogs.com/sld666666/archive/2010/06/25/1765510.html
3.使用Graphviz繪制流程圖和關系圖
http://www.tuicool.com/articles/qeqeuyb
如果有什麼問題,歡迎交流