3.5 设计和实现的关系
接下来的部分将介绍一些内核设计和实现之间的关系。本部分最重要的内容是对于内核源
程序目录结构的概述,这一点随后就会提到。本章最后以实现中体系结构无关代码和体系
结构相关代码的相对大小的估算作为总结。
3.5.1 内核源程序目录结构
按照惯例,内核源程序代码安装在/usr/src/linux目录下。在该目录下还有几个其他目录
,每一个都代表一个特定的内核功能性子集(或者非常粗略地说是高层代码模块)。
1. Documentation
这个目录下面没有内核代码,只有一套有用的文档。但是这些文档的质量不一。有一部分
内核文档,例如文件系统,在该目录下有相当优秀而且相当完整的文档;而另外一部分内
核,例如进程调度,则根本就没有文档。但是在这里你可以不时地发现自己所最需要的东
西。
2. arch
arch目录下的所有子目录中都是体系结构相关的代码。每个体系结构特有的子目录下都又
至少包含三个子目录:kernel,存放支持体系结构特有的诸如信号处理和SMP之类特征的
实现;lib,存放高速的体系结构特有的诸如strlen和memcpy之类的通用函数的实现;mm
,存放体系结构特有的内存管理程序的实现。
除了这三个子目录以外,大多数体系结构在必要的情况下还都有一个boot子目录,该目录
中包含有在这种平台上启动内核所使用的部分或全部平台特有代码。这些启动代码中的部
分或全部也可以在平台特有的内核目录下找到。
最后,大部分体系结构所特有的目录还可以根据需要包含了供附加特性或改进组织使用的
其他子目录。例如,i386目录包含一个math-emu子目录,其中包括了在缺少数学协处理器
(FPU)的CPU上运行模拟FPU的代码。作为另外一个例子,m68k移植版本中为每一个该移
植版本所支持的基于680x0的机器建立了一个子目录,从而这些机器所特有的代码都有一
个自然的根目录。
下面几个是arch目录下的子目录:
* arch/alpha/—Linux内核到基于DEC Alpha CPU工作站的移植。
* arch/arm/—Linux到ARM系列CPU 的移植,该类CPU主要用于诸如Corel的NetWinder和
Acorn RiscPC之类的机器。
* arch/i386/—最接近于Linux内核原始平台或标准平台。这是为Intel的80386结构使用
的,当然包括对同一系列后来的CPU(80486,Pentium等等)的支持。它还包括了对AMD、
Cyrix和IDT等公司的一些兼容产品的支持。
本书基本上将这种体系结构称为“x86”。即使这样,严格说来“x86”对于我们的目标来
说还是要求得过于宽泛。早期的Intel CPU,例如80286,并没有包括Linux运行所需的所
有特性。对于这些机器,Linux也没有正式的支持版本(顺便提一下,Linux对这种CPU的
独立移植版本是存在的,不过它在功能上有部分损失)。当本书中提到“x86平台”时,
通常是指80386或更新的CPU。
* arch/m68k/—到Motorola的680x0 CPU系列的移植。该版本可以提供对基于从68020(只
要它同内存管理单元(MMU)68851一起使用)到68060的一切机器的支持。很多公司在他
们的产品中使用680x0系列芯片,例如Commodore(现在是Gateway)的Amiga、Apple的
Macintosh、Atari ST等等。这些老机器中的很多现在正充当可靠的Linux工作站。另外,
到NeXT工作站和SUN 3工作站的移植也正在进行中。
* arch/mips/—到MIPS的CPU系列的移植。虽然有其他几个厂商也使用MIPS开发了一些系
统,但是基于这种CPU的最出名的机器是Silicon Graphics(SGI)工作站。
* arch/ppc/—到Motorola/IBM的PowerPC系列CPU的移植。这包括对基于PowerPC的
Macintosh和Amiga以及BeBox、IBM的RS/6000等其他一些机器的支持。
* arch/sparc/—到32位SPARC CPU的移植。这包括对从Sun SPARC 1到SPARC 20的全部支
持。
* arch/sparc64/—到基于64位SPARC CPU(UltraSPARC系列)系统的移植。这里所能够支
持的机器包括Sun的Ultra 1、Ultra 2和更高配置的机器,直到Sun的最新产品
Enterprise 10000。注意32位和64位的SPARC的移植版本正在合并中。
不幸的是,本书必须将注意力集中在x86上,因此只应用到了arch/i386/目录下的代码,
而其他体系结构所特有的代码将不再涉及了。
3. drivers
这个目录是内核中非常大的一块。实际上,drivers目录下包含的代码占整个内核发行版
本代码的一半以上。它包括显卡、网卡、SCSI适配器、软盘驱动器、PCI设备和其他任何
你可以说出的Linux支持的外围设备的软件驱动程序。
drivers目录下的一些子目录是平台特有的,例如,zorro子目录中包含有和Zorro总线通
讯的代码。而Zorro总线只在Amiga中使用过,因此这些代码必然是Amiga特有的。而其他
一些子目录,例如pci子目录,则至少是部分平台无关的。
4. fs
Linux支持的所有文件系统在fs目录下面都有一个对应的子目录。一个文件系统是存储设
备和需要访问存储设备的进程之间的媒介。
文件系统可能是本地的物理上可访问的存储设备,例如硬盘或CD-ROM驱动器;在这两种情
况下将分别使用ext2和isofs文件系统。文件系统也可能是可以通过网络访问的存储设备
;这种情况下使用的文件系统是NFS。
还有一些伪文件系统,例如proc文件系统,可以以伪文件的形式提供其他信息(例如,在
proc的情况下是提供内核的内部变量和数据结构)。虽然在底层并没有实际的存储设备与
这些文件系统相对应,但是进程可以像有实际存储设备一样处理(NFS也可以作为伪文件
系统来使用)。
5. include
这个目录包含了Linux源程序树中大部分的包含(.h)文件。这些文件按照下面的子目录
进行分组:
* include/asm-*/—这样的子目录有多个,每一个都对应着一个arch的子目录,例如
include/asm-alpha、include/asm-arm、include/asm-i386等等。每个目录下的文件中包
含了支持给定体系结构所必须的预处理器宏和短小的内联函数。这些内联函数很多都是全
部或部分地使用汇编语言实现的,而且在C或者汇编代码中都会应用到这些文件。
当编译内核时,系统将建立一个从include/asm到目标体系结构特有的目录的符号链接。
结果是体系结构无关内核源程序代码可以使用如下形式的代码来实现所需功能:
#include
这样就能够将适当地体系结构特有的文件包含(#include)进来。
* include/linux/—内核和用户应用程序请求特定内核服务时所使用的常量和数据结构在
头文件中定义,而该目录中就包含了这些头文件。这些文件大都是平*立的。这个目录
被全部复制(更多的情况是链接)到/usr/include/linux下。这样用户应用程序就可以使
用#include包含这些头文件,而且能够保证所包含进来的头文件的内容和内核中的定义一
致。第9章将会给出有关的一个样例。
* 对这些文件的移植只有对于内核来说才是必须的,对用户应用程序则没有必要。移植工
作可以按照如下的方式封装处理:
* include/net/—这个目录供与网络子系统有关的头文件使用。
* include/scsi/—这个目录供与SCSI控制器和SCSI设备有关的头文件使用。
* include/video/—这个目录供与显卡和帧显示缓存有关的头文件使用。
6. init
这个目录下面的两个文件中比较重要的一个是main.c,它包含了大部分协调内核初始化的
代码。第4章将详细介绍这部分代码。
7. ipc
这个目录下的文件实现了System V的进程间通讯(IPC)。在第9章中将会对它们进行详细
介绍。
8. kernel
这个目录中包含了Linux中最重要的部分:实现平*立的基本功能。这部分内容包括进
程调度(kernel/sched.c)及创建和撤销进程的代码(kernel/fork.c和kernel/exit.c)
;以上所有内容将在第7章中有所涉及。但是我并不想给你留下这样的印象:需要了解的
内容都在这个目录下。实际上在其他目录下也有很多重要的内容。但是,不管怎样说,最
重要部分的代码是在这个目录下的。
9. lib
lib目录包含两部分的内容。lib/inflate.c中的函数能够在系统启动时展开经过压缩的内
核(请参见第4章)。lib目录下剩余的其他文件实现一个标准C库的有用子集。这些实现
的焦点集中在字符串和内存操作的函数(strlen,memcpy和其他类似的函数)及有关
sprintf和atoi的系列函数上。
这些文件都是使用C语言编写的,因此在新的内核移植版本中可以立即使用这些文件。正
如本章前面部分说明的那样,一些移植提供了它们独有的高速的函数版本,这些函数通常
是经过手工调整过的汇编程序,在移植后的系统使用这些函数来代替原来的通用函数。
10. mm
该目录包含了体系结构无关的内存管理代码。正如我们前面说明的那样,为每个平台实现
最底层的原语的体系结构特有的内存管理程序是存储在arch/platform/mm中的。大部分平
*立和x86特有的内存管理代码将在第8章中介绍。
11. net
这个目录包含了Linux应用的网络协议代码,例如AppleTalk、TCP/IP、IPX等等。
12. scripts
该目录下没有内核代码,它包含了用来配置内核的脚本。当运行make menuconfig或者
make xconfig之类的命令配置内核时,用户就是和位于这个目录下的脚本进行交互的。
3.5.2 体系结构相关和体系结构无关的代码
现在我们来估计一下体系结构相关和体系结构无关代码的相对大小。我们首先给出一些数
字。完整的2.2.5的内核总共有1 725 645行代码(顺便一提,请注意本书只包含了39 000
行代码,但是我们仍然努力涵盖了相当部分的核心函数)。其中一共有392 844行代码在
体系结构特有的目录之内,也就是arch/*和include/asm-*下面。我估计还有超过64 000
行的代码是仅供一种体系结构专用的驱动程序。这意味着大约26%的代码是专用于体系结
构相关代码的。
但是,对于单一一种体系结构,体系结构相关代码比例相对较小。不妨理想一点,如果某
种单一体系结构所需要的特有代码约有50 000行,而体系结构无关代码则大约有1 250
000行,那么体系结构相关代码大概只占到4%。当然,在特定的一个内核中,并不是所有
这些体系结构无关代码都会被用到,因此体系结构相关代码在特定内核中所占的比重与内
核的配置有关。但是不管怎样,很显然大部分内核代码是平*立的。