《Linux内核设计与实现》CHAPTER17阅读梳理
《Linux内核设计与实现》CHAPTER17阅读梳理
【学习时间:3.5hours】
【学习内容:设备类型,模块,内核对象,sysfs】
个人思考部分见【】标出的部分
一、课堂讲解整理&思考
1.什么是模块?为什么需要模块?它和设备有什么关系?
- 首先,一个操作系统(这里以Linux系统为例)会关联很多设备,从大家比较熟悉的键盘、打印机,到硬盘、光盘,再到比较专业的一些电路板设备、机械设备以及其他设备;操作系统必须能够向这些设备发出有效指令以便于操控,同时还要根据各个设备能接受的指令差异有针对性地进行“命令”。
- 在内存中的/dev/路径下有一些sda0,sda1……等目录,表示的是硬盘上的扇区——对这些目录的读写就相当于直接对设备读写(当然这仅限于dev目录);这样比较简单地表示出来了一种“捆绑”关系;
- 仅仅有“捆绑”还不够,操作系统首先在Linux源代码目录下的documents/devices.txt中记录下来主设备号/次设备号 与各个设备的关联映射——相当于对设备起了统一的名字然后登记名字与设备的对应;
- 接下来就到了实际应用的时候了(这时候还有一些问题没有解决):当fopen函数要打开的是一个设备的时候,主/次设备号和type(也是一个变量值)就交给了系统,由系统唯一确定是哪个设备然后寻找对应的驱动程序,让驱动去和设备交流(驱动相当于翻译);
- 那么,驱动程序怎么和系统有关联的呢?驱动会事先在操作系统上注册,就是验明身份,这样操作系统就知道了——因为驱动都工作在内核状态下,所以这个“验明身份”的过程就是在Linux内核中添加新东西的过程——也就是这章中讲到的“模块”。
二、课本阅读
1.总述
- 设备类型:在所有Unix 系统中为了统一普通设备的操作所采用的分类.
- 模块: Linux 内核中用于按需加载和卸载目标码的机制.
- 内核对象:内核数据结构中支持面向对象的简单操作,还支持维护对象之间的父子关系。
- sysfs :表示系统中设备树的一个文件系统。
2.设备类型
在Linux 以及所有Unix 系统中,设备被分为以下三种类型
- 块设备
- 块设备通常缩写为blkdev,它是可寻址的,寻址以块为单位,块大小随设备不同而不同;块设备通常支持重定位( seeking )操作,也就是对数据的随机访问。
- 字符设备
- 字符设备通常缩写为cdev,它是不可寻址的,仅提供数据的流式访问,就是一个个字符,或者一个个字节。
- 网络设备
- 网络设备最常见的类型有时也以以太网设备(ethemet devices)来称呼,它提供了对网络(例如Internet)的访问。
3.模块
1.总述
Linux 内核是模块化组成的,它允许内核在运行时动态地向其中插入或从
中删除代码。这些代码(包括相关的子例程、数据、函数人口和函数出口〉被一并组合在一个单
独的二进制镜像中,即所谓的可装载内核模块中,或简称为模块。
2.通用过程
-
流程
- hello_ini的函数是模块的入口点,它通过module_ init()例程注册到系统中,在内核装载时被调用;
- 因为init 函数通常不会被外部函数直接调用,所以你不必导出该函数,故它可标记为static类型;
- hello_ exit()函数是模块的出口函数,它由module_exit()例程应册到系统.在模块从内存卸载时,内核便会调用hello_exit
- MODULE_AUTHOR()宏和MODULE_DESCRIPTION()宏指定了代码作者和模块的简要描述,它们完全是用作信息记录目的。
3.构建模块
-
模块放入内核源代码树中:
-
设备驱动程序存放在内核源码树根目录下/命ivers 的子目录下
-
要向企ivers/cbar/下的Makefile 文件中添加一行。编辑divers/cbar/协kefile/ 并加入:obj -m +• [子目录名]/这行编译指令告诉模块构建系统,在编译模块时需要进入某个子目录中
-
最后,在driv缸的bar/fishing/ 下,需要添加一个新Makefile 文件,其中需要有下面这行指令:
obj-m +=fishing .o
-
一切就绪了,此刻构建系统运行将会进入fishing/ 目录下,并且将fishing.c 编译为fishing.ko模块.虽然你写的扩展名是.o,但是模块被编译后的扩展名却是.ko
-
驱动程序源文件不止一个的话,你只要把你的Makefile 做如下修改就可搞定:
obj -$ (CONFIG_FISHING_POLB) +• fishing.o fishing-obj s : = fishing -main . o fishing - l ine. o
-
-
模块在内核内和在内核外构建的最大区别在于构建过程。当模块在内核源代码树外国时,你
必须告诉make 如何找到内核源代码文件和基础Makefile 文件。不过要完成这个工作同样不难:make -c / kernel/source/ l ocati on SUBDI RS =$PWD modules
在这个例子中, I kernel/source/location 是你配置的内核源代码树
4.安装模块
-
编译后的模块将被装入到目录/li~/modules/version/kemel/ 下, 在kernel/ 目录下的每一个目录都对应着内核源码树中的模块位置
-
下面的构建命令用来安装编译的模块到合适的目录下
make modules i nstall
5.依赖关系
多数Linux 发布版都能自动产生这些依赖关系信息,而且在每次启动时更新。若想产生内核依赖关系的信息, root 用户可运行命令
depmod
为了执行更快的更新操作,那么可以只为新模块生成依赖信息,而不是生成所有的依赖关
系,这时root 用户可运行命令
depmod -A
模块依赖关系信息存放在/lib/modules/version/modules.dep 文件中
6.载入模块
- 初级
-
以root 身份运行命令:这里, module.ko 是要载入的模块名称
insmod module .ko
-
- 高级
-
为了在内核via modprobe 中插入模块,需要以root 身份运行
modprobe module [ module parameters ]
-
其中,参数module 指定了需要载入的模块各称,后面的参数将在模块加载时传入内核。
-
modprobe 命令也可用来从内核中卸载模块,当然这也需要以root 身份运行:
modprobe -r modules
-
7.管理配置选项
-
说明:
- 配置选项第一行定义了该选项所代表的配置目标。注意CONFIG_ 前缀并不需要写上。
- 第二行声明选项类型为住istate,也就是说可以编译进内核( Y ),也可作为模块编译( M),或者干脆不编译它( N)。
- 如果编译选项代表的是一个系统功能,而不是一个模块,那么编译选项将用bool 指令代替往istate,这说明它不允许被编译成模块。处于指令之后的引号内文字为该选项指定了名称。
- 第三行指定了该选项的默认选择,这里默认操作是不编译它(N)。也可以把默认选择指定为编译进内核(Y),或者编译成一个模块(M)。对驱动程序而言,默认选择通常为不编译进内核(N)。
- Help 指令的目的是为该选项提供帮助文档。各种配置工具都可以按要求显示这些帮助。因为这些帮助是面向编译内核的用户和开发者的,所以帮助内容简洁扼要。
-
关于select与depends选项
- depends 指令规定了在该选项被设置前,首先要设置的选项。假如依赖性不满足,那么该选项就被禁止。
- Select 指令和depends 类似,它们只有一点不同之处一一只要是select 指定了谁,它就会强行将被指定的选项打开。
8.模块参数
-
总述:
- Linux 提供了这样一个简单框架一一它可允许驱动程序声明参数,从而用户可以在系统启动或者模块装载时再指定参数值,这些参数对于驱动程序属于全局变量。
-
定义一个模块参数可通过宏module_param完成:
module_param(name, t ype, perm);
- 参数name 既是用户可见的参数名,也是你模块中存放模块参数的变量名.参数type 则存放了参数的类型
- 宏其实并没有定义变量,你必须在使用该宏前进行变量定义
- 有可能模块的外部委数名称不同于它对应的内部变量名称,这时就该使用宏module_param_named()定义
9.导出符号表
- 原因:
- 导出的内核函数可以被模块调用,而来导出的函数模块则无法被调用。只有当被显式导出后的外部函数,才可以被动态库调用。在内核中,导出内核函数需要使用特殊的指令: EXPORT_ SYMBOL()和EXPORT_SYMBOL_GPL()。
- 导出的内核符号表被看做导出的内核接口,甚至称为内核API. 导出符号相当简单,在声明函数后,紧跟上EXPORT_SYMBOL()指令即可
4.设备模型
-
统一设备模型C device model)提供了一个独立的机制专门来表示设备,并描述其在系统中的拓扑结构。最初设计动机是:可以沿设备树的叶子向其根的方向依次遍历,以保证能以正确顺序关闭各设备的电源。
-
kobject
- 设备模型的核心部分就是kobject (kernel object),它自struct kobject 结构体表示,定义于头文件<linux/kobject.h>中。kobject 类似于C# 或Java 这些面向对象语富中的对象,提供了诸如引用计数、名称和父指针等字段,可以创建对象的层次结构
-
ktype
kobject 对象被关联到一种特殊的类型,ktype 由kobj_type 结构体表示,定义于头文件<linux/kobject.h>中。ktype 的存在是为了描述一族kobject 所具有的普遍特性struct kobj_type { void (*release) (struct kobject *); const struct sysfs_ops • sysfs_ops; struct attribute ••default_attrs; }
3.kset
kset 是kobject 对象的集合体。把它看成是一个容器,可将所有相关的kobject 对象放在一起
4. 以上三者的区别与联系
- kset 可把kobject 集中到一个集合中,而ktype 描述相关类型kobject 所共有的特性,它们之间的重要区别在于具有相同ktype 的kobject 可以被分组到不同的kset。就是说,在Linux 内核中,只有少数一些的ktype,却有多个kset.
- kobject 本身意义井不大,通常情况下它需要被嵌入到其他数据结构中,让那些包含
它的结构具有了kobject 的特性。
- kobject 叉归入了称作kset 的集合, kset 集合由struct kset 结构体表示。kset 提供了两个功能。第一,其中嵌入的kobj创作为kobject 组的基类。第二, kset 将相关的kobject 集合在一起。在sysfs 中,这些相关的koject 将以独立的目录出现在文件系统中。
-
5.sysfs
- 总述
- sys俗文件系统是一个处于内存中的虚拟文件系统,它为我们提供了kobject 对象层次结构的视圈。帮助用户能以一个简单文件系统的方式来观察系统中各种设备的拓扑结构.借助属性对象, kobject 可以用导出文件的方式,将内核变量提供给用户读取或写入〈可选〉。
- 特点
- sysfs 的诀窍是把kobject 对象与目录项(directory entries)紧密联系起来,这点是通过kobject 对象中的dentry 字段实现的
- Sysf单的根目录下包含了至少十个目录: block、bus、class、dev、devices、firmware、fs、kernel、module 和power。其中最重要的目录是devices , 该目录将设备模型导出到用户空间. 目录结构就是系统中实际的设备拓扑。其他目录中的很多数据都是将devices 目录下的数据加以转换加工而得到的
- sysfs中添加和删除kobject
-
想要把kot加ct 导入sysfs,你需要用到函数kobject_add():
int kobject_add (struct kobj ect •kobj , struct kobj ect •parent, const char •fmt , . .. ) ;
-
kobject 在sysfs 中的位置取决于kobject 在对象层次结构中的位置。如果kobject 的父指针被设置, 那么在sysfs 中kobject 将被映射为其父目录下的子目录: 如果parent 没有设置,那么kobject 将被映射为kset->kobj 中的子目录。
-