概述
看内核代码都会发现,内核链表的操作常用的二个宏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则无法继续遍历,删除后会导致无法继续遍历)。
最后
以上就是敏感煎蛋为你收集整理的linux 内核链表遍历宏 list_for_each_entry list_for_each_entry_safe的全部内容,希望文章能够帮你解决linux 内核链表遍历宏 list_for_each_entry list_for_each_entry_safe所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复