1 简介
现在回过头来,再来看看理论上的一些知识,可以更好的去编写操作系统。
资源:
1.freebsd handbook 1
2 保护模式
这节简单说一下保护模式。
在一般的实模式下,访问的空间只有1MB,总共占用20根数据线,所以为了扩展空间,或者是历史原因,并不能扩展到更大的空间,于是intel以workaround的方式提出了A20线,这样会扩展出4根地址线,也就使地址线扩展到了24位,这样能访问16MB的空间。
怎么进入保护模式:
进入保护需要开启A20,这样可以将空间扩展到16MB,然后是需要设置寄存器CR0,让处理器进入保护模式,最后则是设置GDT,LDT,IDT等等相关表项。
写成一个具体流程,则是如下:
1.开启A20
2.配置GDT,LDT,IDT
3.配置CR0
开启A20有很多的方法,不过我选择使用端口0x92。
控制端口0x92的方法为:
in al,0x92
or al,00000010b
out 0x92,al
含义为:
位 | 含义 |
---|---|
Bit 0 | Setting to 1 causes a fast reset |
Bit 1 | 0: disable A20, 1: enable A20 |
Bit 2 | Manufacturer defined |
Bit 3 | power on password bytes. 0: accessible, 1: inaccessible |
Bits 4-5 | Manufacturer defined |
Bits 6-7 | 00: HDD activity LED off, 01 or any value is "on" |
配置CR0,可以列出其每一位的含义:
位 | 含义 |
---|---|
bit0 | 保护允许位PE, PE=0,实模式运行。PE=1,保护模式运行 |
给出进入保护模式下的代码:
mov eax,cr0
or eax,0x1
mov cr0,eax
退出保护模式下的代码:
mov eax,cr0
and eax,0fffffffeh
mov cr0,eax
关于gdt,有一个通用的寄存器图:

位段 | 含义 |
---|---|
段限长 | 20位,G=1,则段大小4KB到4GB。G=0,则段大小1B到1MB |
基地址 | 开始位置 |
类型字段 | |
S标志 | 0表示系统段,1表示代码段或者数据段 |
DPL字段 | 特权级0,1,2,3 |
P标志 | 1表示该段在内存中,0表示该段不在内存中 |
D/B标志 | |
G标志 | 段的界限字段的的大小和步进 |
L标志 | 1表明代码段运行在64位模式,0表示运行在32位模式 |
以上描述的就是GDT表中存放的数据内容含义。
类型字段描述在下表中:

举一个基本例子:
要求 | 取值 |
---|---|
代码段 | P=1,S=1 |
只读,可执行 | 类型type=1010 |
颗粒度4kbytes,32位模式 | G=1,B/D=1 |
基址为0 | |
段限长8MB | 8MB=2048*4KB,值为0x7ff |
访问权限最高 | DPL=0 |
所以按照这个例子,GDT=0000 0000 1100 0000 1001 1010 0000 0000
0000 0000 0000 0000 0000 0111 1111 1111
故为:0x00c09a00 0x000007ff。段限长是最低位。
同理,如果是数据段,则只需要改变type字段,因为数据段是可读可写的,对应的最终值为:0x00c09200 0x0000 07ff。
理解了这个部分之后,需要了解的就是怎么去加载这个表项。然后提出使用lgdt这个操作,操作对象是gdtr寄存器,其存放了gdt的32位线性地址和16位表长度值。
再来简单说说段选择符内容:
位 | 含义 |
---|---|
bit0~bit1 | RPL ,与DPL含义相同,只有RPL级别高于DPL才能执行后续操作 |
bit2 | TI=0,表示GDT,TI=1表示LDT |
bit3~bit15 | 索引 |
关于中断门,也列出其格式:

注意:中断门和gdt的意义是不一样的,最低16位表示的是入口点偏移值,高16位表示的是段选择符。
3 代码分离与加载
完成了保护模式的测试之后,就需要加载更多的内核代码运行了。根据上面的基础,bios会自动加载一段代码到0x7c00处,512字节后的代码就需要自己手动加载到内存中了。
这里面就涉及到代码的分布了。同时要考虑到将512B的代码放到内存的哪个位置比较合适的问题。
要考虑的问题:
1.将代码放到软盘,怎么放,这个时候需要写ld脚本与makefile
2.使用读磁盘指令加载磁盘数据到内存中
4 汇编跳转到c环境
以上条件准备完毕之后,就可以跳转到c环境了,跳转到c环境仍然是使用jmp指令。看过"linux内核完全剖析.pdf"会发现linux0.12为了跳转到c做的挺多的。但是,我这只是做一个简单的的系统,并且也不需要返回main,那也就不需要做那么严谨的工作了。
5.功能发布
其实到目前为止,基础的环境已经搭建好了,可以尝试做进程切换与中断处理这些工作了,我也将代码上传到了我的github上。
6.问题与思考
写一个操作系统,目前来说在环境上还是比较方便的。我在这里写出一个操作系统慢慢搭建过程。
a.实模式跳转到保护模式(先做gdt,然后整体改为ldt)
b.保护模式跳转到c代码
c.实模式下的中断程序
d.保护模式下的中断程序
e.保护模式下的程序切换
f.扩展更多的内容
这篇文章在代码级别的实现已经基本完成,所以就不再写了,开另外一篇继续写其他的。
网友评论