一个简单多任务内核实例的分析
转载请注明出处:http://blog.csdn.net/rosetta
简介
Linux-0.00是由Linus Torvalds写的Linux最初版本(未发布),只是打印AAA和BBB而没有更多的功能,比如内存管理、文件系统、字符设备驱动程序等,而Linux-0.11是一个比较完整的内核,也包含上述内容。
先分析Linux-0.00而不是Linux-0.11是因为前者是后者的基础,它非常精简但又能涵括几乎所有操作系统的基础知识,在真实完全明白Linux-0.00后再分析Linux-0.11就相对容易很多。
Linux-0.00的原始源码在redhat9.0中无法编译,但在《Linux内核完全剖析》一书中其作者给出了可以在redhat9.0中编译通过的Linux-0.00版本。
在分析任何代码之前如果可以我一般会先搭建环境,搭建环境的过程就会对整个操作过程有大概映像,如何搭建环境文档如下。http://blog.csdn.net/rosetta/article/details/8933240
Linux-0.00包含两个特权级3的用户任务和一个系统调用中断过程。其由两个文件组成:as86汇编语言写的boot.s(引导启动程序)和GNU as汇编写的head.s(多任务内核程序)。前者只是引导程序,把head.s代码加载进内存并把控制权转移到head.s中执行;后者实现两个特权级3上的任务在时钟中断控制下相互切换运行,并实现显示字符的系统调用。
任务A(0)不停的打印“AAA……”,当遇到时钟中断后切换到任务B(1)中运行打印“BBB……”,再遇时钟中断再打印“AAA……”,如此循环。
boot.s编译出的代码共544Bytes,由Makefile处理后剩余512B存放在软盘映像文件的第一扇区中(就是《Linux-0.00运行环境搭建》中的Image,这里使用的环境能用不用实际的软盘就不用,因为现在的计算机都没软区,以后考虑使用USB启动是挺不错的)。Image是由Makefile通过dd等命令把boot.s和head.s编译出来的二进制文件合起来的,在Linux-0.11中是由build.c完成合并操作的。
PC加电启动时,ROM BIOS中的程序会把启动盘(Image可被bochs直接启动,相当于启动盘)上第一个扇区加载到物理内存0x7c00处,并把程序执行权移到0x7c00处开始运行boot代码。boot的主要功能是把映像文件中的head内核代码先加载到内存的0x10000处,再把head搬到0x0处,并设置好临时GDT等信息后,把处理器设置成运行在保护模式下,再跳转到head中执行。这里有一个问题是boot为什么要把head先加载到0x10000再移到0x0处,而不直接加载到0x0处,这是因为把映像文件加载到内存中时需要使用BIOS INT13中断,而BIOS在初始化时会把中断向量表放在内存0x0处。
BOOT.S源码分析
完整的boot.s共66行,包括注释和空行,带行号。所有新增的分析都写在代码旁边,不带行号。
1 ! boot.s
2
3 BOOTSEG = 0x07c0
4 SYSSEG = 0x1000 ! system loaded at 0x10000 (65536).
5 SYSLEN = 17 ! sectors occupied.
6
7 entry start
8 start:
9 jmpi go,#BOOTSEG !跳转到段BOOTSEG,段内偏移为go的地方执行代码,此时为实模式,所以BOOTSEG为段地址,后面在保护模式下会讲到,jmpi后面跟的是偏移和段选择符。
10 go: mov ax,cs !跳转到这里后,cs的值就为0x07c0(注意,段地址在实际转换过程是需要左移4位,即多一个零为0x07c00,以下出些段的地方类似)
11 mov ds,ax
12 mov ss,ax
13 mov sp,#0x400 ! arbitrary value >>512
14 !设置临时栈指针。在这个程序中好像没什么用。
!开始加载内核模块head到段0x1000处。这里主要利用BIOS 的int 13中断从第一扇区读取代码到内存。
使用BIOS int 13 02H功能时各寄存器含义,可查询《x86中断手册》。
功能描述:读扇区
入口参数:AH=02H
AL=扇区数
CH=柱面
CL=扇区
DH=磁头
DL=驱动器,00H~7FH:软盘;80H~0FFH:硬盘
ES:BX=缓冲区的地址
出口参数:CF=0——操作成功,AH=00H,AL