《orange's:一个操作系统的实现》

第一章 第一个程序

1. 为什么引导扇区要以0x55aah为结尾,还有为什么org 0x7c00h对执行程序有什么作用?

第一个程序我用bochs+软盘映像按照书上第二章的命令来实验, 毕竟现在找个软盘不容易。但是整个实验现象是如何产生的,是如何到最后在屏幕左上角显示出 Hello, Os World!这串字符串的?这些都是需要搞明白的。这里面有几个知识点,1:开机启动流程 ;2:引导扇区

开机启动流程:

  1. 打开电源, 一通电源其实CPU已经开始工作了,CS: eip 指向 FFFF:0, 而FFFF:0处存放着一条跳转指令, 所以CPU就执行这条跳转指令

  2. 跳转指令跳转到BIOS ROM中的程序中执行,控制权交给BIOS

  3. BIOS 进行硬件检测和一些程序初始化比如设置一些BIOS中断向量(16位下)

  4. BIOS根据硬件设置,取得第一个可开机装置(这也是我们经常装系统是需要去BIOS中设置的开机启动装置顺序)

  5. BIOS去开机装置中加载数据,比如硬盘或者软盘就会去读取0号柱面0号磁柱1号扇区(MBR)中的数据,加载到内存0:7c00中(同时用0x55aa作为对引导扇区的校验, 如果不是以这两个字节数据结束,数据加载失败),加载完后cs:eip指向 0:7c00(加载过程是通过调用BIOS提供的扇区读取中断程序)

  6. 从0:7c00开始执行程序,CPU控制权从BIOS转移到开机管理程序也就是从硬盘或者软盘第一个扇区加载进来的数据,所以严格意义上说第一个程序只是一个开机管理程 序

  7. 我们的程序调用BIOS提供的中断程序向显存中写入字符串,并传递数量,现实属性等参数

  8. 最后调用 jmp $进入无限循环

通过这个启动流程发现其实我们写org 0x7c00h并不能认为将程序加载到7c00处去执行,上面7)之前全部都是硬件执行,我们控制不了,我们做的只是将数据放到确定的地 方,让硬件去读去执行,我们写org 0x7c00h 只是让编译器将其他标签的偏移地址设置正确,如果我们写成org 0x1000h,程序依然会被加载到 0:7c00处,但是 mov ax, BootMessage这句就会执行出错, 因为此时BootMessage会变成 1000h+ 1c(BootMessage和程序开头之间所有指令长度总和),但字符串实际是存在 0:7c1c处的。

引导扇区:

我们以硬盘的引导扇区为例,0柱面0磁道1扇区是整个硬盘的第一个扇区,也是所谓的引导扇区MBR。MBR总共512字节, 446开机管理程序 boot loader,64字节的分区 表, 2字节 0x55aa校验字节。这个开机管理程序是由操作系统写进去的,所以开机管理程序认识操作系统安装分区的文件系统,可以找到操作系统的核心文件,然后加载进 内存,并 将CPU控制权交给操作系统。一个硬盘只有一个MBR,但是当将磁盘进行磁盘分区后,每个分区都会有一个启动扇区,每个分区都会被格式化为一种文件系统

启动扇区都可以安装boot loader,boot loader认识核心文件在文件系统的位置。这就是为什么可以创建双系统的原因。像下图:

開機管理程式的選單功能與控制權轉交功能示意圖

首先安装windows操作系统,windows占有第一个分区叫做C盘吧,windows系统将自己的boot loader写入MBR,同时也会写入C盘分区的启动扇区(左下角的蓝块),这时如 果有其他分区比如D,E,其他分区的启动扇区都会写入boot loader

加入你现在想安装双系统,你需要分割出一个分区给linux, 叫D分区吧(当然一旦格式化成linux的文件系统比如ext3,windows就不认识这个分区了,在windows的文档管理 里也就看不见这个盘了),格式化分区(也就是写入文件系统信息),然后将linux的boot loader写入MBR和自己所在分区D的启动扇区。

启动机器时执行MBR的boot loader(因为这是最后linux写入的,而linux的boot loader不会覆盖windows所在分区的启动扇区)提供选单,MBR 的 boot loader可以直接加载l inux的核心文件(因为它认识linux的文件系统), 可以转向windows启动扇区的boot loader然后再由windows 自己的boot loader加载windows的核心文件,也可以转向linux启动 扇区的boot loader然后加载Linux的核心文件。这就是多重操作系统的原理。

第三章 保护模式

1. 描述符和选择子的结构

2. 为什么由实模式跳入保护模式的程序必须要在GDT第一项有一个空描述符?

这个问题一直困扰着我,把那项描述符去掉后,程序无法正常显示进入保护模式后显示的'P'字,然后我就开始debug程序观察前后有什么差别吗,结果也还是没有找到答案。

后来我在网上看到一位网友提到这样一个问题:

程序加载完gdtr,打开A20地址线,设置保护模式PE位后已经位于保护模式了, 那么也就是说执行完 mov cr0, ax这句后,程序已经位于保护模式了,但这个时候cs段寄存器中仍然存放的是实模式下的段地址而不是保护模式下的选择子, 执行完后一句 jmp dword SelectorCode32:0 后cs才会变成选择子的值,那既然已经位于保护模式,程序是通过实模式下的cs, eip取得下句执行代码的地址呢?也就是如何定位到该执行 jmp dword SelectorCode32:0这句呢?

举个例子吧,加入mov cr0,ax这句实模式下地址cs:eip=0000:7c00h,按实模式下的寻址机制下条指令的地址就是cs:eip=0000:7c03h也就是jmp dword SelectorCode32:0的地址,但现在已经位于保护模式下了,按照保护模式下的寻址机制下条指令的物理地址应该是: cs选择子所指描述符中的段基址 + eip,这样的话就指不到指令

jmp dword SelectorCode32:0 了。

那个问题中提到了描述符缓存寄存器,所以我就在网上搜索相关内容,然后就找到了这个解释了上面提到的两个问题,感谢!

描述符表和描述符高速缓存寄存器

这里面的主要意思是:

CPU有一组用于存储描述符的描述符告诉缓存寄存器,这些寄存器主要是为了CPU快速得到一个段的信息,不用经由gdtr寄存器,再去描述符表里取,而且段具有计算机科学中常见的局部性,不经常变动,这样可以提高效率。当段寄存器发生变化时比如jmp,call操作,CPU才从描述符表中将新值同步到缓存寄存器中。

不管是在实模式还是在保护模式下,CPU在实际寻址时都会使用这个高速缓存。所不同的是,在实模式下,在段寄存器的值发生变化时,仅仅把段寄存器的值×16(左移4位)放到高速缓存的的基地址位置,段界限和存取权限总是一个固定不变的值(按照Intel的说法,PC机加电后工作在实模式,高速缓存中将被置入缺省值,在实模式下,其中的段界限和存取权限将一直保持不变);而在保护模式下,当段寄存器发生变化时,CPU要从描述符表中加载数据到高速缓存中。

而GDT第一项必须为空描述符的原因请参考这篇文章:http://blog.csdn.net/ruyanhai/article/details/7181270

下面我们在mov cr0, eax和jmp dword SelectorCode32:0之间添加几条指令,然后在执行完mov cr0, eax后,通过debug设置cs eip,让程序转到保护模式下的代码

mov cr0, eax

mov eax, 0

mov eax, 0

jmp dword SelectorCode32:0

1)执行到mov eax, 0这条指令

image
  1. 设置cs=8, eip=0,即保护模式下32位段的逻辑段地址
image

3)然后用s单步执行,别用n因为n会跳过函数直接显示结果了

image

这就证明了上面的叙述,只有更改了告诉缓存寄存器中的信息,寻址模式才会更改。实模式下cs高速缓存寄存器下存储的就是段寄存器左移4位,段界限和段属性用默认值,而保护模式下CPU将描述符加载到缓存寄存器中,所以寻址模式是根据告诉缓存寄存器中的信息来决定的。

3. 为什么从保护模式跳回实模式需要一个Normal描述符和一个16位代码段?

这个问题其实都是问题2中描述的,因为高速缓存寄存器的存在,实模式和保护模式下高速缓存寄存器存储值的不同,并且影响了寻址机制造成的。

Normal描述符就是为了将ds, es等寄存器的高速缓存寄存器设置为实模式下正确的段界限和段属性,16位代码段的存在是为了cs寄存器切换时高速缓存寄存器中界限和属性符合实模式下的规范。

第三章工具的使用

1.如何调试超过一个扇区的程序?

在这里总结以下从第一章到第四章,书上所用各种工具的意义。

  1. 第一章直接用软盘,用bochs加载软盘,程序写到软盘的第一个扇区,bochs相当于一个实体机器,从软盘第一个扇区读取程序到0x7c00h,并跳转到此处执行。(具体开机流程可以参考第一篇笔记)

2)第二章用软盘的镜像文件充当虚拟的软盘,并没有突破引导扇区一个扇区容量的限制

  1. 第三章由于引导扇区512字节的限制,限制了实验程序的大小,而又暂时不想进入引导扇区加载其他程序进内存比如Loader的步骤,所以就暂时利用freedos这个操作系统,bochs通过利用freedos.img的第一个扇区当做引导扇区,先把CPU控制权交给freedos,freedos格式化我们实验程序写入的b.img为dos文件系统格式,我们将自己的实验程序拷贝到b.img(就是向文件系统中拷贝个文件)中,然后在freedos通过指令加载执行我们的实验程序,利用了dos的文件系统

4).第四章我们自己写引导扇区,加载自己的Loader,要突破第一扇区,就得靠自己加载了,因为bios程序只负责将引导扇区的程序读到0x7c00h中,然后就将控制权交给了引导扇区的程序。但如果我们自己去读取程序的话,只能靠硬件软盘,硬盘的物理划分来读取,一个扇区一个扇区的加载到程序,这限制了我们程序边界的灵活性,要读多个文件的话也很复杂,这个时候就需要文件系统的帮忙了,但鉴于我们还没有自己的文件系统,我们可以先用一个成熟的文件系统FAT12,这样我们就可以方便的将自己的Loader写到软盘的文件系统FAT12里,并在引导扇区程序里方便灵活的找到它。

我本来是按照书上所说使用freedos来执行编译的程序,但是想调试32位汇编程序,并没有找到好工具,根据网上所说我找到了TDdebug.exe,但是执行起来有问题(费了老大的劲又是重装bochs又是找各种版本),结果最后还是不行,后来用了个Debug32.exe但是不知道如何现实X86架构下的各个寄存器,所以最后放弃了。(其实想想很有可能是freedos根本不支持X86下的东西,或者版本不对,反正搞得很不爽,所以就停了)。最后就用bochs调试了,但是毕竟由于扇区限制和又还没写自己的Loader那。我的解决方案就是删程序。在这里我也建议大家如果没有更好的调试方法可以通过删程序将程序精简到512字节内直接用bochs调试。第一比较直观没有其他系统或者文件系统的干扰,第二在前几章还没有很复杂的程序出现,只需要保留自己想实验的代码,把没用的删除了,第三这样更能考察对于书中代码的理解,如果只是把书上的源代码原封不动的拿来调试,会忽视很多问题,只有出现问题,才能解决问题,如果没有出现任何问题也就证明没有解决任何问题。

2. 为什么用COM,ELF文件?

COM, ELF文件都只是中文件格式,就是除了文件内容有一些标识文件类型的信息,用来被解释者读取使用。COM是dos下可执行文件格式,ELF是Linux下可执行文件格式。前三章一直用com格式是因为一直在用dos加载执行程序,而后续我们要自己加载执行ELF文件。可执行文件除了代码外还会有其他的执行相关信息,比如代码,数据的分段,将代码加载到内存什么位置,这些信息在编译链接的时候写进可执行文件,加载的时候被加载程序读取用来做相应处理。所以说加载程序和可执行文件是配对的。采用ELF文件是因为我们会在Linux开发自己的操作系统,所以生成的执行文件就是ELF格式的,我们自己同样要开发加载程序去读取ELF文件中的信息。

3.什么时候使用汇编语言什么时候用C语言?

这个问题应该从技术发展的历史角度看。刚开始程序员用汇编写操作系统,但后来发现开发效率非常低下,所以发明了高级语言C语言,高级语言是为了开发通用软件时的效率。但是高级语言受限于编译器,主流的编译器只能将C语言转化为有限的汇编也就是汇编的一个子集,所以必然有一部分指令是没办法通过C语言实现的,这个时候就需要汇编了。

4.boot, loader, kernel都实现了什么?

Boot是引导扇区代码,受限于512字节大小限制,负责根据FAT12文件系统找到Loader所在软盘扇区位置,并将它加载到内存,将CPU控制权交给Loader。实现的是MBR程序的功能。但是使用了FAT12文件系统这个现有的功能并未自己实现。

Loader是负责加载kernel进内存,并且进入保护模式,然后将kernel根据ELF program信息将kernel载入不同的段,并且实现了分页,最后将CPU控制权交给Kernel。所以Loader实现了程序加载器的功能。

Kernel就是我们的内核,操作系统的核心,进程管理,内存管理,磁盘管理等的实现处。

5.既然需要用到FAT12文件系统还需要自己定义引导盘的盘头信息吗?

这个问题的意思是既然我们需要将自己的软盘格式化为FAT12格式,我们还需要定义下图的FAT盘头信息吗?
image

其实不需要了,因为反正我们都要将整个软盘格式化成FAT12,因为我们没有自己的文件系统,格式化的时候,引导扇区就已經被写入了上图定义的那些数据了。

image

可以看到前面62个字节就是所谓的软盘BP头信息,所以boot.asm其实没有必须定义这些数据,所以将

BS_OEMName DB 'ForrestY' ; OEM String, 必须 8 个字节

改成Off_BS_OEMNAME equ 3,其他信息一样的处理,这样变量定义变成了常量,可以节省62个字节的内存,这对于引导扇区512字节可是很珍贵的,相应的使用语句

add bx, [BPB_BytsPerSec]需要变成add bx, [Off_BPB_BytsPerSec+7c00h],因为编译器不会再帮我们计算偏移地址了,经测试程序可以正常运行。

6. 编译,链接,加载,执行流程?

编译器主要负责程序的翻译,以及内存布局等指导信息和一些链接需要的符号表信息。我们现在主要通过nasm编译汇编程序,用gcc编译C程序

链接主要是将多个独立编译的由编译器生成的目标文件和一些已经预编译好的公共库文件,根据各自提供的符号表信息合成最终的可执行文件,我们现在链接过程用ld

加载主要是内存中负责将外部存储设备上的可执行文件挪到内存中,并根据可执行文件中的信息,将不同的数据搬到不同的内存段中,我们现在加载靠自己写的Loader属于操作系统的一部分

操作系统加载完程序后,跳转执行可执行文件的第一条指令,CPU控制权交给可执行文件

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,542评论 6 504
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,822评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,912评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,449评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,500评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,370评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,193评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,074评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,505评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,722评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,841评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,569评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,168评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,783评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,918评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,962评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,781评论 2 354

推荐阅读更多精彩内容