QEMU代码中的QLIST

来源:互联网 发布:linux cst时间 编辑:程序博客网 时间:2024/05/29 12:23

下面会陆续写一些KVM、QEMU相关的学习笔记,个人觉得KVM相比Xen而言真的简单很多,当然前提是你对Linux内核得非常熟悉,我也建议虚拟化的初学者不要去碰Xen,直接学习KVM就好。而QEMU则是另一个大头,无论Xen还是KVM都绕不开这个苦主,而无论是Xen还是KVM都离不开Qemu,而且在Qemu领域融合的也越来越好,最新upstream的Qemu已经可以同时支持Xen/KVM了,真心赞一下redhat

本文先分析下Qemu里用到的工具类QLIST,代码在include/qemu/queue.h中,主要是从netbsd系统里port过来的

QLIST是类似linux hlist类型的double linked list结构体,其结构体定义如下

#define QLIST_HEAD(name, type)                                          \struct name {                                                           \        struct type *lh_first;  /* first element */                     \}#define QLIST_HEAD_INITIALIZER(head)                                    \        { NULL }#define QLIST_ENTRY(type)                                               \struct {                                                                \        struct type *le_next;   /* next element */                      \        struct type **le_prev;  /* address of previous next element */  \}
几个操作宏如下
#define QLIST_INSERT_AFTER(listelm, elm, field) do {                    \        if (((elm)->field.le_next = (listelm)->field.le_next) != NULL)  \                (listelm)->field.le_next->field.le_prev =               \                    &(elm)->field.le_next;                              \        (listelm)->field.le_next = (elm);                               \        (elm)->field.le_prev = &(listelm)->field.le_next;               \} while (/*CONSTCOND*/0)#define QLIST_INSERT_BEFORE(listelm, elm, field) do {                   \        (elm)->field.le_prev = (listelm)->field.le_prev;                \        (elm)->field.le_next = (listelm);                               \        *(listelm)->field.le_prev = (elm);                              \        (listelm)->field.le_prev = &(elm)->field.le_next;               \} while (/*CONSTCOND*/0)#define QLIST_INSERT_HEAD(head, elm, field) do {                        \        if (((elm)->field.le_next = (head)->lh_first) != NULL)          \                (head)->lh_first->field.le_prev = &(elm)->field.le_next;\        (head)->lh_first = (elm);                                       \        (elm)->field.le_prev = &(head)->lh_first;                       \} while (/*CONSTCOND*/0)#define QLIST_INSERT_HEAD_RCU(head, elm, field) do {                    \        (elm)->field.le_prev = &(head)->lh_first;                       \        (elm)->field.le_next = (head)->lh_first;                        \        smp_wmb(); /* fill elm before linking it */                     \        if ((head)->lh_first != NULL)  {                                \            (head)->lh_first->field.le_prev = &(elm)->field.le_next;    \        }                                                               \        (head)->lh_first = (elm);                                       \        smp_wmb();                                                      \} while (/* CONSTCOND*/0)#define QLIST_REMOVE(elm, field) do {                                   \        if ((elm)->field.le_next != NULL)                               \                (elm)->field.le_next->field.le_prev =                   \                    (elm)->field.le_prev;                               \        *(elm)->field.le_prev = (elm)->field.le_next;                   \} while (/*CONSTCOND*/0)#define QLIST_FOREACH(var, head, field)                                 \        for ((var) = ((head)->lh_first);                                \                (var);                                                  \                (var) = ((var)->field.le_next))#define QLIST_FOREACH_SAFE(var, head, field, next_var)                  \        for ((var) = ((head)->lh_first);                                \                (var) && ((next_var) = ((var)->field.le_next), 1);      \                (var) = (next_var))
这些操作宏,可以对比内核里的hlist_xxxx的相同操作,两者完全一致

QSLIST 指的是one-way的list结构体,比较简单本文就不分析了

QTAILQ的数据结构其实和QLIST差不多,只是head里面多了一个指向最后一个元素的指针的地址,其定义如下

#define Q_TAILQ_HEAD(name, type, qual)                                  \struct name {                                                           \        qual type *tqh_first;           /* first element */             \        qual type *qual *tqh_last;      /* addr of last next element */ \}#define QTAILQ_HEAD(name, type)  Q_TAILQ_HEAD(name, struct type,)#define QTAILQ_HEAD_INITIALIZER(head)                                   \        { NULL, &(head).tqh_first }#define Q_TAILQ_ENTRY(type, qual)                                       \struct {                                                                \        qual type *tqe_next;            /* next element */              \        qual type *qual *tqe_prev;      /* address of previous next element */\}#define QTAILQ_ENTRY(type)       Q_TAILQ_ENTRY(struct type,)
上述代码写得还是蛮坑人的,至少我第一次看的时候,就对这个qual百思不得其解,后来才发现其实真正用的时候,根本就没有定义qual,所以其实可以简化为这个

#define Q_TAILQ_HEAD(name, type)                                  \struct name {                                                           \        type *tqh_first;           /* first element */             \        type **tqh_last;      /* addr of last next element */ \}#define QTAILQ_HEAD(name, type)  Q_TAILQ_HEAD(name, struct type,)#define QTAILQ_HEAD_INITIALIZER(head)                                   \        { NULL, &(head).tqh_first }#define Q_TAILQ_ENTRY(type)                                       \struct {                                                                \        type *tqe_next;            /* next element */              \        type **tqe_prev;      /* address of previous next element */\}#define QTAILQ_ENTRY(type)       Q_TAILQ_ENTRY(struct type)
注意,这里的tqh_last指针,指的是tailq最后一个元素的tqe_next指针本身的&地址,具体可以看下面的QTAILQ相关操作函数

#define QTAILQ_INIT(head) do {                                          \        (head)->tqh_first = NULL;                                       \        (head)->tqh_last = &(head)->tqh_first;                          \} while (/*CONSTCOND*/0)#define QTAILQ_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 (/*CONSTCOND*/0)#define QTAILQ_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 (/*CONSTCOND*/0)#define QTAILQ_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 (/*CONSTCOND*/0)#define QTAILQ_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 (/*CONSTCOND*/0)#define QTAILQ_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 (/*CONSTCOND*/0)#define QTAILQ_FOREACH(var, head, field)                                \        for ((var) = ((head)->tqh_first);                               \                (var);                                                  \                (var) = ((var)->field.tqe_next))#define QTAILQ_FOREACH_SAFE(var, head, field, next_var)                 \        for ((var) = ((head)->tqh_first);                               \                (var) && ((next_var) = ((var)->field.tqe_next), 1);     \                (var) = (next_var))
上述代码还是比较直观的,和linux内核中的hlist_xxxx宏也大同小异,除了我之前提到过的head->tqh_last指针,里面的地址实际上是队列最后一个元素的&elem->tqh_next的指针本身地址。下面来看QTAILQ中比较难理解的部分:

#define QTAILQ_LAST(head, headname) \        (*(((struct headname *)((head)->tqh_last))->tqh_last))#define QTAILQ_PREV(elm, headname, field) \        (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))#define QTAILQ_FOREACH_REVERSE(var, head, headname, field)              \        for ((var) = (*(((struct headname *)((head)->tqh_last))->tqh_last));    \                (var);                                                  \                (var) = (*(((struct headname *)((var)->field.tqe_prev))->tqh_last)))

QTAILQ_LAST作用是拿到队列最后一个元素的指针,但由于没有container_of宏,加之无法确定QTAILQ_ENTRY在元素结构体中的位置,所以代码变得非常晦涩

head首先是QTAILQ_ENTRY结构体的指针,这个QTAILQ_ENTRY结构体做为field成员存在于element结构体中,所以首先,head->tgh_last指向了最后一个元素&elm->field.tgh_next指针所在的地址。这里我们并不关心tgh_next指针的内容,只关心tgh_next本身的地址,因为要通过tgh_next拿到下一个指针地址,所以首先做一次转化,即

(struct headname *)((head)->tgh_last),这个转换之后返回的地址,可以认为是最后一个元素QTAILQ_ENTRY部分(field成员)的开头地址,但注意这里是通过struct headname *来进行指针转化的,因此只能通过tgh_last来拿到tge_prev(这里要绕一下,因为tgh_last和tge_prev其实是同一个位置,所以这样做是没问题的)

最后,由于tgh_prev指向的是元素地址的指针,所以最后通过*拿到真正最后一个元素的地址,是一个struct type* 的指针,指向了最后一个元素

理解了这个,QTAILQ_PREV也不难理解了,这个宏用来拿到队列之前一个元素的指针,但为了拿到这个指针,又去找之前的之前的元素,e.g. 假设队列是 A <-- B <-- C 的样子,通过C拿到B的话,最终还得通过A才行,这里就不详细说了,代码里啥都有

这两篇文章也分析了QTAILQ_PREV, QTAILQ_LAST,可以对比参考下

http://www.blogjava.net/bacoo/archive/2010/08/08/328235.html

http://www.cnblogs.com/UnGeek/archive/2013/03/29/2989325.html



0 0
原创粉丝点击