概述
1. linux 内核中循环链表
内核中的循环链表实现:
struct list_head{
struct list_head *next, *prev;
};
平时我们用的循环链表实现:
struct list{
struct list *next, *prev;
int value;
};
两者区别在于,list_head中只有前指针next和后指针prev; list中还有我们关心的数据value;
2. list_for_each
list_for_each 是针对内核中特有的循环链表设计的一个宏,能够遍历这个循环链表的每一个成员;
#define list_for_each(pos, head) for(pos = (head)->next; pos != (head); pos = pos->next)
#define list_for_each(pos, head) for(pos = (head)->next; pos != (head); pos = pos->next)
请注意:list_for_each(pos, head) 遍历循环链表的特点是, head 可以是循环链表中的任意一个节点;从head开始完整的遍历整个链表一圈;
3. list_entry
list_entry(ptr, type, member).
其中:
1) type是一个结构体类型;
2) member是结构体type内的一个成员的类型;
(由type, member两个参数, 我们可以知道成员变量member在结构体中的偏移量是多少);
3) ptr是一个具体的变量指针,指向一个type结构体数据中的成员变量member;
已知type结构体数据中一个成员变量的地址ptr,就可以求的这个type数据结构的地址;
注意: list_head 类型尽管没有成员变量int value,依旧能够表示所有数据类型的循环链表,很大程度上取决于list_entry)
list_entry的实现
#define list_entry(ptr, type, member) container_of(ptr, type, member)
#define container_of(ptr, type, member)
({
const typeof( ((type*)0)->member ) *__mptr = (ptr); //实质上是简单地将ptr赋值给__mptr;
(type*)( (char *)__mptr - offsetof(type, member)); //实质上是成员变量member的地址(ptr) - 成员变量偏移地址 = 这个type数据结构的地址
})
其中
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE*)0)->MEMBER) //取得成员变量member在结构体type中的偏移量;
1) (TYPE*)0 //将地址0变为一个TYPE类型,假装一个TYPE数据类型放在内存地址0;
2) &((TYPE*)0)->MEMBER // 成员变量MEMBER的绝对地址, 由于其结构体地址为0, 所以这个绝对地址也是成员变量相对于结构体TYPE的相对地址, 也就是偏移量
3) (size_t)&((TYPE*)0)->MEMBER 将这个偏移量变换成size_t 类型
4. linux 内核中访问子进程的代码
struct tast_struct *task;
struct list_head *list;
list_for_each(list, *current->children){
task = list_entry(list, struct task_struct, sibling);
}
父子进程的结构如图所示,是一个用list_head实现的循环链表。 父进程current成员变量children相当于循环链表中的head节点;
子进程中使用成员变量sibling链接在一起;
list_for_each(list, *current->children) //相当于使用list遍历了所有子进程child1.sibling, child2.sibling, child3.sibling, …; 这里必须注意, 我们平常在使用一个指针遍历循环链表的时候, 这个指针指向的是节点(也就是子进程child1); 内核使用list_head的指针list不是指向子进程child1, 而是指向子进程的成员变量sibling或是父进程的成员变量children;
假设此时指针list指向child2.sibling,
task = list_entry(list, task_struct, sibling); 这个函数就取得child2.sibling 成员变量的结构体child2的地址了;
因此,在for循环中, task得到所有子进程的地址;
最后
以上就是繁荣大神为你收集整理的linux内核中循环链表说明_list_head, list_for_each, list_entry的全部内容,希望文章能够帮你解决linux内核中循环链表说明_list_head, list_for_each, list_entry所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复