linux 内核链表遍历宏 list_for_each_entry list_for_each_entry_safe

看内核代码都会发现,内核链表的操作常用的二个宏list_for_each_entry和list_for_each_entry_safe
循序渐进,先从最底层的函数container_of 函数说起,其内核定义如下:

先看offsetof宏,根据优先级的顺序,最里面的小括号优先级最高,

TYPE *将整型常量0强制转换为TYPE型的指针,且这个指针指向的地址为0,

也就是将地址0开始的一块存储空间映射为TYPE型的对象,

接下来再对结构体中MEMBER成员进行取址,而整个TYPE结构体的首地址是0,

这里获得的地址就是MEMBER成员在TYPE中的相对偏移量。

再将这个偏移量强制转换成size_t型数据(无符号整型)。

所以整个offsetof的功能就是获取MEMBER成员在TYPE型数据中的偏移量

再看看container_of宏,他首先赋值member成员的指针给__mptr,

然后减去成员在结构体中的相对偏移量,

这样就可以获得对应的结构体的(地址)指针;

从注释可以看出,他的作用是获取结构体实体指针,他其实就是container_of
进入主题来看list_for_each_entry,宏定义如下所示:

首先需要了解双向链表和链表头的概念:建立一个双向链表通常有一个独立的用于管理链表的链表头,链表头一般是不含有实体数据的,必须用INIT_LIST_HEAD()进行初始化,表头建立以后,就可以将带有数据结构的实体链表成员加入到链表中;链表头和链表的示意如下:


 
list_for_each_entry宏是一个for循环语句,

for循环的第一个参数就是让(head)->next指向member成员所在数据结构的指针,

也就是将pos初始化为链表头指向的第一个实体链表成员,

for的第三句话通过pos->member.next指针遍历整个实体链表,

当pos->member.next再次指向我们的链表头的时候跳出for循环。

整个过程没有对链表头进行遍历(不需要被遍历),

所以使用list_for_each_entry遍历链表必须从链表头开始。

因此可以看出,list_for_each_entry的功能就是遍历以head为链表头的实体链表,对实体链表中的数据结构进行处理;

相比于list_for_each_entry,list_for_each_entry_safe用指针n对链表的下一个数据结构进行了临时存储,

所以如果在遍历链表的时候需要做删除链表中的当前项操作时,

用list_for_each_entry_safe可以安全的删除,而不会影响接下来的遍历过程(用n指针可以继续完成接下来的遍历,

list_for_each_entry则无法继续遍历,删除后会导致无法继续遍历)。

 

 

  • 8
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值