|
因为setup.S1最后的为一条转跳指令,跳到内核第一条指令并开始执行。指令中指向的是内存中的绝对地址,我们无法依此判断转跳到了head.S。但是我们可以通过Makefile简单的确定head.S位于内核的前端。
在arch/i386 的 Makefile 中定义了
HEAD := arch/i386/kernel/head.o
而在linux总的Makefile中由这样的语句
include arch/$(ARCH)/Makefile
说明HEAD定义在该文件中有效
然后由如下语句:
vmlinux: $(CONFIGURATION) init/main.o init/version.o linuxsubdirs
$(LD) $(LINKFLAGS) $(HEAD) init/main.o init/version.o \
$(ARCHIVES) \
$(FILESYSTEMS) \
$(DRIVERS) \
$(LIBS) -o vmlinux
$(NM) vmlinux | grep -v '\(compiled\)\|\(\.o$$\)\|\( a \)' | sort > System.map
从这个依赖关系我们可以获得大量的信息
1>$(HEAD)即head.o的确第一个被连接到核心中
2>所有内核中支持的文件系统全部编译到$(FILESYSTEMS)即fs/filesystems.a中
所有内核中支持的网络协议全部编译到net.a中
所有内核中支持的SCSI驱动全部编译到scsi.a中
...................
原来内核也不过是一堆库文件和目标文件的集合罢了,有兴趣对内核减肥的同学,
可以好好比较一下看究竟是那个部分占用了空间。
3>System.map中包含了所有的内核输出的函数,我们在编写内核模块的时候
可以调用的系统函数大概就这些了。
好了,消除了心中的疑问,我们可以仔细分析head.s了。
Head.S分析
1 首先将ds,es,fs,gs指向系统数据段KERNEL_DS
KERNEL_DS 在asm/segment.h中定义,表示全局描述符表中
中的第三项。
注意:该此时生效的全局描述符表并不是在head.s中定义的
而仍然是在setup.S中定义的。
2 数据段全部清空。
3 setup_idt为一段子程序,将中断向量表全部指向ignore_int函数
该函数打印出:unknown interrupt
当然这样的中断处理函数什么也干不了。
4 察看数据线A20是否有效,否则循环等待。
地址线A20是x86的历史遗留问题,决定是否能访问1M以上内存。
5 拷贝启动参数到0x5000页的前半页,而将setup.s取出的bios参数
放到后半页。
6 检查CPU类型
@#$#%$^*@^?(^%#$%!#!@?谁知道干了什么?
7 初始化页表,只初始化最初几页。
1>将swapper_pg_dir(0x2000)和pg0(0x3000)清空
swapper_pg_dir作为整个系统的页目录
2>将pg0作为第一个页表,将其地址赋到swapper_pg_dir的第一个32
位字中。
3>同时将该页表项也赋给swapper_pg_dir的第3072个入口,表示虚拟地址
0xc0000000也指向pg0。
4>将pg0这个页表填满指向内存前4M
5>进入分页方式
注意:以前虽然在在保护模式但没有启用分页。
--------------------
| swapper_pg_dir | -----------
| |-------| pg0 |----------内存前4M
| | -----------
| |
--------------------
8 装入新的gdt和ldt表。
9 刷新段寄存器ds,es,fs,gs
10 使用系统堆栈,即预留的0x6000页面
11 执行start_kernel函数,这个函数是第一个C编制的
函数,内核又有了一个新的开始。1
注:我在linux0.01版本中没有看到setup.s文件,请问是不是因为以当时内核的大小还用不着压缩,所
以没有解压缩模块,但硬件信息的检测与初始化,在哪里进行呢?希望高手解答。 |
|