Xen的调度分析 (四) ——credit调度算法中关于链表的部分细节

上一节讲了credit调度算法中的一些细节,这里提一些细节中的细节。

首先是lish.h文件中的几个问题。linux通过结构体某个成员的地址,以及他的偏移量来获得该结构体的地址。

这里是如何进行的呢?

假设我们有以下代码:

struct csched_vcpu {
    struct list_head runq_elem;
    int credit;
};


void main() {
    struct csched_vcpu svc;
    INIT_LIST_HEAD(&svc.runq_elem);
    svc.credit = 10;
    struct list_head *iter = &svc.runq_elem;
    while (1);
}

vcpu结构体简化如上,main已经声明了一个vcpu svc,并且对该VCPU的链表进行了初始化。现在的问题是,假设我们只有iter这个指针,这个指针指向队列中一个队列元素,但是这个队列元素只是某个vcpu的结构体成员,我们如何通过这个队列元素获取该队列元素所属于的结构体呢?

struct csched_vcpu * iter_svc = list_entry(iter, struct csched_vcpu, runq_elem);
printf("%d", iter_svc->credit);

如上,使用list_entry来获取该结构体的地址。该宏定义等同于container_of()。

#define container_of(ptr, type, member) ({                      \
        typeof( ((type *)0)->member ) *__mptr = (ptr);          \
        (type *)( (char *)__mptr - offsetof(type,member) );})

该宏定义乍看起来比较复杂,分析起来倒是不难。主要还是实现了通过结构体某个成员的地址,以及他的偏移量来获得该结构体的地址。

第一句的作用是将该成员的ptr地址缓存一下。要缓存该地址,就要声明一个该指针同样类型的指针变量。typeof( ((type *)0)->member )就是获得了member的指针类型。为什么不直接使用typeof( member )是因为type结构体不一定用member成员,主要还是为了合法性检查。为什么不直接使用ptr是避免ptr的多次调用。比如ptr++之类的,就像使用min(x,y)可能导致的一些问题。

第二句的作用是,把该成员的地址减去该成员的地址在结构体里的偏移量,得到该结构体的地址。

最后,还有个问题,就是container_of是如何返回__mptr - offsetof()的值的呢?

GCC把包含在圆括号和大括号双层括号内的复合语句看作是一个表达式,它可以出现在任何允许表达式的地方,而复合语句中可以声明局部变量,以及循环条件判断等复杂处理。而表达式的最后一条语句必须是一个表达式,它的计算结果作为返回值。例如{(A;B;C)}返回C的值。

最后是分析offsetof()

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER);

这是假设有个地址为0的type类型的结构体,取出了该结构体的member成员,再获得该成员的地址。显然该地址就是member在结构体中的偏移。

posted @ 2016-04-10 17:38  linanwx  阅读(483)  评论(0编辑  收藏  举报