1. 设备类型
linux中主要由3种类型的设备,分别是:
设备类型 |
代表设备 |
特点 |
访问方式 |
块设备 |
硬盘,光盘 |
随机访问设备中的内容 |
一般都是把设备挂载为文件系统后再访问 |
字符设备 |
键盘,打印机 |
只能顺序访问(一个一个字符或者一个一个字节) |
一般不挂载,直接和设备交互 |
网络设备 |
网卡 |
打破了Unix "所有东西都是文件" 的设计原则 |
通过套接字API来访问 |
除了以上3种典型的设备之外,还有"伪设备",即一些虚拟的设备,仅提供访问内核功能而已,没有物理设备与之关联。
2. 内核模块
Linux内核是模块化组成的,内核中的模块可以按需加载,从而保证内核启动时不用加载所有的模块,即减少了内核的大小,也提高了效率。
通过编写内核模块来给内核增加功能或者接口是个很好的方式,既不用重新编译内核,也方便调试和删除。
2.1 内核模块示例
2.1.1. 无参数的内核模块
2.1.2. 带参数的内核模块
构造带参数的内核模块其实也不难,内核中已经提供了简单的框架来给我们声明参数。
1. module_param(name, type, perm) : 定义一个模块参数
+ 参数 name :: 既是用户可见的参数名,也是模块中存放模块参数的变量名
+ 参数 type :: 参数的类型(byte, short, int, uint, long, ulong, charp, bool...) byte型存放在char变量中,bool型存放在int变量中
+ 参数 perm :: 指定模块在 sysfs 文件系统中对应的文件权限(关于 sysfs 的内容后面介绍)
2. module_param_named(name, variable, type, perm) : 定义一个模块参数,并且参数对内对外的名称不一样
+ 参数 name :: 用户可见的参数名
+ 参数 variable :: 模块中存放模块参数的变量名
+ 参数 type和perm :: 同 module_param 中的 type 和 perm
3. module_param_string(name, string, len, perm) : 拷贝字符串到指定的字符数组
+ 参数 name :: 用户可见的参数名
+ 参数 string :: 模块中存放模块参数的变量名
+ 参数 len :: string 参数的缓冲区长度
+ 参数 perm :: 同 module_param 中的 perm
4. module_param_array(name, type, nump, perm) : 定义数组类型的模块参数
+ 参数 name :: 同 module_param 中的 name
+ 参数 type :: 同 module_param 中的 type
+ 参数 nump :: 整型指针,存放数组的长度
+ 参数 perm :: 同 module_param 中的 perm
5. module_param_array_named(name, array, type, nump, perm) : 定义数组类型的模块参数,并且数组参数对内对外的名称不一样
+ 参数 name :: 数组参数对外的名称
+ 参数 array :: 数组参数对内的名称
+ 参数 type,nump,perm :: 同 module_param_array 中的 type,nump,perm
6. 参数描述宏
可以通过 MODULE_PARM_DESC() 来给内核模块的参数添加一些描述信息。
这些描述信息在编译完内核模块后,可以通过 modinfo 命令查看。
2.2 内核模块的位置
2.2.1. 内核代码外
2.2.2. 内核代码中
内核模块的代码也可以直接放到内核代码树中。
编写驱动的时候就可以将完成此驱动功能的内核模块加到内核代码树中 driver 的相应位置。
之后,在编译内核的时候会将新的驱动以内核模块的方式编译出来。
2.3 内核模块相关操作
2.3.1. 模块安装
make modules_install <-- 把随内核编译出来的模块安装到合适的目录中
2.3.2. 模块依赖性
linux中自动生产模块依赖性的命令:
depmod <-- 产生内核依赖关系信息
depmod -A <-- 只为新模块生成依赖信息(速度更快)
2.3.3. 模块的载入
内核模块实验时已经用过:
insmod module.ko
<-- 推荐使用以下的命令, 自动加载依赖的模块
modprobe module [module parameters]
2.3.4. 模块的卸载
内核模块实验时已经用过:
rmmod module.ko
<-- 推荐使用以下的命令, 自动卸载依赖的模块
modprobe -r module
2.3.5. 模块导出符号表
内核模块被载入后,就动态的加载到内核中,为了能让其他内核模块使用其功能,需要将其中函数导出。
内核模块中导出函数的方法:
EXPORT_SYMBOL(函数名) <-- 接在要导出的函数后面即可
EXPORT_SYMBOL_GPL(函数名) <-- 和EXPORT_SYMBOL一样,区别在于只对标记为GPL协议的模块可见
3. 内核对象
2.6内核中增加了一个引人注目的新特性--统一设备模型(device model)。
统一设备模型的最初动机是为了实现智能的电源管理,linux 内核为了实现智能电源管理,需要建立表示系统中所有设备拓扑关系的树结构,
这样在关闭电源时,可以从树的节点开始关闭。
实现了统一设备模型之后,还给内核带来了如下的好处:
1. 代码重复最小化(统一处理的东西多了)
2. 可以列举系统中所有设备,观察它们的状态,并查看它们连接的总线
3. 可以将系统中的全部设备以树的形式完整,有效的展示出来--包括所有总线和内部连接
4. 可以将设备和其对应的驱动联系起来,反之亦然
5. 可以将设备按照类型加以归类,无需理解物理设备的拓扑结构
6. 可以沿设备树的叶子向其根的反向依次遍历,以保证能以正确的顺序关闭设备电源
3.1 kobject 简介
统一设备模型的核心部分就是 kobject,通过下面对kobject结构体的介绍,可以大致了解它是如何使得各个物理设备能够以树结构的形式组织起来的。
3.1.1. kobject
kobject 本身不代表什么实际的内容,一般都是嵌在其他数据结构中来发挥作用。嵌入了kobject之后,cdev设备之间就有了树结构关系,cdev设备和其他设备之间也有可层次关系。
3.1.2. ktype
ktype是为了描述一族的kobject所具有的普遍属性,也就是将这一族的kobject的属性统一定义一下,避免每个kobject分别定义。
3.1.3. kset
kset是kobject对象的集合体,可以所有相关的kobject置于一个kset之中,比如所有“块设备”可以放在一个表示块设备的kset中。
3.1.4. kobject,ktype和kset之间的关系
这3个概念中,kobject是最基本的。kset和ktype是为了将kobject进行分类,以便将共通的处理集中处理,从而减少代码量,也增加维护性。
这里kset和ktype都是为了将kobject进行分类,为什么会有2中分类呢?
从整个内核的代码来看,其实kset的数量是多于ktype的数量的,同一种ktype的kobject可以位于不同的kset中。
做个不是很恰当的比喻,如果把kobject比作一个人的话,kset相当于一个一个国家,ktype则相当于人种(比如黄种人,白种人等等)。
人种的类型只有少数几个,但是国家确有很多,人种的目的是描述一群人的共通属性,而国家的目地则是为了管理一群人。
同样,ktype侧重于描述,kset侧重于管理。
3.1.5. kref
kref记录kobject被引用的次数,当引用计数降到0的时候,则执行release函数释放相关资源。
4. sysfs
sysfs是一个处于内存中的虚拟文件系统,它提供了kobject对象层次结构的视图。
可以用下面这个命令来查看 /sys 的结构
tree /sys # 显示所有目录和文件
或者
tree -L 1 /sys # 只显示一层目录