Linux驱动编程 step-by-step (十)

终于可以清闲下来打理一下我的blog了,台资企业真的事情很多很烦……

前几篇文章对字符设备有个简单介绍,并以简单的一个字符设备驱动作结尾,其实linux上大部分驱动程序都是字符设备程序,Linux源码中也可以看到很多的字符设备驱动,所以供学习的代码还是很多的。

这一节本想说一下如何测试设备驱动,但是因为最近看了看内核链表,所以趁还记的比较清楚赶紧记录下来。有不到位的地方烦请指正。

Linux 内核链表(1)

链表的简单介绍:

链表是线性表的实现方式之一,是一种常用的数据结构,用来组织一系列有序数据,并通过指针将这些有序数列链成一条数据链。相对于数组来说,它有良好的伸缩性,可以动态的添加或者删除等有点(但是访问速度较数组慢)。

数据链表常有两个域 数据域 与 指针域

内核链标是一个双向循环链表,但是它与我们常用的链表有所不同.下边用试图的形式简单介绍一下我们常用的双向链表,与内核链表。因为不怎么懂算法有些描述不到位的地方 包涵

常用的双向链标定义如下:

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;
};

Linux驱动编程 step-by-step (十)_第1张图片

可以看到内核链表 的next指针指示只想的下一个链表的的指针域,而没有指向整个的结构节点。
在这里我们可以通过内核链标获取获取整个数据结构,并获得有用数据。(还记得container_of函数吗)

这样的好处是:实现了内核链标的通用性,不需要为每个结构定义一个链表,使用统一的内核链表即可。

那我们怎样使用内核链标呢? 如何通过内核链标得到想要的数据结构呢?

使用内核链表:

定义包含内核链标的数据结构

struct list_sample{ //示例
    struct list_head list;
    /*The data you need to use*/
    char ch;
    int x;
    /*************************/
};

只要在数据结构中包含struct list_head成员即可。

初始化内核链表

使用内核链标需要一个链表头,故需要先行初始化链表头

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;
}

它会将新的节点放于prev 与 next之间。

同样可以调用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;
}

这里即将 entry节点从链表中删除(其实节点仍存在只是不在链表里边了)

static inline void list_del(struct list_head *entry)
而list_del 函数会将entry节点删除并会将entry的两个成员置为不可用的地址,防止代码重复使用这个节点。

获取数据结构

#define list_entry(ptr, type, member) \
    container_of(ptr, type, member)

定义 list_entry宏 来获取数据结构,它会调用container_of 宏来获取这个结构体(container_of 宏我在前边的文章里边已经阐述过)

ptr 填入链表节点指针, type填入数据结构体类型, member示意链标在结构体中的名字。

如果现在我们知道链表的指针为entry,就可以这样获取整个结构体。

struct list_sample sample = list_entry(entry, struct list_sample, list);

以上是内核链表的最基本的使用方法。
参考文档:

http://www.ibm.com/developerworks/cn/linux/kernel/l-chain/

你可能感兴趣的:(Linux驱动编程 step-by-step (十))