Libevent源碼分析——隊列

在compat/sys/queue.h文件裏定義了5種數據結構,分別是單鏈表、雙鏈表、簡單隊列、尾隊列、環形隊列,其中 TAILQ_QUEUE尾隊列是libevent裏使用最多的,libevent中用到的隊列其實也是尾隊列,所以這裏只分析尾隊列TAILQ_QUEUE他結構都大同小異,TAILQ_QUEUE隊列的結構體如下:
/*
* Tail queue definitions.
*/
#define TAILQ_HEAD(name, type)                                          \
struct name {                                                           \
        struct type *tqh_first; /* first element */                     \
        struct type **tqh_last; /* addr of last next element */         \
}



#define TAILQ_HEAD_INITIALIZER(head)                                    \
        { NULL, &(head).tqh_first }



#define TAILQ_ENTRY(type)                                               \
struct {                                                                \
        struct type *tqe_next;  /* next element */                      \
        struct type **tqe_prev; /* address of previous next element */  \
}

TAILQ_ENTRY沒有結構體名,所以它一般都是另外一個結構體的成員,tqe_next和tqh_frist是一級指向結構體地址,tqh_last和tqh_prev是二級指針,也就是指向指針的指針,所以指向隊列的next指針,構造出來的隊列一般如下圖所示:

隊列操作宏函數如下:

 
//隊列中的第一個元素
#define TAILQ_FIRST(head)               ((head)->tqh_first)
//隊列結束
#define TAILQ_END(head)                 NULL
//隊列中當前元素的下一個節點
#define TAILQ_NEXT(elm, field)          ((elm)->field.tqe_next)
//隊列最後一個節點
#define TAILQ_LAST(head, headname)                                      \
        (*(((struct headname *)((head)->tqh_last))->tqh_last))


/* XXX */
//隊列前一個節點
#define TAILQ_PREV(elm, headname, field)                                \
        (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))

//隊列是否爲空
#define TAILQ_EMPTY(head)                                               \
        (TAILQ_FIRST(head) == TAILQ_END(head))

//隊列遍歷
#define TAILQ_FOREACH(var, head, field)                                 \
        for((var) = TAILQ_FIRST(head);                                  \
            (var) != TAILQ_END(head);                                   \
            (var) = TAILQ_NEXT(var, field))

//隊列從尾遍歷
#define TAILQ_FOREACH_REVERSE(var, head, headname, field)               \
        for((var) = TAILQ_LAST(head, headname);                         \
            (var) != TAILQ_END(head);                                   \
            (var) = TAILQ_PREV(var, headname, field))



/*
* Tail queue functions.
*/

//隊列頭初始化
#define TAILQ_HEAD_INITIALIZER(head) \
{ NULL, &(head).tqh_first }

//隊列初始化
#define TAILQ_INIT(head) do {                                           \
        (head)->tqh_first = NULL;                                       \
        (head)->tqh_last = &(head)->tqh_first;                          \
} while (0)

//隊列頭插入節點
#define TAILQ_INSERT_HEAD(head, elm, field) do {                        \
        if (((elm)->field.tqe_next = (head)->tqh_first) != NULL)        \
                (head)->tqh_first->field.tqe_prev =                     \
                    &(elm)->field.tqe_next;                             \
        else                                                            \
                (head)->tqh_last = &(elm)->field.tqe_next;              \
        (head)->tqh_first = (elm);                                      \
        (elm)->field.tqe_prev = &(head)->tqh_first;                     \
} while (0)

//隊列尾插入節點
#define TAILQ_INSERT_TAIL(head, elm, field) do {                        \
        (elm)->field.tqe_next = NULL;                                   \
        (elm)->field.tqe_prev = (head)->tqh_last;                       \
        *(head)->tqh_last = (elm);                                      \
        (head)->tqh_last = &(elm)->field.tqe_next;                      \
} while (0)

//隊列節點後插入
#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do {              \
        if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
                (elm)->field.tqe_next->field.tqe_prev =                 \
                    &(elm)->field.tqe_next;                             \
        else                                                            \
                (head)->tqh_last = &(elm)->field.tqe_next;              \
        (listelm)->field.tqe_next = (elm);                              \
        (elm)->field.tqe_prev = &(listelm)->field.tqe_next;             \
} while (0)

//隊列節點前插入
#define TAILQ_INSERT_BEFORE(listelm, elm, field) do {                   \
        (elm)->field.tqe_prev = (listelm)->field.tqe_prev;              \
        (elm)->field.tqe_next = (listelm);                              \
        *(listelm)->field.tqe_prev = (elm);                             \
        (listelm)->field.tqe_prev = &(elm)->field.tqe_next;             \
} while (0)

//隊列節點刪除
#define TAILQ_REMOVE(head, elm, field) do {                             \
        if (((elm)->field.tqe_next) != NULL)                            \
                (elm)->field.tqe_next->field.tqe_prev =                 \
                    (elm)->field.tqe_prev;                              \
        else                                                            \
                (head)->tqh_last = (elm)->field.tqe_prev;               \
        *(elm)->field.tqe_prev = (elm)->field.tqe_next;                 \
} while (0)

//替換隊列節點
#define TAILQ_REPLACE(head, elm, elm2, field) do {                      \
        if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL)   \
                (elm2)->field.tqe_next->field.tqe_prev =                \
                    &(elm2)->field.tqe_next;                            \
        else                                                            \
                (head)->tqh_last = &(elm2)->field.tqe_next;             \
        (elm2)->field.tqe_prev = (elm)->field.tqe_prev;                 \
        *(elm2)->field.tqe_prev = (elm2);                               \
} while (0)

其他的宏都比較簡單,比較好理解,TAILQ_LAST,TAILQ_PREV這兩個宏可能比較難理解。

  TAILQ_LAST、TAILQ_PREV兩個宏操作類似,就只說TAILQ_LAST:
//隊列最後一個元素
#define TAILQ_LAST(head, headname)                                      \
        (*(((struct headname *)((head)->tqh_last))->tqh_last))
(head)->tqh_last這裏比較好理解,像上面說的隊列頭TAILQ_HEAD的尾指針是二級指針,它指向隊列最後一個的tqe_next指針,最後一個的tqe_next指針指向的是NULL,通過tqe_next指針怎麼訪問到隊列的最後一個節點的值呢,((struct headname *)((head)->tqh_last))這裏就做了一個強制類型轉換,強制類型轉換成隊列頭的類型,再回頭看隊列頭的結構:
 
#define TAILQ_HEAD(name, type)                                          \
struct name {                                                           \
        struct type *tqh_first; /* first element */                     \
        struct type **tqh_last; /* addr of last next element */         \
}

一級指針tqh_first指向隊列第一個節點的地址,二級指針tqh_last指向隊列最後一個節點的next指針,隊列節點的TAILQ_ENTRY同樣也是一級指針tqe_next指向下一個節點的地址,tqe_prev指向前一個節點的next指針。

#define TAILQ_ENTRY(type)                                               \
struct {                                                                \
        struct type *tqe_next;  /* next element */                      \
        struct type **tqe_prev; /* address of previous next element */  \
}
(head)->tqh_last是最後一個節點的next指針,(((struct headname *)((head)->tqh_last))前面這一坨強制類型轉換的就先不看,同樣是二級指針TAILQ_HEAD隊列頭tqh_last對應隊列節點 TAILQ_ENTRY的tqe_prev,假設前面強制類型轉換之後就是最後一個節點queue_last的地址,queue_last->tqh_prev就是倒數第二個節點next指針,倒數第二個節點的next指針指向最後一個節點的地址,取值*(queue_last->tqh_prev)就是最後一個節點的值了,全部換回來就是(*(((struct headname *)((head)->tqh_last))->tqh_last))
 
    這裏就解釋一下爲什麼強制類型轉換就相當於最後一個節點的地址,其實準確來說應該是最後一個節點的TAILQ_ENTRY的地址,一般隊列結構體定義如下:
 
#define TAILQ_HEAD(name, type)                                          \
struct name {                                                           \
        struct type *tqh_first; /* first element */                     \
        struct type **tqh_last; /* addr of last next element */         \
}

下面這張圖更能反映文中結構體的內存佈局,TAILQ_HEAD和TAILQ_ENTRY都是一級指針在前二級指針在後,TAILQ_HEAD和TAILQ__ENTRY內存分佈相同,強制類型轉換獲取TAILQ_ENTRY的地址,因爲**tqh_last指向*tqh_next, 無論一級還是二級指針,其都是指向另外一個地址,二級指針指向一級指針的地址,TAILQ_ENTRY結構中tqh_next在前它的地址強制類型轉換TAILQ_HEAD,前面說的TAILQ_HEAD和TAIL_ENTRY內存結構相同,就可以表示TAILQ_ENTRY的地址,全部通過指針獲取元素的值。

 

TAILQ_QUEUE隊列是由兩部分組成:隊列頭和隊列節點。在Libevent中,隊列頭一般是event_base結構體的一個成員變量,而隊列節點則是event結構體。
其中,結構體struct event_list如下定義:
TAILQ_HEAD (event_list, event);
//所以event_list的定義展開後如下:
struct event_list
{
    struct event *tqh_first;
    struct event **tqh_last;
};
在event結構體中,則有TAILQ_ENTRY(event)類型的成員變量。
struct event_base;
struct event {
        struct event_callback ev_evcallback;


        /* for managing timeouts */
        union {
                TAILQ_ENTRY(event) ev_next_with_common_timeout;
                size_t min_heap_idx;
        } ev_timeout_pos;
        evutil_socket_t ev_fd;


        struct event_base *ev_base;


        union {
                /* used for io events */
                struct {
                        LIST_ENTRY (event) ev_io_next;
                        struct timeval ev_timeout;
                } ev_io;


                /* used by signal events */
                struct {
                        LIST_ENTRY (event) ev_signal_next;
                        short ev_ncalls;
                        /* Allows deletes in callback */
                        short *ev_pncalls;
                } ev_signal;
        } ev_;


        short ev_events;
        short ev_res;           /* result passed to event callback */
        struct timeval ev_timeout;
};


TAILQ_HEAD (event_list, event);
最後是對隊列宏函數的應用加深理解:
#include <stdio.h>
#include <stdlib.h>
#include "queue.h"

#define ENTRY_NAME entry


struct queue_entry_t
{
    int value;


    //從TAILQ_ENTRY的定義可知,它只能是結構體或者共用體的成員變量
    TAILQ_ENTRY(queue_entry_t)ENTRY_NAME;
};

TAILQ_HEAD(queue_head_t, queue_entry_t);

int main(int argc, char **argv)
{
    struct queue_head_t queue_head;
    struct queue_entry_t *queue, *new_item, *tmp,*next;
    int i;
    int var ;


    TAILQ_INIT(&queue_head);


    for(i = 0; i < 6; ++i)
    {
        new_item = NULL ;
        new_item = (struct queue_entry_t*)malloc(sizeof(struct queue_entry_t));
        new_item->value = i;


        TAILQ_INSERT_TAIL(&queue_head, new_item, ENTRY_NAME);//在隊尾插入數據
    }

    queue = (struct queue_entry_t*)malloc(sizeof(struct queue_entry_t));
    queue->value = 10;
    TAILQ_INSERT_HEAD(&queue_head, queue, ENTRY_NAME);//在隊頭插入數據


    tmp = (struct queue_entry_t*)malloc(sizeof(struct queue_entry_t));
    tmp->value = 20;
    //在隊頭元素q的後面插入元素
    TAILQ_INSERT_AFTER(&queue_head, queue, tmp, ENTRY_NAME);

    i = 0 ;
    TAILQ_FOREACH(tmp, &queue_head, ENTRY_NAME)
    {
        i++ ;
        printf("the %dth entry is %d\n", i,tmp->value) ;
    }

    new_item = (struct queue_entry_t*)malloc(sizeof(struct queue_entry_t));
    new_item->value = 100;


    tmp = TAILQ_FIRST(&queue_head);
    //用new_iten替換第一個元素
    TAILQ_REPLACE(&queue_head, tmp, new_item, ENTRY_NAME);
    free(tmp) ;

    i = 0 ;
    TAILQ_FOREACH(tmp, &queue_head, ENTRY_NAME)
    {
        i++ ;
        printf("the %dth entry is %d\n", i,tmp->value) ;
    }

    while ( (tmp) = TAILQ_FIRST(&queue_head) ) {
        TAILQ_REMOVE(&queue_head, tmp, ENTRY_NAME);
        free(tmp);
    }
    if (!TAILQ_EMPTY(&queue_head)) {
        printf("tail queue is  NOT empty!\n");
    }

    return 0 ;
}
 
 
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章