《30天自制操作系统》前言、目录、样章欢迎阅读!
编著推荐:
只需30天从零开始编写一个五脏俱全的图形操作系统
如果肯坚持,没有什么不可以!祝所有读到这篇文章的人都能写出好的操作系统!
内容简介:
内容简介:
自己编写一个操作系统,是许多程序员的梦想。也许有人曾经挑战过,但因为太难而放弃了。其实你错了,你的失败并不是因为编写操作系统太难,而是因为没有人告诉你那其实是一件很简单的事。那么,你想不想再挑战一次呢?
这是一本兼具趣味性、实用性与学习性的书籍。作者从计算机的构造、汇编语言、C语言开始解说,让你在实践中掌握算法。在这本书的指导下,从零编写所有代码,30天后就可以制作出一个具有窗口系统的32位多任务操作系统。
本书以课题为主导,边做边玩,抛开晦涩难懂的语言,行文风格十分随性,还充满了各种欢乐的吐槽,适合操作系统爱好者和程序设计人员阅读。
作者简介:
川合秀实(Hidemi Kawai)
生于1975年,是一位以“轻量化”编程思想见长的“非主流”开发者。2000年因自行开发的OSASK项目而名声大噪。OSASK是一个开源的 32位微型操作系统,它并非以Linux等内核为基础,而是完全从零开始开发,在一张软盘的容量下实现了GUI、多任务、多语言等高级特性,启动时间只需 1秒。本书的内容可以看成是作者以OSASK为蓝本,教会读者从零开始开发一个操作系统,同时可以让初学者在编写操作系统的过程中,了解操作系统背后更多 的知识。
作者网站:http://hrb.osask.jp/
前 言
“好想编写一个操作系统呀!”笔者的朋友曾说这是所有程序员都曾经怀揣的一个梦想。说“所有的程序员”可能有点夸张了,不过作为程序员的梦想,它至少也应该能排进前十名吧。
也许很多人觉得编写操作系统是个天方夜谭,这一定是操作系统业界的一个阴谋(笑)。他们故意让大家相信编写操作系统是一件非常困难的事情,这样就 可以高价兜售自己开发的操作系统,而且操作系统的作者还会被顶礼膜拜。那么实际情况又怎么样呢?和别的程序相比,其实编写操作系统并没有那么难,至少笔者 的感觉是这样。
在各位读者之中,也许有人曾经挑战过操作系统的编写,但因为太难而放弃了。拥有这样经历的人也许不会认同笔者的观点。其实你错了,你的失败并不是因为编写操作系统太难,而是因为没有人告诉你那其实是一件很简单的事而已。
不仅是编写操作系统,任何事都是一样的。如果讲解的人认为它很难,那就不可能把它讲述得通俗易懂,即便是同样的内容,也会讲得无比复杂。这样的讲解,肯定是很难懂的。
那么,你想不想和笔者一起再挑战一次呢?如果你曾经梦想过编写自己的操作系统,一定会觉得乐在其中的。
可能有人会说,这本书足足有700多页,怎么会“有趣”和“简单”呢?唔,这么一说笔者也觉得挺心虚的,不过其实也只是长了那么一点点啦。平均下来的话,每天只有大约23页的内容,你看,也没有那么长吧?
这本书的文风非常轻松,也许你不知不觉中就会读得很快。但是这样的话可能印象不会很深,最好还是能静下心来慢慢地读。书中所展示的程序代码和文字的说明同样重要,因此也希望大家仔细阅读。只要注意这些,理解本书的内容就应该没有问题了。
在本书中,我们使用C语言和汇编语言来编写操作系统,不过不必担心,你可以在阅读本书的同时来逐步学习关于这些编程语言的知识。本书在这方面写得 非常仔细,如果能有人通过本书终于把C语言中的指针给搞懂了,那笔者的目的也就达到了。即便是从这样的水平开始,30天后你也能够编写出一个很棒的操作系 统,请大家拭目以待吧!
目 录
第0天 着手开发之前 1
1 前言 1
2 何谓操作系统 3
3 开发操作系统的各种方法 4
4 无知则无畏 4
5 如何开发操作系统 6
6 操作系统开发中的困难 7
7 学习本书时的注意事项(重要!) 9
8 各章内容摘要 11
第1天 从计算机结构到汇编程序入门 13
1 先动手操作 13
2 究竟做了些什么 19
3 初次体验汇编程序 22
4 加工润色 24
第2天 汇编语言学习与Makefile入门 28
1 介绍文本编辑器 28
2 继续开发 29
3 先制作启动区 40
4 Makefile入门 41
第3天 进入32位模式并导入C语言 45
1 制作真正的IPL 45
2 试错 50
3 读到18扇区 51
4 读入10个柱面 52
5 着手开发操作系统 54
6 从启动区执行操作系统 55
7 确认操作系统的执行情况 56
8 32位模式前期准备 57
9 开始导入C语言 59
10 实现HLT(harib00j) 62
第4天 C语言与画面显示的练习 64
1 用C语言实现内存写入(harib01a) 64
2 条纹图案(harib01b) 67
3 挑战指针(harib01c) 69
4 指针的应用(1)(harib01d) 74
5 指针的应用(2)(harib01e) 74
6 色号设定(harib01f) 75
7 绘制矩形(harib01g) 84
8 今天的成果(harib01h) 86
第5天 结构体、文字显示与GDT/IDT初始化 88
1 接收启动信息(harib02a) 88
2 试用结构体(harib02b) 89
3 试用箭头记号(harib02c) 91
4 显示字符(harib02d) 91
5 增加字体(harib02e) 94
6 显示字符串(harib02f) 96
7 显示变量值(harib02g) 97
8 显示鼠标指针(harib02h) 99
9 GDT与IDT的初始化(harib02i) 101
第6天 分割编译与中断处理 108
1 分割源文件(harib03a) 108
2 整理Makefile(harib03b) 109
3 整理头文件(harib03c) 110
4 意犹未尽 112
5 初始化PIC(harib03d) 115
6 中断处理程序的制作(harib03e) 119
第7天 FIFO与鼠标控制 125
1 获取按键编码(hiarib04a) 125
2 加快中断处理(hiarib04b) 127
3 制作FIFO缓冲区(hiarib04c) 130
4 改善FIFO缓冲区(hiarib04d) 133
5 整理FIFO缓冲区(hiarib04e) 135
6 总算讲到鼠标了(harib04f) 138
7 从鼠标接受数据(harib04g) 141
第8天 鼠标控制与32位模式切换 144
1 鼠标解读(1)(harib05a) 144
2 稍事整理(harib05b) 146
3 鼠标解读(2)(harib05c) 148
4 移动鼠标指针(harib05d) 151
5 通往32位模式之路 153
第9天 内存管理 162
1 整理源文件(harib06a) 162
2 内存容量检查(1)(harib06b) 163
3 内存容量检查(2)(harib06c) 168
4 挑战内存管理(harib06d) 172
第10天 叠加处理 181
1 内存管理(续)(harib07a) 181
2 叠加处理(harib07b) 184
3 提高叠加处理速度(1)(harib07c) 194
4 提高叠加处理速度(2)(harib07d) 197
第11天 制作窗口 201
1 鼠标显示问题(harib08a) 201
2 实现画面外的支持(harib08b) 202
3 shtctl的指定省略(harib08c) 203
4 显示窗口(harib08d) 206
5 小实验(harib08e) 208
6 高速计数器(harib08f) 209
7 消除闪烁(1)(harib08g) 211
8 消除闪烁(2)(harib08h) 214
第12天 定时器(1) 220
1 使用定时器(harib09a) 220
2 计量时间(harib09b) 224
3 超时功能(harib09c) 225
4 设定多个定时器(harib09d) 228
5 加快中断处理(1)(harib09e) 232
6 加快中断处理(2)(harib09f) 234
7 加快中断处理(3)(harib09g) 236
第13天 定时器(2) 240
1 简化字符串显示(harib10a) 240
2 重新调整FIFO缓冲区(1)(harib10b) 241
3 测试性能(harib10c~harib10f) 243
4 重新调整FIFO缓冲区(2)(harib10g) 246
5 加快中断处理(4)(harib10h) 253
6 使用“哨兵”简化程序(harib10i) 257
第14天 高分辨率及键盘输入 262
1 继续测试性能(harib11a~harib11c) 262
2 提高分辨率(1)(harib11d) 266
3 提高分辨率(2)(harib11e) 269
4 键盘输入(1)(harib11f) 272
5 键盘输入(2)(harib11g) 275
6 追记内容(1)(harib11h) 277
7 追记内容(2)(harib11i) 279
第15天 多任务(1) 282
1 挑战任务切换(harib12a) 282
2 任务切换进阶(harib12b) 289
3 做个简单的多任务(1)(harib12c) 291
4 做个简单的多任务(2)(harib12d) 293
5 提高运行速度(harib12e) 294
6 测试运行速度(harib12f) 297
7 多任务进阶(harib12g) 299
第16天 多任务(2) 304
1 任务管理自动化(harib13a) 304
2 让任务休眠(harib13b) 308
3 增加窗口数量(harib13c) 313
4 设定任务优先级(1)(harib13d) 317
5 设定任务优先级(2)(harib13e) 320
第17天 命令行窗口 329
1 闲置任务(harib14a) 329
2 创建命令行窗口(harib14b) 331
3 切换输入窗口(harib14c) 334
4 实现字符输入(harib14d) 337
5 符号的输入(harib14e) 341
6 大写字母与小写字母(harib14f) 343
7 对各种锁定键的支持(harib14g) 346
第18天 dir命令 350
1 控制光标闪烁(1)(harib15a) 350
2 控制光标闪烁(2)(harib15b) 352
3 对回车键的支持(harib15c) 355
4 对窗口滚动的支持(harib15d) 357
5 mem命令(harib15e) 359
6 cls命令(harib15f) 363
7 dir命令(harib15g) 366
第19天 应用程序 371
1 type命令(harib16a) 371
2 type命令改良(harib16b) 378
3 对FAT的支持(harib16c) 382
4 代码整理(harib16d) 387
5 第一个应用程序(harib16e) 387
第20天 API 392
1 程序整理(harib17a) 392
2 显示单个字符的API(1)(harib17b) 399
3 显示单个字符的API(2)(harib17c) 402
4 结束应用程序(harib17d) 403
5 不随操作系统版本而改变的API(harib17e) 405
6 为应用程序自由命名(harib17f) 408
7 当心寄存器(harib17g) 410
8 用API显示字符串(harib17h) 412
第21天 保护操作系统 418
1 攻克难题——字符串显示API(harib18a) 418
2 用C语言编写应用程序(harib18b) 420
3 保护操作系统(1)(harib18c) 424
4 保护操作系统(2)(harib18d) 426
5 对异常的支持(harib18e) 431
6 保护操作系统(3)(harib18f) 434
7 保护操作系统(4)(harib18g) 435
第22天 用C语言编写应用程序 443
1 保护操作系统(5)(harib19a) 443
2 帮助发现bug(harib19b) 448
3 强制结束应用程序(harib19c) 452
4 用C语言显示字符串(1)(harib19d) 455
5 用C语言显示字符串(2)(harib19e) 457
6 显示窗口(harib19f) 462
7 在窗口中描绘字符和方块(harib19g) 465
第23天 图形处理相关 468
1 编写malloc(harib20a) 468
2 画点(harib20b) 472
3 刷新窗口(harib20c) 475
4 画直线(harib20d) 478
5 关闭窗口(harib20e) 483
6 键盘输入API(harib20f) 484
7 用键盘输入来消遣一下(harib20g) 488
8 强制结束并关闭窗口(harib20h) 489
第24天 窗口操作 493
1 窗口切换(1)(harib21a) 493
2 窗口切换(2)(harib21b) 495
3 移动窗口(harib21c) 496
4 用鼠标关闭窗口(harib21d) 498
5 将输入切换到应用程序窗口(harib21e) 500
6 用鼠标切换输入窗口(harib21f) 506
7 定时器API(harib21g) 507
8 取消定时器(harib21h) 511
第25天 增加命令行窗口 515
1 蜂鸣器发声(harib22a) 515
2 增加更多的颜色(1)(harib22b) 518
3 增加更多的颜色(2)(harib22c) 520
4 窗口初始位置(harib22d) 523
5 增加命令行窗口(1)(harib22e) 524
6 增加命令行窗口(2)(harib22f) 528
7 增加命令行窗口(3)(harib22g) 531
8 增加命令行窗口(4)(harib22h) 532
9 变得更像真正的操作系统(1)(harib22i) 534
10 变得更像真正的操作系统(2)(harib22j) 538
第26天 为窗口移动提速 541
1 提高窗口移动速度(1)(harib23a) 541
2 提高窗口移动速度(2)(harib23b) 543
3 提高窗口移动速度(3)(harib23c) 547
4 提高窗口移动速度(4)(harib23d) 549
5 启动时只打开一个命令行窗口(harib23e) 551
6 增加更多的命令行窗口(harib23f) 554
7 关闭命令行窗口(1)(harib23g) 555
8 关闭命令行窗口(2)(harib23h) 561
9 start命令(harib23i) 563
10 ncst命令(harib23j) 564
第27天 LDT与库 571
1 先来修复bug(harib24a) 571
2 应用程序运行时关闭命令行窗口(harib24b) 573
3 保护应用程序(1)(harib24c) 577
4 保护应用程序(2)(harib24d) 580
5 优化应用程序的大小(harib24e) 583
6 库(harib24f) 587
7 整理make环境(harib24g) 590
第28天 文件操作与文字显示 598
1 alloca(1)(harib25a) 598
2 alloca(2)(harib25b) 601
3 文件操作API(harib25c) 605
4 命令行API(harib25d) 612
5 日文文字显示(1)(harib25e) 615
6 日文文字显示(2)(harib25f) 624
7 日文文字显示(3)(harib25g) 629
第29天 压缩与简单的应用程序 635
1 修复bug(harib26a) 635
2 文件压缩(harib26b) 636
3 标准函数 644
4 非矩形窗口(harib26c) 647
5 bball(harib26d) 648
6 外星人游戏(harib26e) 651
第30天 高级的应用程序 659
1 命令行计算器(harib27a) 659
2 文本阅览器(harib27b) 664
3 MML播放器(harib27c) 671
4 图片阅览器(harib27d) 679
5 IPL的改良(harib27e) 683
6 光盘启动(harib27f) 688
第31天 写在开发完成之后 690
1 继续开发要靠大家的努力 690
2 关于操作系统的大小 692
3 操作系统开发的诀窍 693
4 分享给他人使用 694
5 关于光盘中的软件 695
6 关于开源的建议 696
7 后记 698
8 毕业典礼 703
9 附录 704
多任务(1) --挑战任务切换( harib12a)
“话说,多任务到底是啥呢?”我们今天的内容,就从这个问题开始吧。
多任务,在英语中叫做 “multitask”,顾名思义就是“多个任务”的意思。简单地说,在Windows等操作系统中,多个应用程序同时运行的状态(也就是同时打开好几个窗口的状态)就叫做多任务。
对于生活在现代社会的各位来说,这种多任务简直是理所当然的事情。比如你会一边用音乐播放软件听音乐一边写邮件,邮件写到一半忽然有点东西要查,便打开 Web浏览器上网搜索。这对于大家来说这些都是家常便饭了吧。可如果没有多任务的话会怎么样呢?想写邮件的时候就必须关掉正在播放的音乐,要查东西的时候就必须先保存写到一半的邮件,然后才能打开 Web浏览器……光想象一下就会觉得太不方便了。
然而在从前,没有多任务反倒是普遍的情形(那个时候大家不用电脑听音乐,也没有互联网)。在那个年代,电脑一次只能运行一个程序,如果要同时运行多个程序的话,就得买好几台电脑才行。
就在那个时候,诞生了昀初的多任务操作系统,大家都觉得太了不起了。从现在开始,我们也要准备给“纸娃娃系统”添加执行多任务的能力了。连这样一个小不点儿操作系统都能够实现多任务,真是让人不由地感叹它生逢其时呀。
稍稍思考一下我们就会发现,多任务这个东西还真是奇妙,它究竟是怎样做到让多个程序同时运行的呢?如果我们的电脑里面装了好多个 CPU的话,同时运行多个程序倒也顺理成章,但实际上就算我们只有一个 CPU,照样可以实现多任务。
其实说穿了,这些程序根本没有在同时运行,只不过看上去好像是在同时运行一样:程序 A运行一会儿,接下来程序 B运行一会儿,再接下来轮到程序 C,然后再回到程序 A……如此反复,有点像日本忍者的“分身术”呢(笑)。
为了让这种分身术看上去更完美,需要让操作系统尽可能快地切换任务。如果 10秒才切换一次,那就连人眼都能察觉出来了,同时运行多个程序的戏码也就穿帮了。再有,如果我们给程序 C发出一个按键指令,正巧这个瞬间系统切换到了程序 A的话,我们就不得不等上 20秒,才能重新轮到程序 C对按键指令作出反应。这实在是让人抓狂啊(哭)。
在一般的操作系统中,这个切换的动作每 0.01~0.03秒就会进行一次。当然,切换的速度越快,让人觉得程序是在同时运行的效果也就越好。不过, CPU进行程序切换(我们称为“任务切换”)这个动作本身就需要消耗一定的时间,这个时间大约为 0.0001秒左右,不同的 CPU及操作系统所需的时间也有所不同。如果 CPU每0.0002秒切换一次任务的话,该 CPU处理能力的 50%都要被任务切换本身所消耗掉。这意味着,如果同时运行 2个程序,每个程序的速度就只有单独运行时的1/4,这样你会觉得开心吗?如果变成这种结果,那还不如干脆别搞多任务呢。
相比之下,即便是每 0.001秒切换一次任务,单单在任务切换上面也要消耗 CPU处理能力的 10%。大概有人会想, 10%也没什么大不了的吧?可如果你看看速度快 10%的CPU卖多少钱,说不定就会恍然大悟,“对啊,只要优化一下任务切换间隔,就相当于一分钱也不花,便换上了比现在更快的 CPU嘛……”(笑),你也就明白了浪费 10%也是很不值得的。正是因为这个原因,任务切换的间隔昀短也得 0.01秒左右,这样一来只有 1%的处理能力消耗在任务切换上,基本上就可以忽略不计了。
关于多任务是什么的问题,已经大致讲得差不多了,接下来我们来看看如何让 CPU来处理多任务。
当你向CPU发出任务切换的指令时, CPU会先把寄存器中的值全部写入内存中,这样做是为了当以后切换回这个程序的时候,可以从中断的地方继续运行。接下来,为了运行下一个程序, CPU会把所有寄存器中的值从内存中读取出来(当然,这个读取的地址和刚刚写入的地址一定是不同的,不然就相当于什么都没变嘛),这样就完成了一次切换。我们前面所说的任务切换所需要的时间,正是对内存进行写入和读取操作所消耗的时间。
接下来我们来看看寄存器中的内容是怎样写入内存里去的。下面这个结构叫做“任务状态段”(task status segment),简称TSS。TSS有16位和32位两个版本,这里我们使用 32位版。顾名思义, TSS也是内存段的一种,需要在 GDT中进行定义后使用。
参考上面的结构定义,TSS共包含26个int成员,总计 104字节(摘自 CPU的技术资料),我特意把它们分成 4行来写。从开头的 backlink起,到 cr3为止的几个成员,保存的不是寄存器的数据,而是与任务设置相关的信息,在执行任务切换的时候这些成员不会被写入( backlink除外,某些情况下是会被写入的)。后面的部分中我们会用到这里的设定,不过现在你完全可以先忽略它。
第2行的成员是 32位寄存器,第 3行是16位寄存器,应该没必要解释了吧……不对, eip好像到现在还没讲过呢。 EIP的全称是“extended instruction pointer”,也就是“扩展指令指针寄存器”的意思。这里的“扩展”代表它是一个 32位寄存器,也就是说其对应的 16位版本叫做 IP,类比一下的话,跟 EAX与AX之间的关系是一样的。
EIP是CPU用来记录下一条需要执行的指令位于内存中哪个地址的寄存器,因此它才被称为
“指令指针”。如果没有这个寄存器,记性不好的 CPU就会忘记自己正在运行哪里的程序,于是程序就没办法正常运行了。每执行一条指令, EIP寄存器中的值就会自动累加,从而保证一直指向下一条指令所在的内存地址。
说点题外话, JMP指令实际上是一个向 EIP寄存器赋值的指令。 JMP 0x1234这种写法, CPU会解释为 MOV EIP,0x1234,并向 EIP赋值。也就是说,这条指令其实是篡改了 CPU记忆中下一条该执行的指令的地址,蒙了 CPU一把。这样一来, CPU在读取下一条指令时,就会去读取 0x1234这个地址中的指令。你看,这不就相当于是做了一个跳转吗?
对了,如果你在汇编语言里用 MOV EIP,0x1234这种写法是会出错的,还是不要尝试的好。在汇编语言中,应该使用 JMP 0x1234来代替MOV EIP,0x1234。
如果在TSS中将EIP寄存器的值记录下来,那么当下次再返回这个任务的时候, CPU就可以明白应该从哪里读取程序来运行了。
按照常识,段寄存器应该是 16位的才对,可是在 TSS数据结构中却定义成了 int(也就是 DWORD)类型。我们可以大胆想象一下,说不定英特尔公司的人将来会把段寄存器变成 32位的,这样想想也挺有意思的呢(笑)。
第4行的ldtr和iomap也和第1行的成员一样,是有关任务设置的部分,因此在任务切换时不会被CPU写入。也许你会想,那就和第 1行一样,暂时先忽略好了——但那可是绝对不行的!如果胡乱赋值的话,任务就无法正常切换了,在这里我们先将ldtr置为0,将iomap置为0x40000000就好了。
关于TSS的话题暂且先告一段落,我们回来继续讲任务切换的方法。要进行任务切换,其实还得用JMP指令。 JMP指令分为两种,只改写 EIP的称为near模式,同时改写 EIP和CS的称为far模式,在此之前我们使用的 JMP指令基本上都是 near模式的。不记得 CS是什么了? CS就是代码段(code segment)寄存器啦。
说起来我们其实用过一次 far模式的JMP指令,就在 asmhead.nas的“bootpack启动”的昀后一句(见8.5节)。 MP DWORD 2*8:0x0000001b 这条指令在向 EIP存入0x1b的同时,将CS置为2*8(=16)。像这样在 JMP目标地址中带冒号( :)的,就是far模式的 JMP指令。
如果一条 JMP指令所指定的目标地址段不是可执行的代码,而是 TSS的话, CPU就不会执行通常的改写 EIP和CS的操作,而是将这条指令理解为任务切换。也就是说, CPU会切换到目标 TSS所指定的任务,说白了,就是 JMP到一个任务那里去了。
CPU每次执行带有段地址的指令时,都会去确认一下 GDT中的设置,以便判断接下来要执行的 JMP指令到底是普通的 far-JMP,还是任务切换。也就是说,从汇编程序翻译出来的机器语言来看,普通的 far-JMP和任务切换的 far-JMP,指令本身是没有任何区别的。
好了,枯燥的讲解就到这里,让我们实际做一次任务切换吧。我们准备两个任务:任务 A和任务B,尝试从 A切换到B。
现在两个TSS都创建好了,该进行实际的切换了。
我们向TR寄存器存入3 * 8这个值,这是因为我们刚才把当前运行的任务定义为 GDT的3号。 TR寄存器以前没有提到过,它的作用是让 CPU记住当前正在运行哪一个任务。当进行任务切换的时候,TR寄存器的值也会自动变化,它的名字也就是“ task register”(任务寄存器)的缩写。我们每次给 TR寄存器赋值的时候,必须把 GDT的编号乘以 8,因为英特尔公司就是这样规定的。如果你有意见的话,可以打电话找英特尔的大叔投诉哦(笑)。
给TR寄存器赋值需要使用 LTR指令,不过用 C语言做不到。唉,各位是不是都已经见怪不怪了啊?啥?你早就料到了?(笑)所以说,正如你所料,我们只能把它写进 naskfunc.nas里面。
对了,LTR指令的作用只是改变 TR寄存器的值,因此执行了 LTR指令并不会发生任务切换。要进行任务切换,我们必须执行 far模式的跳转指令,可惜 far跳转这事 C语言还是无能为力,这种语言还真是不方便啊。没办法,这个函数我们也得在 naskfunc.nas里创建。
也许有人会问,在 JMP指令后面写个 RET有意义吗?也对,通常情况下确实没意义,因为已经跳转到别的地方了嘛,后面再写什么指令也不会被执行了。不过,用作任务切换的 JMP指令却不太一样,在切换任务之后,再返回这个任务的时候,程序会从这条 JMP指令之后恢复运行,也就是执行JMP后面的RET,从汇编语言函数返回,继续运行 C语言主程序。
另外,如果 far-JMP指令是用作任务切换的话,地址段(冒号前面的 4*8的部分)要指向 TSS这一点比较重要,而偏移量(冒号后面的 0的部分)并没有什么实际作用,会被忽略掉,一般来说像这样写 0就可以了。
现在我们需要在 HariMain的某个地方来调用 taskswitch(),可到底该写在哪里呢?唔,有了,就放在显示“ 10[sec]”的语句后面好了。也就是说,程序启动 10秒以后进行任务切换。
大功告成了?不对,我们还没准备好 tss_b呢。在任务切换的时候需要读取 tss_b的内容,因此我们得在TSS中定义好寄存器的初始值才行。
乍看之下,貌似会有很多看不懂的地方吧,我们从后半段对寄存器赋值的地方开始看。这里我们给cs置为GDT的2号,其他寄存器都置为 GDT的1号,asmhead.nas的时候也是一样的。也就是说,我们这次使用了和bootpack.c相同的地址段。当然,如果你用别的地址段也没问题,不过这次我们只是想随便做个任务切换的实验而已,这种麻烦的事情还是以后再说吧。
继续看剩下的部分,关于 eflags的赋值,如果把 STI后的EFLAGS的值通过 io_load_eflags赋给变量的话,该变量的值就显示为 0x00000202,因此在这里就直接使用了这个值,仅此而已。如果还有看不懂的地方,大概就是 eip和esp的部分了吧。
。。。。。。
在eip中,我们需要定义在切换到这个任务的时候,要从哪里开始运行。在这里我们先把 task_b_main这个函数的内存地址赋值给它。
这个函数只执行了一个 HLT,没有任何实际作用,后面我们会对它进行各种改造,现在就先这样吧。
task_b_esp是专门为任务 B所定义的栈。有人可能会说,直接用任务 A的栈不就好了吗?那可不行,如果真这么做的话,栈就会混成一团,程序也无法正常运行。
总之先写成这个样子了。我们为任务B的栈分配了64KB的内存,并计算出栈底的内存地址。
请各位回忆一下向栈PUSH数据(入栈)的动作,ESP中存入的应该栈末尾的地址,而不是栈开
头的地址。
好了,我们已经讲解得够多了,现在总算是万事俱备啦,马上“make run”一下吧。这个程
序如果运行正常的话应该是什么样子呢?嗯,启动之后的10秒内,还是跟以前一样的,10秒一到便执行任务切换,task_b_main开始运行。因为task_b_main只有一句HLT,所以接下来程序就全部停止了,鼠标和键盘也应该都没有反应了。
唔……这样看起来好像很无聊啊,算了,总之我们先来“make run”吧。10秒钟的等待还真
是漫长……哇!停了停了!
看来我们的首次任务切换获得了圆满成功。
---------------------------------------------------------------------------------------
1.诚邀合作: 如果您想翻译、写作或是推荐IT类图书,我们将竭诚为您提供最专业的服务平台。
2.联系我们: yuany@turingbook.com(E-Mail)
3.欢迎加入图灵社区与作译者、业内人士进行交流:http://www.ituring.com.cn/article