Linux内核设计与实现第十周读书笔记
第十七章 设备与模块
关于设备驱动与设备管理,我们讨论四种内核成分。
- 设备类型
- 模块
- 内核对象
- sysfs
17.1设备类型
在Linux以及所有Unix系统中,设备被分为以下三种类型:
- 块设备,块设备通常缩写为blkdev,它是可寻址的,寻址以块为单位,块大小随设备不同而不同;块设备通常支持重定位操作,也就是对数据的随机访问。块设备是通过称为“块设备节点”的特殊文件来访问,并且通常被挂载为文件系统。
- 字符设备,字符设备通常缩写为cdev,它是不可寻址的,仅提供数据的流式访问,就是一个一个字符,或者一个一个字节。字符设备通过称为“字符设备节点”的特殊文件访问。与块设备不同,应用程序直接通过访问设备节点与字符设备交互。
- 网络设备最常见的类型有事也以以太网设备来称呼,它提供了对网络的访问,这是通过一个物理适配器和一种特定的协议来进行的。它不通过设备节点来访问,而是通过套接字API这样的特殊接口来访问。
并不是所有设备驱动都表示物理设备。有些设备驱动是虚拟的,仅提供访问内核功能而已。我们成为“伪设备”。
17.2模块
支持模块的好处是基本内核镜像可以尽可能地小,因为可选的功能和驱动程序可以利用模块形式再提供。模块允许我们方便地删除和重新载入内核代码,也方便了调试工作。
17.2.1Hello,World
MOUDLE_LICENSE()
宏用于指定模块的版权。
MOUDLE_AUTHOR()
宏和MOUDLE_DESCRIPTION()
宏指定了代码作者和模块的简要描述,它们完全是用作信息记录目的。
17.2.2构建模块
1.放在内核源代码树中
- 你需要在
drivers/char/
目录下建立一个名为fishing
的子目录。 - 接下来需要在
drivers/char/
下的Makefile
文件中添加一行。编辑drivers/char/Makefile/
并加入obj-m += fishing/
。该行编译指令告诉模块构建系统,在编译模块时需要进入fishing/
子目录中。更可能发生的情况是驱动程序的编译取决于一个特殊配置选项,比如可能的CONFIG_FISHING_POLE
,如果这样,需要用下面的指令替换刚才的指令:obj-$(CONFIG_FISHING_POLE) +=fishing/
- 最后,在
drivers/char/fishing/
下,需要添加一个新的Makefile
文件,其中需要有这条指令:obj-m += fishing.o
2.放在内核代码外
- 在你自己的源代码树目录中建立一个
Makefile
文件,只需要一行指令:obj-m := fishing.o
- 告诉make如何找到内核源代码文件和基础的
Makefile
文件。make –C /kernel/source/location SUBDIRS=$PWD modules
17.2.3安装模块
下面的构建命令用来安装编译的模块到合适的目录下:make moudles_install
,通常需要以root权限运行。
17.2.4产生模块依赖性
- 若想产生内核依赖关系的信息,root用户可运行命令
depmod
。 - 为了执行更快的更新操作,那么可以只为新模块生成依赖信息,而不是生成所有的依赖关系,这是root用户可运行命令的
depmod -A
。
17.2.5载入模块
- 载入模块,以root身份运行命令:
insmod module.ko
- 卸载一个模块,以root身份运行:
rmmod module
- 为了在内核
via modprobe
中插入模块,需要以root身份运行:modprobe module[module parameters]
modprobe
命令也可用来从内核中卸载模块,当然这也需要以root身份运行:modprobe -r modules
与rmmod命令不同,modprobe
也会卸载给定模块所依赖的相关模块,但其前提是这些相关模块没有被使用。
17.2.6管理配置选项
如果你建立了一个新子目录,而且也希望kconfig文件存在于该目录中的话,那么你必须在一个已存在的kconfig文件中将它引入。你需要加入下面一行指令:source "drivers/char/fishing/Kconfig"
17.2.7模块参数
定义一个模块参数可通过宏module_param()
完成:module_param(name,type,perm);
参数name既是用户可见的参数名,也是你模块中存放模块参数的变量名。参数type则存放了参数的类型。最后一个参数perm指定了模块在sysfs文件系统下对应文件的权限。
17.2.8导出符号表
在内核中,导出内核函数需要使用特殊的指令:EXPORT_SYMBOL()
和EXPORT_SYMBOL_GPL()
。
导出的内核函数可以被模块调用。导出的内核符号表被看作导出的内核接口,甚至称为内核API。
17.3设备模型
17.3.1kobject
设备模型的核心部分是kobject(kernel object)
。kobject
提供了诸如引用计数、名称和父指针等字段,可以创建对象的层次结构。
name指针指向kobject的名称。
parent指针指向kobject的父对象。
kref提供引用计数,ktype和kset结构体对kobject对象进行描述和分类。
struct kobject {
constchar *name;
structlist_head entry;
structkobject *parent;
structkset *kset;
structkobj_type *ktype;
structsysfs_dirent *sd;
structkref kref;
unsignedint state_initialized:1;
unsignedint state_in_sysfs:1;
unsignedint state_add_uevent_sent:1;
unsignedint state_remove_uevent_sent:1;
unsignedint uevent_suppress:1;
};
17.3.2ktype
struct kobj_type {
void(*release)(struct kobject *kobj);
structsysfs_ops *sysfs_ops;
structattribute **default_attrs;
};
ktype是为了描述kobject所具有的普遍特性。
release指针指向在kobject引用计数减至0时要被调用的析构函数。该函数负责释放所有kobject使用的内存和其他相关清理工作。
sysfs_ops
变量指向sysfs_ops
结构体。该结构体描述了sysfs文件读写时的特性。
default_attrs
指向一个attribute结构体数组。这些结构体定义了该kobject相关的默认属性。
17.3.3kset
kset是kobject对象的集合体。kset可把kobject集中到一个集合中,而ktype描述相关类型kobject所共有的特性。区别:具有相同ktype的kobject可以被分组到不同的kset。就是说在Linux内核中只有少数一些的ktype,却有多个kset。
struct kset {
structlist_head list;
spinlock_tlist_lock;
structkobject kobj;
structkset_uevent_ops *uevent_ops;
};
list连接kset中所有的kobject对象,list_lock
是保护这个链表元素的自旋锁,kobj指向的kobject对象代表了该集合的基类。uevent_ops
指向一个结构体用于处理集合中kobject对象的热插拔操作。
17.3.4kobject、ktype和kset的相互关系
kobject本身的意义不大,通常情况下需要被嵌入到其他数据结构中,让那些包含它的结构具有了kobject的特性。
kobject与一个特别的ktype对象关联,ktype定义了一些kobject相关的默认特性:析构行为、sysfs行为以及别的一些默认属性。
kobject又归入了称作kset的集合。kset提供了两个功能:1、其中嵌入的kobject作为kobject组的基类2、kset将相关的kobject集合在一起。
17.3.5管理和操作kobject
- 使用kobject的第一步需要先来生命和初始化。kobject通过函数kobiect_init进行初始化。
- 该函数的第一个参数就是需要初始化的kobject对象,在调用初始化函数前,kobject必须清空。
- 在清零后,就可以安全的初始化parent和kset字段。
- 这多步操作也可以由
kobject_create()
来自动处理,它返回一个新分配的kobject。
17.3.6引用计数
kobject的主要功能之一就是为我们提供了一个统一的引用计数系统。
1.递增和递减引用计数
- 增加一个引用计数可通过
kobject_get()
函数完成:struct kobject * kobject_get(struct kobject *kobj);
- 减少引用计数通过
kobject_put()
完成:void kobject_put(struct kobject *kobj);
2.kref
struct kref{
atomic_t refcount;
};
其中唯一的字段是用来存放引用计数的原子变量。那么为什么采用结构体?这是为了便于进行类型检测。
17.4sysfs
sys文件系统是一个处于内存中的虚拟文件系统,它为我们提供了kobject对象层次结构的视图。
17.4.1sysfs中添加和删除kobject
想要把kobject导入sysfs,需要用到函数kobject_add ()
:int kobject_add(struct kobject *kobj,struct kobject *parent,const char *fmt, ...);
kobject在sysfs中的位置取决于kobject在对象层次结构中的位置。
从sysfs中删除一个kobject对应文件目录,需要使用函数kobject_del()
:void kobject_del(struct kobject *kobj);
17.4.2向sysfs中添加文件
1.默认属性
默认的文件集合是通过kobject和kset中的ktype字段提供的。因此所有具有相同类型的kobject在它们对应的sysfs目录下都拥有相同的默认文件集合。这些属性负责将内核数据映射成sysfs中的文件。
2.创建新属性
内核为能在默认集合之上,再添加新属性而提供了sysfs_create_file()
接口:int sysfs_cerate_file(struct kobject *kobj,const struct attribute *attr);
除了添加文件外,还有可能需要创建符号连接。在sysfs中创建一个符号连接相当简单:int sysfs_create_link(struct kobject *kobj,struct kobject *target,char *name);
3.删除属性
删除一个属性需通过函数sysfs_remove_file()
:void sysfs_remove_file(struct kobject *kobj,const struct attribute *attr);
由sysfs_creat_link()
创建的符号连接可通过函数sysfs_remove_link()
删除:void sysfs_remove_link(struct kobject *kobj,char *name);
4.sysfs约定
- 首先,sysfs属性应该保证每个文件只导出一个值,该值应该是文本形式而且映射为简单C类型。其目的是为了避免数据的过度结构化或太凌乱。
- 其次,在sysfs中要以一个清晰的层次组织数据。
- 最后,记住sysfs提供内核到用户空间的服务,这多少有些用户空间ABI(应用程序二进制接口)的作用。
17.4.3内核事件层
- 内核事件层实现了内核到用户的消息通知系统——就是建立在上文一直讨论的kobject基础之上。
- 每个事件都被赋予了一个动词或动作字符串表示信号。该字符串会以“被修改过”或“未挂载”等词语来描述事件。
- 最后,每个事件都会有一个可选的负载。
- 使用kobject和属性不但有利于很好的实现基于sysfs的事件,同时也有利于创建新kobjects对象和属性来表示新对象和数据——它们尚未出现在sysfs中。
Linux内核设计与实现第十周读书笔记的更多相关文章
- LINUX内核设计与实现第三周读书笔记
LINUX内核设计与实现第三周读书笔记 第一章 LINUX内核简介 1.1 Unix的历史 1969年的夏天,贝尔实验室的程序员们在一台PDR-7型机上实现了Unix这个全新的操作系统. 1973年, ...
- Linux内核设计与实现第五周读书笔记
第十八章 调试 18.1准备开始 需要的只是: 一个确定的bug.大部分bug通常都不是行为可靠而且定义明确的. 一个藏匿bug的内核版本. 相关的内核代码的知识和运气. 18.2内核中的bug 内核 ...
- Linux内核设计与实现第八周读书笔记
第四章 进程调度 进程在操作系统看来是程序的运行态表现形式. 4.1多任务 多任务操作系统就是能同时并发地交互执行多个进程的操作系统. 多任务操作系统会使多个进程处于堵塞或者睡眠状态.这些任务尽管位于 ...
- Linux内核设计与实现第六周读书笔记
第三章 进程管理 3.1 进程 进程是处于执行期的代码.通常进程还要包含其他资源,像打开的文件.挂起的信号.内核的内部数据.处理器状态.一个或多个具有内存映射的内存地址空间及一个或多个执行线程,当然还 ...
- linux内核设计与实现第七周读书笔记
第七章 链接 链接(linking)是将各种代码和数据部分收集起来并组合成为一个单一文件的过程,这个文件可被加载(或被拷贝)到存储并执行.链接可以执行于编译时(compile time),也就是在源代 ...
- 《Linux内核设计与实现》Chapter 18 读书笔记
<Linux内核设计与实现>Chapter 18 读书笔记 一.准备开始 一个bug 一个藏匿bug的内核版本 知道这个bug最早出现在哪个内核版本中. 相关内核代码的知识和运气 想要成功 ...
- 《Linux内核设计与实现》Chapter 3 读书笔记
<Linux内核设计与实现>Chapter 3 读书笔记 进程管理是所有操作系统的心脏所在. 一.进程 1.进程就是处于执行期的程序以及它所包含的资源的总称. 2.线程是在进程中活动的对象 ...
- 《Linux内核设计与实现》第四周读书笔记——第五章
<Linux内核设计与实现>第四周读书笔记--第五章 20135301张忻 估算学习时间:共1.5小时 读书:1.0 代码:0 作业:0 博客:0.5 实际学习时间:共2.0小时 读书:1 ...
- 《Linux内核设计与实现》Chapter 1 读书笔记
<Linux内核设计与实现>Chapter 1 读书笔记 一.Unix的特点 Unix从Multics中产生,是一个强大.健壮和稳定的操作系统. 特点 1.很简洁 2.在Unix系统中,所 ...
随机推荐
- 刨根问底KVO原理
介绍 KVO( NSKeyValueObserving )是一种监测对象属性值变化的观察者模式机制.其特点是无需事先修改被观察者代码,利用 runtime 实现运行中修改某一实例达到目的,保证了未侵入 ...
- 【坚持】Selenium+Python学习记录 DAY11
2018/06/1-2018/06/4 参考资料: [菜鸟教程](http://www.runoob.com/python3/python3-examples.html) [Python解惑:True ...
- K-SVD字典学习及其实现(Python)
算法思想 算法求解思路为交替迭代的进行稀疏编码和字典更新两个步骤. K-SVD在构建字典步骤中,K-SVD不仅仅将原子依次更新,对于原子对应的稀疏矩阵中行向量也依次进行了修正. 不像MOP,K-SVD ...
- 国密算法--Openssl 实现国密算法(基础介绍和产生秘钥对)
国密非对称加密算法 又称sm2,它是采取了ECC(曲线加密算法)中的一条固定的曲线,实际上就是ECC算法. 因为openssl里面不包含sm2算法,所以就要重新进行封装-. - 对于ECC算法我就不介 ...
- Linear Regression and Maximum Likelihood Estimation
Imagination is an outcome of what you learned. If you can imagine the world, that means you have lea ...
- [T-ARA/超新星][TTL (Time To Love)]
歌词来源:http://music.163.com/#/song?id=5403002 作曲 : 金道勋 [作曲 : 金道勋] 作词 : Rhymer/Joosuc/황성진 [作词 : Rhymer/ ...
- API验证
API验证说明 API验证: a. 发令牌: 静态 PS: 隐患 key被别人获取 b. 动态令牌 PS: (问题越严重)用户生成的每个令牌被黑客获取到,都会破解 c. 高级版本 PS: 黑客网速快, ...
- scrum立会报告+燃尽图(第二周第二次)
此作业要求参考: https://edu.cnblogs.com/campus/nenu/2018fall/homework/2247 一.小组介绍 组名:杨老师粉丝群 组长:乔静玉 组员:吴奕瑶.公 ...
- Scrum立会报告+燃尽图(十二月七日总第三十八次):功能测试
此作业要求参见:https://edu.cnblogs.com/campus/nenu/2018fall/homework/2284 项目地址:https://git.coding.net/zhang ...
- 树莓派 Raspberry-Pi 折腾系列:系统安装及一些必要的配置
入手树莓派将近一个月了,很折腾,许多资源不好找,也很乱.简单整理一下自己用到的东西,方便以后自己或别人继续折腾. 0. 操作系统下载 树莓派官方 Raspbian 系统下载:http://www.ra ...