Linux内核(10) - 内核中的链表
早上上班坐地铁要排队,到了公司楼下等电梯要排队,中午吃饭要排队,下班了追求一个女孩子也要排队,甚至在网上下载个什么门的短片也要排队,每次看见人群排成一条长龙时,才真正意识到自己是龙的传人。那么下面咱们就说说队列(链表)。
使用链表的目的很明确,因为有很多事情要做,于是就把它放进链表里,一件事一件事的处理。比如在USB子系统里,U盘不停的提交urb请求,USB键盘也提交,USB鼠标也提交,那USB主机控制器咋应付得过来呢?很简单,建一个链表,然后你每次提交就是往里边插入,然后USB主机控制器再统一去调度,一个一个来执行。这里有力得证明了,谭浩强大哥的C程序设计是我们学习Linux的有力武器,书中对链表的介绍无疑是英明的,谭大哥,您不是一个人在战斗!
内核中链表的实现位于include/linux/list.h文件,链表数据结构的定义也很简单。
21 struct list_head {
22 struct list_head *next, *prev;
23 };
list_head结构包含两个指向list_head结构的指针prev和next,由此可见,内核中的链表实际上都是双链表(通常都是双循环链表)。
通常,我们在数据结构课堂上所了解的链表定义方式是这样的(以单链表为例):
struct list_node {
struct list_node *next;
ElemType data;
};
通过这种方式使用链表,对每一种数据类型,都要定义它们各自的链表结构。而内核中的链表却与此不同,它并没有数据域,不是在链表结构中包含数据,而是在描述数据类型的结构中包含链表。
比如在hub驱动中使用struct usb_hub来描述hub设备,hub需要处理一系列的事件,比如当探测到一个设备连进来时,就会执行一些代码去初始化该设备,所以hub就创建了一个链表来处理各种事件,这个链表的结构如下图。
(1)声明与初始化。
链表的声明可以使用两种方式,一种为使用LIST_HEAD宏在编译时静态初始化,一种为使用INIT_LIST_HEAD()在运行时进行初始化。
25 #define LIST_HEAD_INIT(name) { &(name), &(name) }
26
27 #define LIST_HEAD(name) /
28 struct list_head name = LIST_HEAD_INIT(name)
30 static inline void INIT_LIST_HEAD(struct list_head *list)
31 {
32 list->next = list;
33 list->prev = list;
34 }
无论采用哪种方式,新生成的链表头的两个指针next、prev都初始化为指向自己。
(2)判断链表是否为空。
298 static inline int list_empty(const struct list_head *head)
299 {
300 return head->next == head;
301 }
(3)插入。
有了链表,自然就要往里面加东西、减东西。就像我们每个人每天都在不停的走进去,又走出来,似是梦境又不是梦境。一切都是不经意的。走进去是一年四季,走出来是春夏秋冬。list_add()和list_add_tail()这两个函数就是往队列里加东西。
67 static inline void list_add(struct list_head *new, struct list_head *head)
68 {
69 __list_add(new, head, head->next);
70 }
84 static inline void list_add_tail(struct list_head *new, struct list_head *head)
85 {
86 __list_add(new, head ->prev, head);
87 }
其中,list_add()将数据插入在head之后,list_add_tail()将数据插入在head->prev之后。其实对于循环链表来说,表头的next、prev分别指向链表中的第一个和最后一个节点,所以,list_add()和list_add_tail()的区别并不大。
(4)删除。
搞懂谭浩强那本书之后看这些链表的代码那就是小菜一碟。再来看下一个 list_del_init(),里的元素不能只加不减,没用了的元素就该删除掉,把空间腾出来给别人。郭敬明说过,我生命里的温暖就那么多,我全部给了你,但是你离开了我,你叫我以后怎么再对别人笑……
链表里的元素不能只加不减,没用了的元素就应该删除掉。
254 static inline void list_del_init(struct list_head *entry)
255 {
256 __list_del(entry->prev, entry->next);
257 INIT_LIST_HEAD(entry);
258 }
list_del_init()从链表里删除一个元素,并且将其初始化。
(5)遍历。
内核中的链表仅仅保存了list_head结构的地址,我们如何通过它或取一个链表节点真正的数据项?这就要提到有关链表的所有操作里面,最为重要超级经典的list_entry宏了,我们可以通过它很容易地获得一个链表节点的数据。
425 #define list_entry(ptr, type, member) /
426 container_of(ptr, type, member)
我相信,list_entry()这个宏在Linux内核代码中的地位,就相当于广告词中的任静付笛生的洗洗更健康,相当于大美女关之琳的一分钟轻松做女人,这都是耳熟能详妇孺皆知的,是经典中的经典。如果你说你不知道list_entry(),那你千万别跟人说你懂Linux内核,就好比你不知道陈文登不知道任汝芬你就根本不好意思跟人说你考过研,要知道每个考研人都是左手一本陈文登右手一本任汝芬。
可惜,关于list_entry,这个谭浩强老师的书里就没有了,当然你不能指责谭浩强的书不行,再好的书也不可能包罗万象。
关于list_entry(),让我们结合实例来看,还是hub驱动的那个例子,当我们真的要处理hub的事件的时候,我们当然需要知道具体是哪个hub触发了这起事件。而list_entry的作用就是,从struct list_head event_list得到它所对应的struct usb_hub结构体变量。比如以下四行代码:
struct list_head *tmp;
struct usb_hub *hub;
tmp = hub_event_list.next;
hub = list_entry(tmp, struct usb_hub, event_list);
从全局链表hub_event_list中取出一个来,叫做tmp,然后通过tmp,获得它所对应的struct usb_hub。
Linux内核(10) - 内核中的链表的更多相关文章
- Linux系统启动那些事—基于Linux 3.10内核【转】
转自:https://blog.csdn.net/shichaog/article/details/40218763 Linux系统启动那些事—基于Linux 3.10内核 csdn 我的空间的下载地 ...
- Linux 4.10中两个新特性与我的一段故事
今早5点半起来没有開始写文章,而是去西湾红树林连跑带走折腾了将近20公里.回来后就8点多了...洗了个澡之后坐稳当.開始写一段关于我的故事. 在2014年到2015年期间,我在负责研发一 ...
- 交叉编译和安装ARM板(RK3288)和Linux 3.10上的RTL8188无线网卡驱动
插入无线网卡,输入ifconfig,发现没有检测到网卡. 输入lsusb,查看无线网卡型号. 我用的无线网卡是EDUP的网卡,包装盒里有一张驱动光盘,把光盘里linux下的驱动目录复制下来.如果没有驱 ...
- linux内核中的链表
1.内核中的链表 linux内核链表与众不同,他不是把将数据结构塞入链表,而是将链表节点塞入数据,在2.1内核中引入了官方链表,从此内核中所有的链表使用都采用此链表,千万不要在重复造车轮子了!链表实现 ...
- Linux 2.6内核中新的锁机制--RCU
转自:http://www.ibm.com/developerworks/cn/linux/l-rcu/ 一. 引言 众所周知,为了保护共享数据,需要一些同步机制,如自旋锁(spinlock),读写锁 ...
- Linux内核分析--内核中的数据结构双向链表续【转】
在解释完内核中的链表基本知识以后,下面解释链表的重要接口操作: 1. 声明和初始化 实际上Linux只定义了链表节点,并没有专门定义链表头,那么一个链表结构是如何建立起来的呢?让我们来看看LIST_H ...
- Linux内核分析--内核中的数据结构双向链表【转】
本文转自:http://blog.csdn.net/yusiguyuan/article/details/19840065 一.首先介绍内核中链表 内核中定义的链表是双向链表,在上篇文章--libev ...
- linux字符设备驱动中内核如何调用驱动入口函数 一点记录
/* 内核如何调用驱动入口函数 ? *//* 答: 使用module_init()函数,module_init()函数定义一个结构体,这个结构体里面有一个函数指针,指向first_drv_init() ...
- Linux 2.6 内核实时性分析 (完善中...)
经过一个月的学习,目前对linux 下驱动程序的编写有了入门的认识,现在需要着手实践,编写相关的驱动程序. 因为飞控系统对实时性有一定的要求,所以先打算学习linux 2.6 内核的实时性与任务调 ...
随机推荐
- php过滤文字中的表情字符和mysql服务端对emoji的支持
1.过滤emoji表情的原因 在我们的项目开发中,emoji表情是个麻烦的东西,即使我们可以能存储,也不一定能完美显示,因为它的更新速度很快:在iOS以外的平台上,例如PC或者android.如果你需 ...
- SELinux安全系统基础
一.SELinux简介 SELinux(Secure Enhanced Linux)安全增强的Linux是由美国国家安全局NSA针对计算机基础结构安全开发的一个全新的Linux安全策略机制.SELin ...
- Android实现Material Design风格的设置页面(滑动开关控件)
前言 本文链接 http://blog.csdn.net/never_cxb/article/details/50763271 转载请注明出处 參考了这篇文章 Material Design 风格的设 ...
- Java 基本概念
1.JVM -- java virtual machine JVM就是我们常说的java虚拟机,它是整个java实现跨平台的 最核心的部分,所有的java程序会首先被编译为.class的类文件,这种类 ...
- 算法笔记_184:历届试题 约数倍数选卡片(Java)
目录 1 问题描述 2 解决方案 1 问题描述 问题描述 闲暇时,福尔摩斯和华生玩一个游戏: 在N张卡片上写有N个整数.两人轮流拿走一张卡片.要求下一个人拿的数字一定是前一个人拿的数字的约数或倍数 ...
- ERROR 1227 (42000): Access denied; you need (at least one of) the SUPER privilege(s) for this operation
开启super权限: 1. update user set Super_priv=‘Y’ where User=‘root’2. flush privileges
- $ionicModal
Ionic中[弹出式窗口]有两种(如下图所示),$ionicModal和$ionicPopup; $ionicModal是完整的页面: $ionicPopup是(Dialog)对话框样式的,直接用Ja ...
- IE hack 汇总
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- JavaScript 复制内容到剪贴板
<html> <head> <title>Selector</title> <script language="javascript&q ...
- 生成CFree 5.0 注册码
C-Free 5.0真是一个很棒的编程软件,可今天用着用着却让我注册,下面的注册码分享给大家: 方法一.下载CFree 5.0之后,直接输入以下信息进行注册. 用户名:tianfang电子邮件:qua ...