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
- QEMU代码中的QLIST
- qemu QLIST数据结构
- 清除QList中的数据
- QList 中的 append 和 push_back
- QLIST
- Qlist
- QList
- Qlist
- Qt中的容器类 QList QLinkedList QVector
- QEMU代码结构分析
- qemu-kvm 代码分析
- qemu-kvm代码分析
- qemu-kvm 代码分析
- QEMU中的tracing使用
- QEMU中的qemu_thread_create函数
- QEMU中的IOCTL
- qemu中的Hbitmap数据结构
- 【小结】QEMU中的LIST
- 程序之美
- UVA - 10129 Play on Words
- Qt for ios开发:iPhone锁屏后解锁,发现软件无法与服务器通信
- FatMouse' Trade(杭电1009)
- CheckStyle报错的常见问题及解决方式
- QEMU代码中的QLIST
- KMP算法详解
- BroadcastReceiver类
- 枚举
- Topcoder SRM 152 Div2 1000(状态压缩呀)
- html5,css3,Javascript,JQuery学习分享平台和教程
- HDU-1237-简单计算器
- 从今天开始撰写动态版的Linux学习笔记
- JAVA设计模式之 工厂方法模式【Factory Method Pattern】