nginx源碼初讀(7)--讓煩惱從數據結構開始(ngx_queue)

ngx_queue_t是nginx中的雙向鏈表,我們看看它的數據結構,太簡單:

typedef struct ngx_queue_s ngx_queue_t;
struct ngx_queue_s {
    ngx_queue_t  *prev;
    ngx_queue_t  *next;
};

是不是很簡單?學過數據結構的我們一看就懂了。唉?等一下!等一下!等一下~~我老公呢~等一下。。(亂入,什麼東西- -)

等一下,我們發現它少了我們必須用到的數據存儲啊,我們鏈表裏的數據放哪兒去了。剛開始我也很疑惑,後來看到它用得地方瞬間懂了,不只是懂了,瞬間驚呆了!!
它是這麼用的:

ngx_queue_t free;            // 這是一個小哨兵,不存數據
ngx_queue_init(&free);       // 初始化一下下
struct node{
    int data;
    ngx_queue_t queue;       // 把queue這個結構體乖乖的放在我們要創建鏈表的結構裏
}  node1;   
gx_queue_insert_head(&free, &node1.queue);
// 對!就這麼用,先把queue放任意結構體裏,再連到free這個哨兵上,都可以做鏈表用啦,寫這個的人真是絕了

好了,知道它是怎麼用的了,我們能放下好奇心好好看看代碼了。

#define ngx_queue_init(q)                                                     \
    (q)->prev = q;                                                            \
    (q)->next = q
/* 初始化函數,哨兵的前後都是自己,也就是鏈表是空的,沒有數據節點 */

#define ngx_queue_empty(h)    \
    (h == (h)->prev)
/* 判斷鏈表是不是空的 */

#define ngx_queue_insert_head(h, x)                                           \
    (x)->next = (h)->next;                                                    \
    (x)->next->prev = x;                                                      \
    (x)->prev = h;                                                            \
    (h)->next = x
/* 插入是直接插入頭部的 */ 

#define ngx_queue_insert_after   ngx_queue_insert_head
/* 插入到某節點的後面,跟insert_head是一樣的 */

#define ngx_queue_insert_tail(h, x)                                           \
    (x)->prev = (h)->prev;                                                    \
    (x)->prev->next = x;                                                      \
    (x)->next = h;                                                            \
    (h)->prev = x
/* 因爲是雙向循環鏈表,頭尾相連,所以插入到尾部也很容易,就是頭部的前一個元素 */

#define ngx_queue_head(h)                                                     \
    (h)->next
#define ngx_queue_last(h)                                                     \
    (h)->prev
/* 通過哨兵獲得頭尾元素 */

#define ngx_queue_sentinel(h)                                                 \
    (h)
#define ngx_queue_next(q)                                                     \
    (q)->next
#define ngx_queue_prev(q)                                                     \
    (q)->prev
/* 返回自身或者前後的元素 */

#if (NGX_DEBUG)
#define ngx_queue_remove(x)                                                   \
    (x)->next->prev = (x)->prev;                                              \
    (x)->prev->next = (x)->next;                                              \
    (x)->prev = NULL;                                                         \
    (x)->next = NULL
#else
#define ngx_queue_remove(x)                                                   \
    (x)->next->prev = (x)->prev;                                              \
    (x)->prev->next = (x)->next
#endif
/* 把節點從鏈表中刪除的操作,在debug模式下還會重置指針爲null */

#define ngx_queue_split(h, q, n)                                              \
    (n)->prev = (h)->prev;                                                    \
    (n)->prev->next = n;                                                      \
    (n)->next = q;                                                            \
    (h)->prev = (q)->prev;                                                    \
    (h)->prev->next = h;                                                      \
    (q)->prev = n;
/* 注意一下!
 * 有人是這麼說的:其中,h、n分別爲兩個隊列的指針,即頭節點指針,該操作將n隊列鏈接在h隊列之後。
 * 也有人表示完全看不懂~~
 * 我其實也是看不懂的,畫了好久,同學們可以自己畫一下,我感覺n是一個單獨的節點, 
 * 然後把h遠遠連在前面(中間隔n個節點),把q連在後面(中間不隔節點),然後變成了一個環(大家畫的時候要注意每個鏈表都是雙向環形的)。
 * 好吧,不好意思我也沒弄對,應該是這樣:
 * 大家動手畫一畫吧,這個因爲源碼也沒註釋~~
 */

#define ngx_queue_add(h, n)                                                   \
    (h)->prev->next = (n)->next;                                              \
    (n)->next->prev = (h)->prev;                                              \
    (h)->prev = (n)->prev;                                                    \
    (h)->prev->next = h;
/* h、n分別爲兩個隊列的指針,即頭節點指針,該操作將n隊列鏈接在h隊列之後。*/

#define ngx_queue_data(q, type, link)                                         \
    (type *) ((u_char *) q - offsetof(type, link))
/* 依靠內存位置的off_t來得到相應節點元素數據的內存地址,q爲當前地址,type爲相應結構體的類型,link爲queue */

另外兩個非宏的函數:

/* 
 * find the middle queue element if the queue has odd number of elements 
 * or the first element of the queue’s second part otherwise 
 */  
ngx_queue_t *  
ngx_queue_middle(ngx_queue_t *queue)  
{  
    ngx_queue_t  *middle, *next;  
    middle = ngx_queue_head(queue);  
    if (middle == ngx_queue_last(queue)) {  
        return middle;  
    }  
    next = ngx_queue_head(queue);  
    for ( ;; ) {  
        middle = ngx_queue_next(middle);  
        next = ngx_queue_next(next);  
        // 偶數個節點,在此返回後半個隊列的第一個節點  
        if (next == ngx_queue_last(queue)) {
            return middle;  
        }  
        //奇數個節點,在此返回中間節點
        next = ngx_queue_next(next);  
        if (next == ngx_queue_last(queue)) {  
            return middle;  
        }  
    }  
}  
/* 這個函數是用來獲取列表最中間那個值的,使用了快慢指針的方法,mid移動1位,next就移動2位,返回mid */

void  
ngx_queue_sort(ngx_queue_t *queue,  
    ngx_int_t (*cmp)(const ngx_queue_t *, const ngx_queue_t *));
/* 使用了簡單的插入排序算法,是一個穩定的排序 */
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章