我们之前认为的链表和写的链表 是这样定义的:
这种方法就是将数据结构嵌入链表
struct list_node{
char buf[128];
int num;
struct list_node *prev;
struct list_node *next;
}
我们这样定义一个链表的节点,这个节点包含了 char buf[128] 和 int num 这两个数据就是数据域
prev和next就是指针域
可见我们是把数据结构放在了链表里,这样子就有一个问题:我们定义的list_node 的复用率很低。因为如果在其他场景需要使用链表 但是每个节点需要添加一个char name 那我们就要重新编写一个
struct list_node ! 这种低效率的方法 内核肯定是不会使用的!
Linux内核中的链表
内核中的链表的使用是将链表嵌入到数据结构中的!
内核中的标准链表是环形双向链表:
双向环形链表就像一个圈一样,所以每一个节点都可以作为头结点、尾结点
内核版本:4.1.15, 在目录include/linux/types.h中定义了list_head结构:
struct list_head {
struct list_head *next, *prev;
};
next指针指向下一个链表节点,prev指针指向前一个节点。
可以看到,很简单,Linux内核中不是将数据结构嵌入链表,而是将链表嵌入数据结构。
list_head本身其实并没有意义——它需要被嵌入到你自己的数据结构中才能生效!
举个例子:
struct data_struct{
char buf[128];
int num;
struct list_head node;
}
我们将链表嵌入数据结构,将链表的节点和数据分离开来,这样对于不同的数据域需求 我们都可以通过list_head作为链表的节点。
contianer_of ()宏
container_of宏的作用:
通过:
1.指向该结构体中的成员member的指针ptr
2.该结构体类型type
3.成员member
来获得该数据结构的起始地址
(上面说的成员member 就比如我数据结构体定义的是:struct data{ struct list_head list } 那list就可以作为成员member传入 作为container_of的第三个参数)
使用宏container_of()我们可以很方便地从链表指针找到父结构中包含的任何变量。这时因为在C语言中,一个给定结构体中的变量偏移在编译时地址就被ABI固定下来了。
container_of宏的定义: