linux 3.5 insert_resource函数解析

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这部分。。

如果有错误,请多多指正。



发布了21 篇原创文章 · 获赞 1 · 访问量 6105

猜你喜欢

转载自blog.csdn.net/darling54454/article/details/38229089
3.5
今日推荐