QEMU代码中的QLIST

下面会陆续写一些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



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值