resource在内核里面,是以树的形式进行管理。
它的形式可以为下图所示:
现在先来看__request_resource函数:
static struct resource * __request_resource(struct resource *root, struct resource *new)
{
resource_size_t start = new->start;
resource_size_t end = new->end;
struct resource *tmp, **p;
if (end < start)
return root;
if (start < root->start)
return root;
if (end > root->end)
return root;
p = &root->child;
for (;;) {
tmp = *p;
if (!tmp || tmp->start > end) {
new->sibling = tmp;
*p = new;
new->parent = root;
return NULL;
}
p = &tmp->sibling;
if (tmp->end < start)
continue;
return tmp;
}
}
两个参数:root和new。 这个函数的大致意思就是在root所指向的区域(root->start 和 root->end)进行申请资源,所要申请的资源有new->start 和new->end指定。
那么很显然就是,new所指示的区域要在root内,所以在函数的一开始进行条件检测:
如果检测条件不通过则返回root 结点
if(end < start)
{
...
}
if(start < root->start)
{
....
}
if(end > root->end)
{
...
}
只有在条件检测通过之后,才进行后续的操作,如果申请成功的话则会返回NULL,否则返回tmp。那么tmp是指向某一个resource结构体的,当然它是作为root所指示的某个孩子。下图所示的是申请成功的话,对链表的操作就如下。
第一种情况就是:
A->start > new -> end
第二种情况就是:
A->end < new ->start
B->start > new -> end
第三种情况就是:
E->end < new -> start
显然,new所要申请的资源是没有被其他resource所占有的,那么可以直接申请成功。
for (;;) {
tmp = *p;
if (!tmp || tmp->start > end) {
new->sibling = tmp;
*p = new;
new->parent = root;
return NULL;
}
p = &tmp->sibling;
if (tmp->end < start)
continue;
return tmp;
}
这个函数体如果要执行到return tmp;这个语句那么应该满足这样的条件:
tmp->start < end ;
tmp->end > start ;
这3种情况都是因为[new->start, new->end]这个区域在root下的resource的某个孩子的区域有重叠部分。
注意在第三种情况下tmp后面可能还会有n个resource结构体、也可能就只有tmp这个,取决于new->end这个大小。
然后函数返回到__insert_resource这个函数。
static struct resource * __insert_resource(struct resource *parent, struct resource *new)
{
struct resource *first, *next;
for (;; parent = first) {
first = __request_resource(parent, new);
if (!first) // 1. __request_resource 返回NULL,申请成功
return first;
if (first == parent)// 2. __request_resource 返回parent,检测条件不通过,申请区域parent不包含。
return first;
if (WARN_ON(first == new)) /* duplicated insertion */
return first;
//3. __request_resource
if ((first->start > new->start) || (first->end < new->end))
break;
if ((first->start == new->start) && (first->end == new->end))
break;
}
for (next = first; ; next = next->sibling) {
/* Partial overlap? Bad, and unfixable */
if (next->start < new->start || next->end > new->end)
return next;
if (!next->sibling)
break;
if (next->sibling->start > new->end)
break;
}
new->parent = parent;
new->sibling = next->sibling;
new->child = first;
next->sibling = NULL;
for (next = first; next; next = next->sibling)
next->parent = new;
if (parent->child == first) {
parent->child = new;
} else {
next = parent->child;
while (next->sibling != first)
next = next->sibling;
next->sibling = new;
}
return NULL;
}
现在看到这几条语句,first就是__request_resource返回的tmp
if ((first->start > new->start) || (first->end < new->end))
break;
if ((first->start == new->start) && (first->end == new->end))
break;
符合这么几个条件的时候就break。之前的四种情况下,一、二、三都需要break。留下第四种情况在进行for循环内。
然后第四种情况是,所要申请的内存区域直接在parent下的某个孩子所完全包含。那么相当于在把该孩子作为一个parent
然后再进行一次__request_resource,那么操作等同于在一次进行该函数。
for (;; parent = first) {
first = __request_resource(parent, new);
循环体,parent = first 表明了,拿该孩子(frist)作为parent,
然后再进行first = __request_resource(parent, new);进行在一次的请求资源。
好,现在我们继续break之后的程序执行。现在是有3种情况,第一种和第二种是区域有部分重叠。第三种直接是,要请求的资源直接把整个resource包含,至于区间[new->start , new->end]包含了几个resource的区域,这个不清楚,需要后面在继续讨论。刚才第四种情况是,要请求的区域被别的资源所包含(相反)。
现在来这个这部分函数体:
for (next = first; ; next = next->sibling) {
/* Partial overlap? Bad, and unfixable */
if (next->start < new->start || next->end > new->end)
return next;
if (!next->sibling)
break;
if (next->sibling->start > new->end)
break;
}
for(next = first; ; next = next->sibling)很明显就是遍历以first为起点的兄弟链表..
然后该函数体的这条语句直接把第一种和第二种情况(部分包含区域)给return了。
if (next->start < new->start || next->end > new->end)
就第一种情况: next->end > new->end;
就第二种情况: new->start > next->start
最后则需要对第三种情况在进行细分:
我们要注意的就是new->end的指示情况,
1. 它也许会大于最后一个resource的end。那么就是第一种情况。
2. 它也许会刚才在某一个reource的中间。那么就是第二种情况。
3. 它也许是在中间某两个resource中间。那么就是第三种情况。
对于第一种情况在for( xxx; xxx; xxx)遍历孩子结点的最后会因为next->sibling = null
然后
if (!next->slibling)
break;
而break出该for循环。
对应第三种情况,
if (next->sibling->start > new->end)
break;
这时候next指示在new->end所包含的区域外那个结点,next->sibling->start > new->end是成立的,那么它也需要break;
而第二种情况,它的new->end刚好在某个兄弟结点的[start,end]中间。
则因为
if (next->start < new->start || next->end > new->end)
的 next->end > new->end而return .
注意:退出for循环时候,next指向的结点为new->end包含的最后一个结点。
好。现在只有第一种情况和第三种情况的可能继续执行剩下的语句。
<span style="white-space:pre"> </span>new->parent = parent;
new->sibling = next->sibling;
new->child = first;
next->sibling = NULL;
for (next = first; next; next = next->sibling)
next->parent = new;
if (parent->child == first) {
parent->child = new;
} else {
next = parent->child;
while (next->sibling != first)
next = next->sibling;
next->sibling = new;
}
return NULL;
这么一大段语句就是,添加一个子树,然后new作为parent的一个孩子。可以看做是下图:
<span style="white-space:pre"> </span>new->parent = parent;
new->sibling = next->sibling;
new->child = first;
<span style="white-space:pre"> </span>next->sibling = NULL;
红色结点为new.设置为new的parent,那么就是作为parent的孩子。
其实是指向D结点的next,new->sibling = new->sibling就是,添加到parent下面的兄弟链表。
new->child = first。 first是指向A节点的。
next->sibling = NULL.。 设置最后一个结点指向NULL,就是D指向NULL
那么此时,就重新构成一个树了。
for (next = first; next; next = next->sibling)
next->parent = new; //设置图中所示的ABCD的parent为new
if (parent->child == first) { //first是new->start,new->end区间包含的resource的第一个结点
parent->child = new; //判断是否为parent的第一个孩子。如果为第一个孩子,还需要设置parent->child = new。
} else { //如果不是第一个孩子。
next = parent->child;
while (next->sibling != first) //遍历找到first的前一个结点。
next = next->sibling;
next->sibling = new; // 前一个结点指向new ,而不是指向first(A结点)
}
这样之后,就算请求资源成功,可以返回NULL了。。
另外这些代码是我自个分析出来的。难免会有错误,也才没几天来看resource这部分。。
如果有错误,请多多指正。