u-boot linux链表的管理

参考:https://blog.csdn.net/qqliyunpeng/article/details/53789082

感谢作者: 李云鹏(qqliyunpeng@sina.cn)

 

1. 链表数据结构 list_head 的定义:

Simple doubly linked list implementation.
struct list_head {
    struct list_head *next, *prev;
};
 

2. 声明和初始化:有两种方法

 ①声明的时候初始化一个链表 LIST_HEAD 宏:

 

②运行时初始化链表  INIT_LIST_HEAD 函数:

3. 插入/删除/合并

a) 插入

对链表的插入操作有两种:

在表头插入 list_add函数 和在表尾插入 list_add_tail函数:

b) 删除

对链表的删除操作函数有两种:

list_del函数和

list_del_init函数:

 

c) 替换

 

对链表的替换操作有两个:

list_replace函数和

list_replace_init函数:

 

d) 搬移

搬移的含义是将原本属于一个链表的节点移动到另一个链表的操作,有两个函数:

list_move函数和

list_move_tail函数:

e) 合并

合并在这里的意思就是合并了,是将两个独立的链表合并成为一个链表,合并的时候根据合并的位置的不同可以分为:

合并到头部的 list_splice函数和

合并到尾部的 list_splice_tail函数:(这两个函数有推荐使用的函数)

 

4. 找到链表中的数据

 

     前边提到的函数都是操作的链表节点的入口,但是对于我们真正有意义的是节点上的数据,链表的头上没有数据,其他的节点上都是带有数据的。如何从一个链表节点的入口得到节点的数据呢?要用到以下的函数:

list_entry函数:

/**
* list_entry - 获得含链表入口的结构体首地址
* @ptr: member的首地址
* @type: 容器的类型
* @member: 要得到他的容器的某个成员
*/
#define list_entry(ptr, type, member) \
container_of(ptr, type, member)

#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) // 将数据结构体放到0地址处,天然的结构体中成员的首地址就是成员在结构体中的偏移量

我们知道list_for_each_entry会用到list_entry,而list_entry用到container_of,而container_of中有offsetof,所以从offsetof讲起。
offsetof(TYPE, MEMBER) ((size_t)&((TYPE *)0)->MEMBER)
理解offsetof的关键在于&((TYPE *)0)->MEMBER,几乎可以说只要理解了这一部分,后面的几个函数都能够解决,那么我们看看这一部分究竟完成了怎样的工作。根据优先级的顺序,最里面的小括号优先级最高,TYPE *将整型常量0强制转换为TYPE型的指针,且这个指针指向的地址为0,也就是将地址0开始的一块存储空间映射为TYPE型的对象,接下来再对结构体中MEMBER成员进行取址,而整个TYPE结构体的首地址是0, 这里获得的地址就是MEMBER成员在TYPE中的相对偏移量 。再将这个偏移量强制转换成size_t型数据(无符号整型)。

首先可以看出container_of被预定义成一个函数,函数的第一句话,通过((type *)0)->member定义一个MEMBER型的指针__mptr,这个指针指向ptr,所以第一句话获取到了我们要求的结构体,它的成员member的地址,接下来我们用这个地址减去成员member在结构体中的相对偏移量,就可以获取到所求结构体的地址, (char *)__mptr - offsetof(type,member)就实现了这个过程,最后再把这个地址强制转换成type型指针, 就获取到了所求结构体指针, define预定义返回最后一句话的值, 将所求结构体指针返回。

所以整个container_of的功能 就是通过指向结构体成员member的指针ptr获取指向整个结构体的指针 。container_of清楚了,那list_entry就更是一目了然了。

 

5. 遍历链表

对linux内核的遍历可以分为遍历链表和遍历链表中的结构体:

从头开始遍历链表,list_for_each宏,

从头开始遍历链表中的结构体,list_for_each_entry宏:

 

6. 安全性

只讲一点判断链表是不是为空:

list_empty宏:

 

static inline int list_empty(const struct list_head *head)
{
  return head->next == head;
}

posted @ 2022-02-12 13:18  liujunhuasd  阅读(52)  评论(0编辑  收藏  举报