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 *));
/* 使用了簡單的插入排序算法,是一個穩定的排序 */