Linux-0.11

体系结构

Linux 内核主要由 5 个模块构成,它们分别是:进程调度模块、内存管理模块、文件系统模块、进程间通信模块和网络接口模块。
notion image
虚线和虚框部分 表示Linux 0.12中还未实现的部分
由图可以看出,所有的模块都与进程调度模块存在依赖关系。因为它们都需要依靠进程调度程序来 挂起(暂停)或重新运行它们的进程。通常,一个模块会在等待硬件操作期间被挂起,而在操作完成后 才可继续运行。例如,当一个进程试图将一数据块写到软盘上去时,软盘驱动程序就可能在启动软盘旋 转期间将该进程置为挂起等待状态,而在软盘进入到正常转速后再使得该进程能继续运行。另外 3 个模 块也是由于类似的原因而与进程调度模块存在依赖关系。
其他几个模块的依赖关系有些不太明显,但同样也很重要。进程调度子系统需要使用内存管理来调 整一特定进程所使用的物理内存空间。进程间通信子系统则需要依靠内存管理器来支持共享内存通信机制。这种通信机制允许两个进程访问内存的同一个区域以进行进程间信息的交换。虚拟文系统也会使用网络接口来支持网络文件系统(NFS),同样也能使用内存管理子系统提供内存虚拟盘(ramdisk)设备。而内存管理子系统也会使用文件系统来支持内存数据块的交换操作。
notion image
其中内核级中的几个方框,除了硬件控制方框以外,其他粗线方框分别对应内核源代码的目录组织结构。
除了这些图中已经给出的依赖关系以外,所有这些模块还会依赖于内核中的通用资源。这些资源包 括内核所有子系统都会调用的内存分配和收回函数、打印警告或出错信息函数以及一些系统调试函数。

对内存的管理和使用

物理内存

在 Linux 0.12 内核中,为了有效地使用机器中的物理内存,在系统初始化阶段内存被划分成几个功能区域
 
notion image
其中,Linux 内核程序占据在物理内存的开始部分,接下来是供硬盘或软盘等块设备使用的高速缓 冲区部分(其中要扣除显示卡内存和 ROM BIOS 所占用的内存地址范围 640K--1MB)。当一个进程需要 读取块设备中的数据时,系统会首先把数据读到高速缓冲区中;当有数据需要写到块设备上去时,系统 也是先将数据放到高速缓冲区中,然后由块设备驱动程序写到相应的设备上。内存的最后部分是供所有 程序可以随时申请和使用的主内存区。
Linux 系统同时采用了内存分段和分页管理机制。
虚拟地址(Virtual Address)是指由程序产生的由段选择符和段内偏移地址两个部分组成的地址。
逻辑地址(Logical Address)是指由程序产生的与段相关的偏移地址部分。 应用程序员仅需与逻辑地址打交道。不过有些资料并不区分逻辑 地址和虚拟地址的概念,而是将它们统称为逻辑地址。
线性地址(Linear Address)是虚拟地址到物理地址变换之间的中间层,是处理器可寻址的内存空间 (称为线性地址空间)中的地址。程序代码会产生逻辑地址,或者说是段中的偏移地址,加上相应段的 基地址就生成了一个线性地址。如果启用了分页机制,那么线性地址可以再经变换以产生一个物理地址。 若没有启用分页机制,那么线性地址直接就是物理地址。
物理地址(Physical Address)是指出现在 CPU 外部地址总线上的寻址物理内存的地址信号,是地址变换的最终结果地址。

引导启动程序

主要流程

 
notion image
  1. 当 PC 的电源打开后,80x86 结构的 CPU 将自动进入实模式,并从地址 0xFFFF0 开始自动执行程序代码,这个地址通常是 ROM-BIOS 中的 地址。PC 机的 BIOS 将执行系统的某些硬件检测和诊断功能,并在物理地址 0 处开始设置和初始化中断向量。此后,它将可启动设备的第一个扇区(磁盘引导扇区,512 字节)读入内存绝对地址 0x7C00 处, 并跳转到这个地方开始引导启动机器运行。
  1. Linux 的最前面部分是用 8086 汇编语言编写的(boot/bootsect.S),并保存在引导设备的第一个扇区 中。它将由 BIOS 读入到内存绝对地址 0x7C00(31KB)处。当它被执行时就会把自己移动到内存绝对地址 0x90000(576KB)处,并把启动设备盘中后 2KB 字节代码(boot/setup.S)读入到内存 0x90200 处。 而内核的其他部分(system 模块)则被读入到从内存地址 0x10000(64KB)开始处。
  1. 启动部分识别主机的某些特性以及 VGA 卡的类型。如果需要,它会要求用户为控制台选择显示模 式。然后将整个系统从地址 0x10000 移至 0x0000 处,进入保护模式并跳转至系统的余下部分(在 0x0000 处)。到此时,所有 32 位运行方式的启动设置均已被完成: IDT、GDT 以及 LDT 被加载,处理器和协处理器也已确认,分页工作也设置好了,最终会调用执行 init/main.c 中的 main()代码。上述操作的源代码 是在 boot/head.s 中的,这可能是整个内核中最有诀窍的代码了。注意如果在前述任何一步中出了错,计算机就会死锁。在操作系统还没有完全运转之前是处理不了出错的。
bootsect不把系统模块直接加载到物理地址 0x0000 处,而在setup中进行移动的原因是:随后执行的setup开始部分代码还需要利用ROM BIOS提供的中断调用功能获取有关机器配置的一些参数(例如显示卡模式、硬盘参数表等)。而当 BIOS 初始化时会在物理内存开始处 放置一个大小为 0x400 字节(1KB)的中断向量表,直接把系统模块放在物理内存开始处将导致该中断向 量表被覆盖掉。因此引导程序需要在使用完 BIOS 的中断调用后才能将这个区域覆盖掉。
另外,仅在内存中加载了上述内核代码模块还不足以让 Linux 系统运行起来。作为完整可运行的 Linux 系统还需要有一个基本的文件系统支持,即根文件系统(Root file-system)。Linux 0.12 内核仅支持 MINIX 的 1.0 文件系统。根文件系统通常存在于另一个软盘上或者在一个硬盘分区中。为了通知内核所 需要的根文件系统在什么地方,bootsect.S 程序第 44 行上给出了根文件系统所在的默认块设备号 ROOT_DEV。块设备号的含义请参见程序中的注释。在内核初始化时会使用编译内核时放在引导扇区第 509、510(0x1fc--0x1fd)字节中的指定设备号。bootsect.S 程序第 45 行上给出了交换设备号 SWAP_DEV, 它指出用作虚拟存储交换空间的外部设备号。

bootsect.S程序

bootsect.S 代码是磁盘引导块程序,驻留在磁盘的第一个扇区中(引导扇区,0 磁道(柱面),0 磁头, 第 1 个扇区)。
在 PC 机加电、ROM BIOS 自检后,ROM BIOS 会把引导扇区代码 bootsect 加载到内存地 址 0x7C00 开始处并执行。
在 bootsect 代码执行期间,它会将自己移动到内存绝对地址 0x90000 开始处 并继续执行。
该程序的主要作用是首先把从磁盘第 2 个扇区开始的 4 个扇区的 setup 模块(由 setup.s 编译而成)加载到内存紧接着 bootsect 后面位置处(0x90200),
然后利用 BIOS 中断 0x13 取磁盘参数表中当前启动引导盘的参数,接着在屏幕上显示“Loading system...”字符串。
再者把磁盘上 setup 模块后面的 system 模块加载到内存 0x10000 开始的地方。
随后确定根文件系统的设备号。若没有指定,则根据所保 存的引导盘的每磁道扇区数判别出盘的类型和种类(是 1.44M A 盘吗?),并保存其设备号于 root_dev (引导块的 508 地址处)中。
最后长跳转到 setup 程序开始处(0x90200)去执行 setup 程序。
在磁盘上, 引导块、setup 模块和 system 模块的扇区位置和大小示意图见图 6-3 所示。
 
notion image

编译和运行过程

运行环境

  • Ubuntu 12.04.5 LTS
  • gcc 3.4.6
  • Bochs x86 Emulator 2.4.6

编译内核时中断

In file included from /usr/include/linux/fs.h:10, from tools/build.c:28: /usr/include/linux/ioctl.h:4:23: asm/ioctl.h: No such file or directory In file included from /usr/include/linux/fs.h:11, from tools/build.c:28: /usr/include/linux/types.h:4:23: asm/types.h: No such file or directory In file included from /usr/include/linux/types.h:8, from /usr/include/linux/fs.h:11, from tools/build.c:28: /usr/include/linux/posix_types.h:35:29: asm/posix_types.h: No such file or directory In file included from /usr/include/linux/fs.h:11, from tools/build.c:28: /usr/include/linux/types.h:27: error: syntax error before "__le16" /usr/include/linux/types.h:28: error: syntax error before "__be16" /usr/include/linux/types.h:29: error: syntax error before "__le32" /usr/include/linux/types.h:30: error: syntax error before "__be32" /usr/include/linux/types.h:31: error: syntax error before "__le64" /usr/include/linux/types.h:32: error: syntax error before "__be64" /usr/include/linux/types.h:34: error: syntax error before "__sum16" /usr/include/linux/types.h:35: error: syntax error before "__wsum" In file included from tools/build.c:28: /usr/include/linux/fs.h:43: error: syntax error before "__u64" /usr/include/linux/fs.h:45: error: syntax error before "minlen" make: *** [tools/build] Error 1
原因是64位linux的asm目录为
/usr/include/x86_64-linux-gnu/asm
解决方法 建立链接
sudo ln -s /usr/include/x86_64-linux-gnu/asm /usr/include/asm

启动Bochs错误

./bochs/bochs-gdb: error while loading shared libraries: x: cannot open shared object file: No such file or directory
缺少libSM.so libX11.so.6 libstdc++.so6库
通过dpkg-query -S x 查找所在的包名,然后使用apt安装对的包
sudo apt-get install libx11-6:i386 lib32stdc++6 libxpm4:i386