您可以添加到网摘 让更多人关注此文章:
三 编码过程
1. 开发环境
我们尝试了两种开发环境,Linux和Windows。在Linux环境下,程序开发工具丰富好用,但是在对中文的支持上不够。在Windows下,适合操作系统开发的程序开发工具并不是很多,但对中文支持很好,且具有更大的通用性。所以,在进行了一段时间的Linux开发后,我们选用Windows做为开发平台。
2. C编译器
我们采用GNU的gcc编译器进行C程序的开发。Gcc是Linux下著名的C程序编译器,支持内联的汇编语句,比较适合系统程序的开发。且支持多种目标文件的格式,如coff,elf等,可以将C源程序方便地编译成多种目标文件。在Windows环境下,默认情况下产生coff文件格式,而在Linux环境下,默认产生elf格式的文件。
3. 汇编器
在众多的汇编器中,我们选用nasm来进行汇编部分代码的编译。这是因为nasm非常适合操作系统的开发。如用其它的汇编器,在从实模式到保护模式的过渡过程中,因为涉及到16位与32位指令的过渡,一般要直接写二进制代码来进行jmp的步骤。而在nasm中,直接用jmp dword就可以轻松做到。可以使用nasm –f coff a.asm来产生coff格式的目标文件,-f开关用于选择要产生的目标文件类型。Nasm也支持elf、coff等多种目标文件格式。
4. 连接器
在用编译器产生C和汇编目标文件后,下一步是用连接器将它们连接成一个内核文件。我们选用GNU的ld连接器来进行目标文件的连接工作。
5. 二进制文件格式
在Windows环境下,coff文件格式是得到编译器和连接器较为广泛支持的一种目标文件格式。在coff目标文件中,C与汇编产生的标号(Symbol)略有不同,在开发中应予以重视。即gcc在对C源程序中的标号进行处理时,在前面添加了一个下划线。如果我们在汇编程序中要引用这个标号,就要注意增加一个下划线。如有一个C程序里面有一个void maind(void)函数,如在汇编中引用,则要这样定义:
extern _maind
如果编译成elf文件格式,则没有此问题。
四 调试过程
1. 使用X86模拟器
作为计算机的基础软件,在编译后的操作系统一定要在真实的机器上运行才能知道程序是否正确。这样,如果要进行操作系统的调试,要么把本机重启,要么找一台专门的机器用于调试。这样做都不是很方便。
我们还有一个选择就是使用X86系统的模拟器。模拟器运行于现有的操作系统之上,其将操作系统内核读入,在其内部摸拟X86系统。这样,我们运用摸拟器就可以直接在开发机上进行操作系统的运行,非常方便快速。
常见的X86模拟器有VMWare、Bochs等。VMWare是商业软件,有Windows和Linux两种版本,易于使用,但其是为一般用户开发的,功能较少。Bochs是开源的优秀模拟器,免费使用,而且它体积小巧,功能强大。在我们的开发中,使用Bochs进行系统的模拟环境,并借用Bochs进行二进制级别的调试。
2. 使用Bochs进行二进制级别的调试
在操作系统的开发中,调试是一件比较困难的事情。因为在开发的初期无法移植一些知名的调试软件之前,调试一般只能依靠二进制级的工具来进行。前面提到的模拟器Bochs,在除了模拟功能以外,还具有一定的调试功能。在Bochs目录下,BOCHSDBG.EXE即为集成了调试功能的Bochs可执行文件。在Bochs运行后,出现了如下的提示后,就可以输入一些进行诸如断点设置,汇编/反汇编,查看CPU状态等调试命令。
Next at t=0
(0) context not implemented because BX_HAVE_HASH_MAP=0
[0x000ffff0] f000:fff0 (unk. ctxt): jmp f000:e05b
<bochs:1>
下面是用Bochs进行调试的一些常用命令,熟练掌握这些命令,对调试系统是非常有帮助的。
|
命令 |
说明 |
|
c |
继续执行(continue) |
|
step [count] |
单步执行,count为步数,默认为1(execute count instructions) |
|
Ctrl-C |
停止运行 |
|
quit |
退出bochs |
|
vbreak seg:off |
虚拟地址断点,seg为段地址,off为偏移,用于在指定的虚拟地址处设置断点(virtual address break) |
|
pbreak addr |
物理地址断点,addr为物理地址。用于在指定的物理地址处设置断点(physical address break) |
|
info break |
显示已经设置的断点情况 |
|
delete n |
删除断点(Delete a breakpoint) |
|
x /nuf addr |
查看在线性地址处的内存单元内容(Examine memory at linear address addr) 其中:n表示要显示的单元数,u表示单元大小,f表示打印格式 |
|
xp /nuf addr |
查看在物理地址处的内存单元内容(Examine memory at physical address addr)其中nuf的意义同上 |
|
setpmem addr datasize val |
设置在物理内存地址addr上的内存单元内容。Datasize为要设置的单元大小,val为要设置的值。 |
|
info registers |
打印出CPU寄存器的当前值。 |
|
set $reg = val |
将一个寄存器赋值,其中reg可以换为eax等寄存,如set $eax = 0xA |
|
dump_cpu |
将CPU的整个状态全部打印出来 |
|
disassemble start end |
反汇编指定地址的程序。Start为开始的线性地址,end为结束的线性地址。 |
参考文献:
1. Intel,IA-32 Intel® Architecture Software Developer’s Manual Volume 3: System Programming Guide,Intel Corporation,2002
2. 杨季文,80x86汇编语言程序设计教程,清华大学出版社,1998
3. Peter Abel,IBM PC Assembly Language And Programming(Fourth Edition),Prentice Hall,1998
4. W.Richard Stevens,Advanced Programming in the UNIX Environment,Addison-Wesley,1993
5. M.Morris Mano,Computer System Architecture(Third Edition),Prentice Hall,1993
6. Barry B.Brey,The Intel Microprocessors(Fifth Edition),Pearson Education,2000
7. Maurice.Bach,The Design of The UNIX Operating System,Prentice Hall,1986
8. Andrew S.Tanenbaum Albert S.Wooddhull,Operating System:Design and Implementation(Second Edition), Prentice Hall,1997
|