Linux驱动编程 step-by-step (十) Linux 内核链表

转载 2012年03月21日 10:31:09

  

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

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

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

Linux 内核链表

链表的简单介绍:

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

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

内核链标是一个双向循环链表,但是它与我们常用的链表有所不同.

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

  1. typedef struct NODE{  
  2.     char data;  
  3.     struct NODE *prev;  
  4.     struct NODE *next;  
  5. }NODE;  

这里的next 指针指向了下一个链标节点,通过node->data获得链标中的数据, 类似下图(但下图有些不准确)。

内核链表结构定义:

  1. struct list_head {  
  2.     struct list_head *next, *prev;   
  3. };  
使用内核链表,定义数据结构
  1. struct data_struct{  
  2.     struct  list_head list;  
  3.     char data;  
  4. };  

在这里我们可以通过内核链标获取获取整个数据结构,并获得有用数据。

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

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

使用内核链表:

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

  1. struct list_sample{  
  2.     struct list_head list;  
  3.     /*The data you need to use*/  
  4.     char ch;  
  5.     int x;  
  6.     /*************************/  
  7. };  

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

初始化内核链表

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

  1. static inline void INIT_LIST_HEAD(struct list_head *list)  
使用INIT_LIST_HEAD 传入一个list_head指针,他会将传入的list_head结构的next 于 prev成员都指向他本身。

添加一个链表节点

  1. static inline void list_add(struct list_head *newstruct list_head *head)                                                                                                                                       
  2. {  
  3.     __list_add(new, head, head->next);                       
  4. }  
在head 之后添加一个链表节点。此处会调用一个更底层的函数__list_add。
  1. static inline void __list_add(struct list_head *new,  
  2.                   struct list_head *prev,          
  3.                   struct list_head *next)                                                                                                                                                                        
  4. {  
  5.     next->prev = new;  
  6.     new->next = next;  
  7.     new->prev = prev;  
  8.     prev->next = new;  
  9. }  

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

同样可以调用list_add_tail, 在head之前添加一个链表节点。

删除链表节点:

  1. static inline void __list_del_entry(struct list_head *entry)  
  2. {  
  3.     __list_del(entry->prev, entry->next);  
  4. }  
  1. static inline void __list_del(struct list_head * prev, struct list_head * next)  
  2. {  
  3.     next->prev = prev;  
  4.     prev->next = next;  
  5. }  

这里即将 entry节点从链表中删除(其实节点仍存在只是不在链表里边了)
  1. static inline void list_del(struct list_head *entry)  
而list_del 函数会将entry节点删除并会将entry的两个成员置为不可用的地址,防止代码重复使用这个节点。

获取数据结构

  1. #define list_entry(ptr, type, member) \  
  2.     container_of(ptr, type, member)  

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

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

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

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

以上是最基本的使用方法。







相关文章推荐

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

阻塞型字符设备驱动 前面说到了 如何实现read write 等操作,但如果设备缓冲已满,现在想而用户此时又想写入设备,此请求就无法立即执行,那怎么办呢? 第一种情况是:驱动程序想用户返回请求失败...

Linux驱动编程 step-by-step (六) .

说点上节没有讲完的话题 用户地址检测 简单模块调试 以及一些杂项 检测用户空间地址的有效性 上一节中提到在read write时候要检测用户空间传递的参数地址是否是有效地址,有的内核函数会自...
  • huzm08
  • huzm08
  • 2011年11月15日 08:58
  • 135

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

字符设备中 重要的数据结构 大部分字符驱动设计三个重要的数据结构 struct file_operations struct file struct inode   一、文件操...

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

并发 竞态 (信号量与自旋锁) 代码传至并发竞态控制 并发进程 导致竞态的一个例子 前面所述的字符驱动都是没有考虑并发竟态的情况,想象一下 一个进程去读一个字符设备,另一个进程在同...

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

字符设备模拟pipe的驱动程序 让我们用一个”pipe“的设备驱动去结束简单字符设备吧(这里所说的pipe并非标准的pipe只是模拟了一个从一端写入从另一端写入的设备) 测试代码1      测试...

Linux驱动编程 step-by-step

第三次看了LDD3了(虽然现在已经是kernel3.0但从这本书商还是能学到很多) 每次都有一些收获 现在终于能够写一写代码了 驱动程序的作用: 简单来说 驱动程序就是使计算机与设备通信的特殊的代...

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

第三次看了LDD3了(虽然现在已经是kernel3.0但从这本书商还是能学到很多) 每次都有一些收获 现在终于能够些一些代码了 驱动程序的作用: 简单来说 驱动程序就是使计算机与设备通信的特殊的代码,...

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

简单字符设备驱动 1、主次设备号 主设备号标识设备连接的的驱动,此设备好由内核使用,标识在相应驱动下得对应的设备 在linux中设备号是一个32位的dev_t类型 typedef __...
  • huzm08
  • huzm08
  • 2011年11月15日 08:53
  • 162

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

似乎每一章介绍的内容比较少,但学习是一个循序渐进的过程,不在于一天学多少,重要的一天能真正的学懂多少,所以我主张一步一步来,从多个渠道去学习知识,实现互补。 本节测试代码传到此处了:char_ste...

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

说点上节没有讲完的话题 用户地址检测 简单模块调试 以及一些杂项 检测用户空间地址的有效性 上一节中提到在read write时候要检测用户空间传递的参数地址是否是有效地址,有的内核函数会自...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Linux驱动编程 step-by-step (十) Linux 内核链表
举报原因:
原因补充:

(最多只允许输入30个字)