前几天刚读了《
Bran
的内核开发指南
》,感觉有些收获。这里简单总结一下。
首先介绍下我的环境:
DJGPP,NASM [Windows]
VMWare WorkStation.(
上跑
RedHat Linux)
在将一个编译好的简单
kernel
运行的时候,用的是
VMWare
上虚拟的软盘。至于怎样在软盘上装
grub
等,不提。
下面说下几点体会:
1.
对于一个整个系统,或者叫一个整个的工程,一般都是有若干个小的部分组成的。以这个
kernel
为例,为了生成最终的一个
kernel.bin
,需要编写若干个
.c
文件和一个
.asm
文件。在分别将这些文件编译成
.o
后,最终需要一个
ld
的过程。从最终文件来看,这些各组成文件中的符号应该是全局惟一的,因为他们最终都会在
kernel.bin
中存在。以前在学习c的时候,讲到关键字
extern
,说它的作用是声明一个变量或者函数在另外一个模块中定义,现在终于体会到了是怎么一回事。也从全局的角度来看,对于
.bin
的贡献
(^_^)
,
.asm
和
.c
是相同的。只是
.asm
只需一个
nasm
,而
.c
需要预处理,编译,汇编的过程而已。
2.
在过程中,曾遇到这样一个问题:我在
DJGPP
中
gcc
了那些
.c
文件,但是我却在
RH Linux
中用
ld
,结果总是出现某些符号为定义的错误。找过去,找过来,最终发现一个问题。在
DJGPP
中
GCC
生成的文件是
COFF
格式,而
RH LINUX
下的
LD
需要的是
ELF
格式。我猜测,因为格式的不同,
ELF格式中对于.C的函数和变量符号在汇编后,在.S中是用本身的符号表示的,而不做变换。如.C中有函数ABC(),在编译后的.S中也以符号ABC表示;而COFF格式中对于.C的函数和变量符号在汇编后,在.S中会对他们的符号作些变换,具体是在前面加一个下画线。例如:.C中的函数ABC()在编译后的.S中会以符号_ABC表示。所以,当我换在WINDOWS CMD下,用LD链接后,没有出现问题。这时用的LD应该就是以COFF格式输入的。
3.
在生成了
KERNEL.BIN
后,
COPY
到软盘下时,要注意
:
在
BOOT/GRUB
下面的
STAGE1,2
的访问权限要有
X
。
(
试试知道的
)
4.
在汇编和
C
间传递参数用栈就可以了。并且,可以定义结构来表示栈中的内容,但要主要栈中内容的顺序和结构的顺序。
对于
OS
内核,有些感受:
5.
正如
MULTIBOOT
规范中所讲,一个
OS
的内核的格式应该可以是操作系统中可以运行的文件的格式,入
A.OUT
、
ELF
等。为了能够引导不同格式的内核,也及可以利用
MULTIBOOT
,那么就必须在内核文件的开头或者某些特殊位置定义某些固定的变量。如
MAGIC
等。
6.
接上,既然内核可以是一般格式的可执行文件。那么它就可以象应用程序那样使用系统的内存,该文件也可以定义若干的数据结构。只是,为了满足硬件对于操作系统的支持,需要把这些数据结构的地址,大小等加载到某些特定功能的寄存器。如
GDT
的地址和大小限制需要一个数据结构来表示,该结构的地址需要被
LOAD
到
GDTR
中,类似的还有
IDT
和
IDTR.
7.
在
IDT
中保存了若干的中断号以及中断服务程序的地址后,也可以对它们的映射关系做改变。当然,对于开始的
32
个中断
(
异常
)
是保留的,后面的可以自己映射。对于这里,以前一直不是很理解,现在简单总结一下中断。当一个中断源
(如键盘,系统时钟)到来时,在8259对它们进行优先级判断等操作后通知CPU,CPU在得到它们的中断号后,通过IDTR得到IDT的基地址,然后用中断号取出距离IDT若干距离的ISR的地址,并转到该ISR
运行。注意,这个
IDTR
是我们用指令
LIDT
写入的。其实,从这个
KERNEL
来看,我们就只需加载
GDTR
和
IDTR
,以及提供一些映射就可以了。其他的,
CPU
会在遇到某种情况时自己找到该运行的程序。
上面的东西,部分是我自己猜测的,希望各位看官能够指出其中的错误。感谢万分。