终于可以清闲下来打理一下我的blog了,台资企业真的事情很多很烦……
前几篇文章对字符设备有个简单介绍,并以简单的一个字符设备驱动作结尾,其实linux上大部分驱动程序都是字符设备程序,Linux源码中也可以看到很多的字符设备驱动,所以供学习的代码还是很多的。
这一节本想说一下如何测试设备驱动,但是因为最近看了看内核链表,所以趁还记的比较清楚赶紧记录下来。有不到位的地方烦请指正。
链表是线性表的实现方式之一,是一种常用的数据结构,用来组织一系列有序数据,并通过指针将这些有序数列链成一条数据链。相对于数组来说,它有良好的伸缩性,可以动态的添加或者删除等有点(但是访问速度较数组慢)。
数据链表常有两个域 数据域 与 指针域
内核链标是一个双向循环链表,但是它与我们常用的链表有所不同.下边用试图的形式简单介绍一下我们常用的双向链表,与内核链表。因为不怎么懂算法有些描述不到位的地方 包涵
typedef struct NODE{ char data; //数据域 struct NODE *prev; //指针域 struct NODE *next; }NODE;
这里的next 指针指向了下一个链标节点(整个结构体),通过node->data获得链标中的数据, 类似下图(但下图有些不准确)。
struct list_head { struct list_head *next, *prev; };使用内核链表,定义数据结构
struct data_struct{ struct list_head list; char data; };
可以看到内核链表 的next指针指示只想的下一个链表的的指针域,而没有指向整个的结构节点。
在这里我们可以通过内核链标获取获取整个数据结构,并获得有用数据。(还记得container_of函数吗)
这样的好处是:实现了内核链标的通用性,不需要为每个结构定义一个链表,使用统一的内核链表即可。
那我们怎样使用内核链标呢? 如何通过内核链标得到想要的数据结构呢?
struct list_sample{ //示例 struct list_head list; /*The data you need to use*/ char ch; int x; /*************************/ };
使用内核链标需要一个链表头,故需要先行初始化链表头
static inline void INIT_LIST_HEAD(struct list_head *list)使用INIT_LIST_HEAD 传入一个list_head指针,他会将传入的list_head结构的next 于 prev成员都指向他本身。
static inline void list_add(struct list_head *new, struct list_head *head) { __list_add(new, head, head->next); }在head 之后添加一个链表节点。此处会调用一个更底层的函数__list_add。
static inline void __list_add(struct list_head *new, struct list_head *prev, struct list_head *next) { next->prev = new; new->next = next; new->prev = prev; prev->next = new; }
同样可以调用list_add_tail, 在head之前添加一个链表节点。
static inline void __list_del_entry(struct list_head *entry) { __list_del(entry->prev, entry->next); }
static inline void __list_del(struct list_head * prev, struct list_head * next) { next->prev = prev; prev->next = next; }
static inline void list_del(struct list_head *entry)而list_del 函数会将entry节点删除并会将entry的两个成员置为不可用的地址,防止代码重复使用这个节点。
#define list_entry(ptr, type, member) \ container_of(ptr, type, member)
ptr 填入链表节点指针, type填入数据结构体类型, member示意链标在结构体中的名字。
如果现在我们知道链表的指针为entry,就可以这样获取整个结构体。
struct list_sample sample = list_entry(entry, struct list_sample, list);
以上是内核链表的最基本的使用方法。
参考文档: