《[arm驱动]linux内核链表》涉及内核驱动函数五个,内核结构体一个,分析了内核驱动函数二个;可参考的相关应用程序模板或内核驱动模板零个,可参考的相关应用程序模板或内核驱动一个
一、描述
链表是一种常用的数据结构,它通过指针将一系列数据节点连接成一条数据链。相对于数组,链表具有更好的动态性,建立链表时无需预先知道数据总量,可以随机分配空间,可以高效地在链表中的任意位置实时插入或删除数据。链表的开销主要是访问的顺序性和组织链的空间损失。通常链表数据结构至少包含两个域:数据域和指针域,数据域用于存储数据,指针域用于建立与下一个节点的联系。Linux内核中使用了大量的链表结构来组织数据。这些链表大多采用了include/linux/list.h中实现的一套精彩的链表数据结构。
二、结构提及函数
结构体一)1、结构体:双向循环链表
struct list_head
{
struct list_head *next, *prev;
};
2、相关函数
内核驱动函数一)初始化
INIT_LIST_HEAD(list_head *head)
内核驱动函数二)插入节点
list_add(struct list_head *new, struct list_head *head)
list_add_tail(struct list_head *new, struct list_head *head)
内核驱动函数三)删除节点
list_del(struct list_head *entry)
内核驱动函数四)提取数据结构(获取一个节点)
list_entry(ptr, type, member)
内核驱动函数五)遍历节点
list_for_each(pos, head)
内核源码一)函数原型内核中的定义
//INIT_LIST_HEAD构造双向循环链表,将首尾相连 #define INIT_LIST_HEAD(ptr) do { (ptr)->next = (ptr); (ptr)->prev = (ptr); } while (0) #define list_for_each(pos, head) for (pos = (head)->next; prefetch(pos->next), pos != (head); pos = pos->next) #define list_entry(ptr, type, member) ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
4、关于list_entry(ptr, type, member) 详解
内核源码二)
#define list_entry(ptr, type, member)
((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))在0这个地址看做有一个虚拟的type类型的变量,那么取一个成员再取这个成员的地址,就是这个结构体中这个成员的绝对地址 。
a)list_entry的原理结合代码分析
typedef struct { int i; int j; }exp; 这个exp结构体占用8个字节,假设声明一个变量。 exp e1; 那么假如已知e1.j的地址,想知道e1的地址该如何办呢?只要知道j在e1中的偏移,然后把j的地址减去这个偏移就是e1的地址了。 int *p = e1.j; 假设e1的地址是0x100,那么p就是0x104。 list_entry(p, exp, j); 变成: (exp *)((char *)p-(unsigned long)(&((exp *)0)->j)) ,在exp结构体中j成员的绝对地址是4,所以&((exp *)0)->j 就是4 &e1 == list_entry(p, exp, j)
实例一)三、使用案例:
module_exit(mylist_exit);
本文转自lilin9105 51CTO博客,原文链接:http://blog.51cto.com/7071976/1391684,如需转载请自行联系原作者