首先我们来看看遍历子进程的代码:
list_for_each(pos,&task->tasks)
{
p=list_entry(pos,struct task_struct,tasks);
count++;
printk("%d---->%s\n",p->pid,p->comm);
}
内核维护着一个节点为list_head类型的链表,这个节点的定义如下:
struct list_head {
struct list_head *next, *prev;
};
这个节点没有数据域,因为这个节点是在task_struct中的,如图:
list_for_each宏:
#define list_for_each(pos,head) \
for(pos=(head)->next;prefetch(pos->next),pos!=(head);
pos=pos->next)
其中pos和head都是list_head 类型根据上边这个宏,可以遍历整个链表,但是这个链表的数据域是空的,我们的目的是得到这个链表的节点所在的
task_struct,那么我们就要根据tasks节点在整个task_struct中的偏移,用tasks的物理地址减去这个偏移值就得到了task_struct的地址。此时,另外一个重要的宏登场了:
#define list_entry(ptr,type,member)/ container_of(ptr,type,member)
//continer_of:
#define container_of(ptr,type,member) (P{/
const typeof(((type *)0->member) *_mptr=(ptr);/
(type*)((char*)__mptr-offset(type,member));})
是不是看到这头都大了?放心,会一步一步讲明白的
首先看第一部分:
const typeof(((type *)0->member) *_mptr=(ptr);
这个宏里,你可以把type想象成task_struct,member可以认为是task_struct中的tasks(类型是list_head),ptr也是一个list_head类型,在遍历的时候是一个移动的 struct list_head *pos;
(type *)0->member:
将0强制转换成type(task_struct)类型的变量指针,这个指针的地址是0x0;
然后去拿这个type(task_struct)的member(tasks)成员;
typeof(((type *)0->member)
typeof:获取上一部中得到的member的类型,在这里是list_head;
const typeof(((type *)0->member) *_mptr=(ptr);
根据以上的分析,在遍历进程时,这个等价于:
list_head * _mptr=(ptr);
因为ptr和member本身就是list_head类型的,所以这里好像没有什么存在的意义?这里是对指针类型做了一次检查而已
我们再来看:(type*)((char*)__mptr-offset(type,member));})
里边又有一个offset宏:
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER);
字面意思offset是偏移的意思
首先将0强制转换成(type *)类型的指针,然后用这个指针去引用其中的member,相当于得到了member相对于0x0的偏移值,然后将其强转为size_t类型,即得到了member在type中的偏移值,相当于得到tasks在task_struct中的偏移值
((char*)__mptr
将__mptr强制转换成指向char的指针,为什么要强制转呢?
我们假设__mptr是int类型的指针,对一个int类型的指针做加法会有什么结果呢?
int a=1;
int *p=&a;
p+=1;
这个情况,p指向的地址是加1还是sizeof(int)?
所以明白为什么要强转成int了吧!
到现在,我们得到了tasks的物理地址,还有tasks在整个结构体中偏移值,
用物理地址减去偏移地址,就得到了task_struct的首地址的,那么我们就根据task_struct中镶嵌的list_head节点得到了task_struct;
然后回过头我们再来看看遍历的代码:
list_for_each(pos,&task->tasks)
{
p=list_entry(pos,struct task_struct,tasks);
count++;
printk("%d---->%s\n",p->pid,p->comm);
}
首先外边的循环遍历list_head链表,里边的list_entry根据当前节点的list_head类型的tasks返回当前list_head所在的task_struct的首地址,从而遍历一遍list_head就得到了整个链表中的task_struct了。