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