前几天大家对Linux的启动有些讨论。
于是整理了一下前段时间与他人交流的提纲,希望起到抛砖引玉的作用。
xiaoman04@hotmail.com
这是一次对Linux介绍后的整理。
对象是一些刚对Linux核心感兴趣,并且准备进一步研究和改造的同志。
因为是由提纲整理而成,有些乱,见谅!
四部分内容:
一、Linux核心源码结构介绍
二、编译和配置的过程
三、系统启动顺序的相关文件
四、核心改造的一些经验
一、
当我们安装好一个Linux系统,通常核心源码存放在/usr/src/linux/目录。
下面先看看这目录下的各个子目录及文件。
[/]#cd /usr/src/linux
[linux]#ls -aF
./ MAINTAINERS drivers/ kernel/ scripts/
../ Makefile fs/ lib/
COPYING README include/ mm/
CREDITS Rules.make init/ modules/
Documentation/ arch/ ipc/ net/下面我们逐一描述:
COPYING
## GPL版权申明,看后你至少应该知道,你对具有GPL版权的源代码改动而
形成的程序,或使用GPL工具产生的程序,具有使用GPL发表的义务。其中之一就是
公开源代码。
CREDITS
##光荣榜,你应当感谢的一些人的信息,其中的每一个人都对Linux做出过
很大贡献。
Documentation/
##文档目录,可有选择地看一下你感兴趣的部分
MAINTAINERS
##维护人员列表,对当前版本的内核各部分都有谁负责,如果你研究的
够深入,可以与他们讨论
Makefile
##如果你在UNIX编译过程序,可以看明白
README
## Linus所写,核心及其编译配置方法简单介绍
Rules.make
## make时使用的一些共同规则
arch/
## architecture(体系结构)我关心的i386启动过程在其中,
##包括Linux在多种平台下的实现。如果要移植系统到一个新的
##CPU环境中,这就是你要关心的目录
drivers/
##驱动程序目录,包含大量设备驱动的实现,按类别分子目录
fs/
##文件系统,实现了当前流行的几乎所有文件系统。Cool
include/
##嵌入文件目录
init/
##初始化文件,包含main.c和version.c两个文件。Initialize
ipc/
## ipc的实现,与SYS V兼容
kernel/
##最核心代码,调度,中断,信号等的处理
lib/
##一些工具。
mm/
##内存管理,Memory Manager,虚拟页、缓冲的实现。
modules/
##模块文件目录,用于存放编译时产生的模块目标文件(参考编译过程)
net/
##网络实现,包括TCP/IP在内的大量网络协议的实现。
scripts/
##描述文件,脚本,用于对核心的配置。
二、
构造内核
常用命令包括:
make config, dep, clean, mrproper, zImage,
bzImage, modules, modules_install
(1) make config
核心配置,调用./scripts/Configure按照arch/i386/config.in来进行
配置。
命令执行完后产生文件.config,其中保存着配置信息。
下一次再做make
config将产生新的.config文件,原.config被改名为.config.old
(2)make dep
寻找依存关系。
产生两个文件.depend和.hdepend
其中.hdepend表示每个.h文件都包含其它哪些嵌入文件。
而.depend文件有多个,在每个会产生目标文件(.o)文件的目录下均有,
它表示每个目标文件都依赖哪些嵌入文件(.h)。
(3)make clean清出以前构核所产生的所有目标文件、模块文件、核心以及一些临时文件等,
不产生任何文件(4)make rmproper
删除所有因构核过程中产生的所有文件,及除了做make clean外,还要
删除.config,.depend等文件,把核心源码恢复到最原始的状态。
下次构核时就必须重新配置了。
(5)make, make zImage, make bzImage
make:
构核。通过各目录的Makefile文件进行。
会在各个目录下产生一大堆目标文件,若核心代码没有错误,将
产生文件vmlinux,这就是所构的核心。并产生映射文件System.map
通过各目录的Makefile文件进行。
.version文件中的数加1,表示版本号(又产生一个新的版本了),让你
明白,你已经对核心改动过多少次了。
make zImage:
在make的基础上产生压缩的核心映象文件./arch/$(ARCH)/boot/zImage
以及在./arch/$(ARCH)/boot/compresed/目录下产生一些临时文件。
make bzImage:
在make的基础上产生压缩比例更大的核心映象文件
./arch/$(ARCH)/boot/bzImage
以及在./arch/$(ARCH)/boot/compresed/目录下产生一些临时文件。
在核心太大时进行。
(6)make modules
编译模块文件,你在make
config时所配置的所有模块将在这时编译,
形成模块目标文件,并把这些目标文件存放在modules目录中。
使用如下命令看一看。
ls modules
(7)make modules_install
把上面编译好的模块目标文件目录/lib/modules/$KERNEL_VERSION/中。
比如我的版本是2.0.36,做完这个操作后可使用下面的命令看看:
ls /lib/modules/2.0.36/
相关的命令还有很多,有兴趣可看相关资料和Makefile文件。
另外注意,这儿我们产生了一些隐含文件
.config
.oldconfig
.depend
.hdepend
.version它们的意义应该很清楚了。
三、
系统的启动顺序及相关文件
仍在核心源码目录下,看以下几个文件
./arch/$ARCH/boot/bootsect.s
./arch/$ARCH/boot/setup.s
./init/main.c
bootsect.S及setup.S
这个程序是linux
kernel的第一个程序,包括了linux自己的bootstrap程序,
但是在说明这个程序前,必须先说明一般IBM
PC开机时的动作(此处的开机是指
"打开PC的电源"):
一般PC在电源一开时,是由内存中地址FFFF:0000开始执行(这个地址一定
在ROM BIOS中,ROM
BIOS一般是在FEOOOh到FFFFFh中),而此处的内容则是一个
jump指令,jump到另一个位於ROM
BIOS中的位置,开始执行一系列的动作,包
括了检查RAM,keyboard,显示器,软硬磁盘等等,这些动作是由系统测试代码
(system test code)来执行的,随着制作BIOS厂商的不同而会有些许差异,但都
是大同小异,读者可自行观察自家机器开机时,萤幕上所显示的检查讯息。
紧接着系统测试码之后,控制权会转移给ROM中的启动程序
(ROM bootstrap routine),这个程序会将磁盘上的第零轨第零扇区读入
内存中(这就是一般所谓的boot
sector,如果你曾接触过电脑病
毒,就大概听过它的大名),至於被读到内存的哪里呢?
--绝对
位置07C0:0000(即07C00h处),这是IBM系列PC的特性。而位在linux开机
磁盘的boot
sector上的正是linux的bootsect程序,也就是说,bootsect是
第一个被读入内存中并执行的程序。现在,我们可以开始来
看看到底bootsect做了什么。
第一步
首先,bootsect将它"自己"从被ROM
BIOS载入的绝对地址0x7C00处搬到
0x90000处,然后利用一个jmpi(jump
indirectly)的指令,跳到新位置的
jmpi的下一行去执行,
第二步
接着,将其他segment
registers包括DS,ES,SS都指向0x9000这个位置,
与CS看齐。另外将SP及DX指向一任意位移地址(
offset ),这个地址等一下
会用来存放磁盘参数表(disk para-
meter table )
第三步
接着利用BIOS中断服务int
13h的第0号功能,重置磁盘控制器,使得刚才
的设定发挥功能。
第四步
完成重置磁盘控制器之后,bootsect就从磁盘上读入紧邻着bootsect的setup
程序,也就是setup.S,此读入动作是利用BIOS中断服务int 13h的第2号功能。
setup的image将会读入至程序所指定的内存绝对地址0x90200处,也就是在内存
中紧邻着bootsect所在的位置。待setup的image读入内存后,利用BIOS中断服
务int 13h的第8号功能读取目前磁盘的参数。
第五步
再来,就要读入真正linux的kernel了,也就是你可以在linux的根目录下看
到的"vmlinuz"。在读入前,将会先呼叫BIOS中断服务int 10h的第3号功能,
读取游标位置,之后再呼叫BIOS中断服务int 10h的第13h号功能,在萤幕上输
出字串"Loading",这个字串在boot
linux时都会首先被看到,相信大家应该觉
得很眼熟吧。
第六步
接下来做的事是检查root
device,之后就仿照一开始的方法,利用indirect
jump跳至刚刚已读入的setup部份
比较
把大家所熟知的MS DOS与linux的开机部份做个粗浅的比较,MS DOS由位於
磁盘上boot
sector的boot程序负责把IO.SYS载入内存中,而IO.SYS则负有把DOS
的kernel
--MSDOS.SYS载入内存的重责大任。而linux则是由位於boot sector的
bootsect程序负责把setup及linux的kernel载入内存中,再将控制权交给setup。
##这几步内容主要参照一个台湾同胞写的文档,setup.s的内容希望有人补充。
start_kernel()
当核心被载入后,首先进入的函数就是start_kernel。
./init/main.c中函数start_kernel包含核心的启动过程及顺序。
通过它来看核心整个初始化过程。
首先进行一系列初始化,包括:
trap_init(); ##./arch/i386/kernel/traps.c陷入
init_IRQ(); ##./arch/i386/kernel/irq.c setup
IRQ
sched_init(); ##./kernel/sched.c调度初始化,并初始化bottom_half
time_init(); ##./arch/i386/kernel/time.c
init_modules(); ##模块初始化
mem_init(memory_start,memory_end);
buffer_init(); ## ./fs/buffer.c缓冲区
sock_init(); ## ./net/socket.c
socket初始化,并初始化各协议(TCP等)
ipc_init();
sysctl_init();
然后通过调用kernelthread()产生init进程,全权交由init进程处理。调用cpu_idle(NULL)休息。
感兴趣又有时间的同志可以写一个startkernel()函数的详细分析报告。
下面看一看init进程的工作:
首先创建进程
bdflush ##./fs/buffer.c缓冲区管理
和kswapd ##./mm/vmscan.c虚拟内存管理
这两个进程非常重要
系统初始化(系统调用setup)
系统初始化包含设备初始化及各文件系统初始化。
sys_setup(./fs/filesystems.c)
|
|-device_setup
| |
| -- chr_dev_init(); ##字符设备
| blk_dev_init(); ##块设备
| scsi_dev_init(); ##SCSI
| net_dev_init(); ##网络设备
| console_map_init(); ##控制台
|-binfmt_setup();
|-init_nls() ##各文件系统初始化
|-init_ext_fs()
|-init_ext2_fs()
. .
. .
. .
|-init_autofs_fs()
--mount_root() ##mount root fs
##从这儿看看设备及文件的初始化顺序,加入我们的设备时就有了大局观。
执行/etc/rc ( rc.sysinit, rc.local,
rc.# )和
执行/bin/sh
______________________________________________________
END