第十七章 设备与模块
关于设备驱动和设备管理,四种内核成分
- 设备类型:在所有unix系统中为了统一普通设备的操作所采用的分类
- 模块:Linux内核中用于按需加载和卸载目标代码的机制
- 内核对象:内核数据结构中支持面向对象的简单操作,还支持维护对象之间的父子关系
- sysfs:系统中设备树的一个文件系统
17.1 设备类型
块设备
可寻址的,寻址以块为单位,块的大小随设备的不同而不同。块设备通常支持重定位操作,也就是对数据的随机访问
字符设备
不可寻址,仅提供数据的流式访问,就是一个个字符或字节。与块设备不同,应用程序是通过直接访问节点与字符设备交互
网络设备
又是也以以太网设备来称呼,提供了对网络的访问,是通过一个网络适配器和一种特定的协议进行的。他不是通过设备节点来访问,而好似通过套接字API这样的特殊接口来访问
杂项设备
实际上是一个简化的字符设备,对通用基本架构的一种折中
并不是所有的设备驱动都表示物理设备,有些设备是虚拟的仅提供内核访问功能
17.2 模块
Linux内核是模块化组成的,他允许内核在运行时动态的向其中插入或删除代码
可装载内核模块(模块):修改的代码被一并组合在一个单独的二进制镜像中
好处
基本内核镜像可以尽可能的小,因为可选的功能和驱动程序可以利用模块形式再提供
模块允许我们方便的删除和重新载入代码也方便了调试工作
17.2.1 hello,world
17.2.2 构建模块
构建模块的第一步是决定在哪管理模块源代码
1、在内核源码树中
内核模块正式成为Linux的一部分
下一步清楚模块应该在内核源码树中的位置,如设备驱动程序应存放在内核源码树根目录下的/drivers下,对于仅仅有一两个源文件的设备驱动程序可以直接存放在对应的目录下
添加模块后需要在对应目录下的Makefile文件里添加命令:
obj-m += ****/
模块被编译后的扩展名是.ko
2、放在内核代码外
在源码树中目录中建立一个Makefile文件
Obj-m:=****.o
编译:
****-objs :=****-mian.o ****-line.o
模块在内核内核内核外构建的最大区别在于构建过程
17.2.3 安装模块
编译后的模块将被安装在目录lib/module/version/kernel下,载kernel目录下的每一个目录都对应着内核源码树中的模块位置
安装编译的模块懂啊合适的目录下
make module_install //以root权限运行
17.2.4 产生模块依赖性
载入一个模块时,另一个相关的模块自动被载入。这里需要的依赖信息必须事先生成,使用命令depmod
依赖关系信息存放在/lib/module/version/module.dep文件中
17.2.5 载入模块
请求内核载入指定的模块
insmod ****.ko
卸载
rmmod ****
17.2.6 管理配置选项
新的配置选项如何加入:向kconfig文件中加入一项,用于对应内核源码树
。如果建立了一个新的子目录而且希望kconfig文件存在于该目录下,那么必须在一个已经存在的kconfig文件中将他引入
指令:source "drivers/char/****/kconfig"
其他指令
depend:规定了该选项在被设置前要设置的选项
select:与depend类似,但select会强行将被指定的选项打开
同时指定多个选项通过&&进行
用 !来禁止某个选项
tristste和bool:通常结合if指令使用
表示某个选项取决于另一个配置选项
17.2.7 模块参数
Linux允许驱动程序声明参数,从而用户可以在系统启动或模块装载时在再指定参数值,这些参数对于驱动程序属全局变量
定义一个模块参数可以通过宏module_param()
module_param(name,type,perm)
name:是用户可见的参数名
type:存放了参数的类型
perm:指定了模块在sysfs文件系统下对应文件的权限
17.2.8 导出符号表
在内核中,导出内核函数需要使用特殊的指令:
EXPORT_SYMBOL()和EXPORT_SYMBOL_GPL()
导出的内核函数可以被调用,而未导出的函数模块则不能被调用
17.3 设备模型
设备模型通过提供了一个独立的机制专门表示设备,并描述其在系统中的拓扑结构,具有以下优点:
· 代码重复最小化
· 提供诸如引用计数这样的同一机制
· 例举系统中所以的设备,观察他们的状态并查看他们的链接总线
· 将系统中的全部设备以树的形式完整有效的展示出来包括总线和内部连接
· 将设备和其对应的驱动联系起来,反之亦然
· 将设备按照类型加以归类
· 沿设备树的叶子向其根的方向遍历能保证以正确的顺序关闭个设备的电源
17.3.1 kobject(核心)
当kobject被嵌入到其他结构体中时,该结构体便拥有了kobject提供的标准,更重要的是嵌入kobject的结构体可以成为对象层次架构中的一部分
17.3.2 ktype
17.3.3 kset
kset可把kobject集中到一个集合中,而ktype描述相关类型kobject所共有的特性,他们的区别在于具有相同的ktype的kobject可以被分组到不同的kset
17.3.4 kobject、ktype和kset的相互关系
最重要的是kobject,他引入了基本对象道具,并且是以一个统一的方式提供这些功能,通常要被嵌入到其他数据结构中,是包含他的结构具有kobject特性
kobject与ktype关联,ktype定义了一些kobject相关的默认特性
kobject又被归入了kset,kset提供了两个功能,第一是嵌入的kobject作为kobject组的基类,第二kset讲kobject集合在一起
17.3.5 管理和操作kobject
使用kobject第一步:声明和初始化
void kobject_init(struct kobject *kobj,struct kobj_type *ktype)
第一个参数:需要初始化的kobject对象
第二步:在初始化清零后,就可以安全的初始化parent和kset字段
多不操作也可以由kobject_create()完成,他返回一个新分配的kobject
17.3.6 引用计数
初始化后kobject的引用计数设置为1,只要计数器不为0该对象就可以继续保留正在内存中
1、递增和递减引用计数
增加一个引用计数:koject_get()
struct kobject * kobject_get(struct kobject *kobj)
减少一个引用指针:koject_put()
void kobject_pu(struct kobject *kobj)
2、kref
kobject是通过kref实现的
struct kref{
atomic_t refcount; //存放引用计数的原子变量
}
17.4 sysfs
sysfs文件系统是一个处于内存中的虚拟文件系统,提供了kobject对象层次结构的视图
sysfs的诀窍是把kobject对象与目录项紧密的联系在一起
sysfs目录下的各种目录:
block:每个子目录都对应着系统中的一个已注册的块设备
bus:提供了一个系统总线视图
class:包含了以高层功能逻辑组织起来的系统设备视图
dev:已注册节点的视图
devices:系统中设备拓扑结构视图反映内核中设备结构体的组织层次
firmware:包含底层子系统的特殊树
fs:已注册文件系统视图
kernel:内核配置项和状态信息
module:系统已加载模块的信息
power:系统范围的电源管理数据
17.4.1 sysfs中添加和删除kobject
把kobject导入sysfs,使用函数:kobject_add()
int kobject_add(struct kobject *kobj,struct kobject *parent,count char *fmt,...)
把kobject从sysf中删除s,使用函数:kobject_del()
void kobject_add(struct kobject *kobj)
17.4.2 向sysfs中添加文件
1. 默认属性
所有具有相同类型的kobject 在它们对应的sysfs 目录下都拥有相同的默认文件集合。
2. 创建新属性
在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()创建的符号连接可通过删除:
void sysfs_remove_link(struct kobject kobj , char name);
4.sysfs约定
- 该保证每个文件只导出一个值。
- 以一个清晰的层次组织数据。
- 供内核到用户空闹的服务。
17.4.3 内核事件层
- 实现了内核到用户的消息通知系统。
- 内核事件由内核空间传递到用户空间需要经过netlink。
- 用户空间实现一个系统后台服务用于监听套接字,处理任何读到的信息,并将事件传送到系统栈里。