第17章 设备与模块
一、设备类型
1. Linux及Unix系统:
块设备
字符设备
网络设备
2.块设备:
通常缩写为blkdev,它是可寻址的,寻址以块为单位,块大小随设备不同而不同;块设备通常支持重定位操作,也就是对数据的随机访问。
块设备是通过称为“块设备节点”的特殊文件来访问的,井且通常被挂载为文件系统。
3.字符设备:
通常缩写为cdev,它是不可寻址的,仅提供数据的流式访问,就是一个个字符,或者一个个字节。
字符设备的例子有键盘、鼠标、打印机,还有大部分伪设备。字符设备是通过称为“字符设备节点”的特殊文件来访问的。与块设备不同,应用程序通过直接访问设备节点与字符设备交互。
4.网络设备:
最常见的类型有时也以以太网设备称呼,它提供了对网络〈例如Internet)的访问,这是通过-个物理适配器和一种特定的协议进行的
网络设备打破了Unix的“所有东西都是文件”的设计原则,它不是通过设备节点来访问,而是通过套接字API这样的特殊接口来访问。
5.伪设备:
大部分设备驱动是表示物理设备的,但并不是所有设备驱动都表示物理设备。有些设备驱动是虚拟的,仅提供访问内核功能而已。我们称为“伪设备”。
常见的如:
内核随机数发生器(通过/dev/random和/dev/urandom 访问)
空设备(通过/dev/null 访问)
零设备(通过/dev/zero 访问)
满设备(通过/dev/full 访问)
内存设备(通过/dev/mem 访问)
二、模块
1. 概述:
Linux 内核是模块化组成的,它允许内核在运行时动态地向其中插入或从中删除代码。这些代码(包括相关的子例程、数据、函数人口和函数出口)被一并组合在一个单独的二进制镜像中,即所谓的可装载内核模块中,或简称为模块。
支持模块的好处是基本内核镜像可以尽可能地小,因为可选的功能和驱动程序可以利用模块形式再提供。模块允许我们方便地删除和重新载入内核代码,也方便了调试工作。而且当热插拔新设备时,可通过命令载入新的驱动程序。
模块的所有初始化函数必须符合形式:int my _ init (void);
退出函数必须符合形式:void my_exit (void);
2.构建与安装
安装编译的模块: make modules_install
产生依赖关系信息,而且在每次启动时更新。
产生内核依赖关系的信息,root用户可运行命令:depmod
只为新模块生成依赖信息,不生成所有的依赖关系,root用户可运行命令:depmod -A
模块依赖关系信息存放在/lib/modules/version/modules.dep文件中。
载入,root用户可运行命令:insmod module .ko
在内核via modprobe中插入模块,root用户可运行命令:zmodprobe module[module parameters] (参数 module 指定了需要载入的模块各称)。
从内核中卸载模块,root用户可运行命令:modprobe -r modules
三、设备模型
-
1.kobject:
设备模型的核心部分。其中struct kobject 结构体表示,定义于头文件<linux/k,ρ1lij四t.b>中。通常是嵌入其他结构中的。 -
2.ktype:
kobject 对象被关联到一种特殊的类型:ktype。由kobj_type 结构体表示,定义于头文件<linux/kobject.h>中。为了描述一族kobject 所具有的普遍特性。 -
3.kset:
在Linux 内核中,只有少数一些的ktype,却有多个kset。由kset 结构体表示,定义于头文件<linux/kobject.h>中: -
4.kobject、ktype、kset关系:
kobject,让那些包含它的结构具有了kobject 的特性。ktype 定义了一些kobject 相关的默认特性。kset 提供了两个功能:第一,其中嵌入的kobj创作为kobject 组的基类。第二, kset 将相关的kobject 集合在一起。 -
5.管理和操作kobject:
kobject 通过函数koject_init 进行初始化,该函数定义在文件<linux/kobject.h>中:void kobject_init(struct kobject kobj, struct kobj_type ktype);第一个参数就是需要初始化的kobject 对象;调用初始化函数前, kobject 必须清空;未被清空,调用memset() 即可:memset(kobj, 0, sizeof (*kobj ) );应该调用kobject_createO 创建koject。 -
6.计数:
kobject的主要功能:提供了一个统一的引用计数系统。初始化后,kobject的引用计数设置为1;引用计数不为零,该对象就会继续保留在内存中。引用计数跌到零时,对象可被撤销,相关内存也被释放。koject 的引用计数是通过kref结构体实现,该结构体定义在头文件<linux/kref.h>中。
第19章 可移植性
一、可移植操作系统
有些操作系统在设计时把可移植性作为头等大事之一,尽可能减少涉及与机器相关的代码。汇编代码少之又少,这样做就是利用代码性能优化能力换取代码的可移植性。Minix,NetBSD和许多研究用的系统就是这种高度可移植的操作系统。
还有一些操作系统完全不顾及可移植性,尽最大可能追求代码的性能表现,尽可能多的使用汇编代码,这样就是利用代码的可移植性换取代码的性能优化能力。这样的系统比可移植的系统更难维护。DOS和Windows95就是这样的操作系统。
二、字长和数据类型
ANSIC标准规定,一个char的长度一定是1字节;尽管没有规定int类型的长度是32位,但在Linux当前支持的体系结构中,它都是32位的。short类型也类似,在当前所有支持的体系结构中,虽然没有明文规定,但是它都是16位的。绝对不应该假定指针和long的长度,在linux当前支持的体系结构中,它们可以在32位和64位中变化。对于不同的体系结构long的长度不同,决不应该假设sizeof(int)=sizeof(long)。类似的,也不要假设指针和int长度相等。
三、本章感受
要想写出移植性好、简洁、合适的内核代码,要注意以下两点:
-
1.编码尽量选取最大公因子:假定任何事情都可能发生,任何潜在的约束都可能存在。编码尽量选取最小公约数:不要假定给定的内核特性是可用的,仅仅需要最小的体系结构功能。
-
2.编写可移植性的代码需要考虑很多问题:字长、数据类型、填充、对齐、字节次序、符号、字节顺序、页大小以及处理器的加载/存储排序等。对于绝大多数的内核开发者来说,可能主要考虑的问题就是保证正确使用数据类型。
第20章 补丁,开发和社区
本章重点介绍了Linux编码风格:
- 缩进——缩进风格是用制表位每次缩进8个字符长度。
- switch语句——switch语句下属的case标记应该缩进到和switch声明对齐,这样有助于减少8个字符的tab键带来的排版缩进。
- 空格——Linux编码风格规定,空格放在关键字周围,函数名和圆括号之间无空格。
- 花括号——内核选定的风格是左括号紧跟在语句的最后,与语句在相同的一行。而右括号要新起一行,作为该行的第一个字符。
- 每行代码的长度——源代码中要尽可能地保证每行代码长度不超过80个字符,因为这样做可能使代码最合适在标准的80*24的终端上显示。
- 命名规范——命名中不允许使用骆驼拼写法、Studly Caps或者其他混合的大小写字符。
- 函数——根据经验,函数的代码长度不应该超过两屏,局部变量不应超过10个。一个函数应该功能单一而且实现精确。
- 注释——一般情况下应该描述的是你的代码要做什么和为什么要这样做,而不是具体通过什么方式实现的。
- typedef——使用typedef要谨慎,只有在确实需要的时候再使用它。
- 多用现成的东西——请勿闭门造车。内核本身就提供了字符串操作函数,压缩函数和一个链表接口,所以请使用他们。
- 在源码中减少使用ifdef——不赞成在源码中使用ifdef预处理指令。
- 结构初始化——结构初始化的时候必须在他的成员前加上结构标识符。
- 代码的事后修正——indent是一个在大多数Linux系统中都能找到的好工具,它可以按照指定的方式对源代码进行格式化 。