以下答案仅代表个人观点。若读者朋友对习题答案有疑问、有想法,欢迎随时留言!
1. 习题
第20章一共有两个习题,分别是:
1.1 习题1
1. 在本章的多任务系统中只用了一个TSS,任务切换时,用新任务的0特权级栈指针替换这个TSS的ESP0域,而不是使用一个固定值。请用一个具体的情景说明,如果不这样做会有什么问题。
答案:0特权级栈是属于用户任务的局部空间下的一段内存(0特权级权限),故要将TSS的ESP0域替换成新任务的ESP0。在代码中,如下图所示,每个用户任务都会创建一个0特权级栈,这个栈是系统api调用时需要的。
1.2 习题2
2. 在本章中,任务切换是借助于 iretd 指令发起的。请想一想、试一试,看还有没有别的办法可以用来执行软件任务切换。
答案:ret/retf、iret 他们都有一个共同点,就是最后,处理器会加载SS和ESP,切换到调用者堆栈;也就是说 iret、retf 发现控制权在不同的特权级之间转移时,会进行栈的恢复(因为call、中断发生时进行了栈切换)。
注意,必须使用 retf,因为CS也要弹出栈,而 ret 只弹出 EIP。
下图的是具体的改动点:
2. 完结感悟
从第一次粗略阅读,到第二次手动实现汇编代码,一共用掉将近一个月的时间。使用纯汇编实现一个玩具“操作系统”的过程很“痛苦”,特别是调试的过程,代码几乎没有一次就通过的,基本上要 debug 好多小时,在这个过程中有以下感受。
- 越底层关注的细节越多,针对当前逻辑汇编而言,不仅要关注功能的实现逻辑,还要关注硬件细节,面向内存中的数据。关注的细节越多越容易干扰功能的实现。
- 对于操作系统的编写者来说,分段机制非常非常麻烦,每次进行内存read/write都要时时刻刻关注DS、ES等段寄存器的内容,而且还要安装段描述符,太麻烦。
- 平坦内存模型大大简化了内存设计,而且不用关注段寄存器的内容,大大减轻了心理负担。
-
时时刻刻要注意某个寄存器的内容是否被覆盖了,特别是段寄存器和esi、edi、这些用来做地址偏移量的寄存器
-
写复杂的功能,通用寄存器不够用,来回暂存寄存器的内容,非常麻烦。
-
汇编写代码真的非常容易出错,关注的细节很多,偏移量、标号、跳转都很容易出错。
-
裸机汇编,debug非常难,直接面向内存查看数据。
-
代码维护更加困难,通常是改一处,其他地方都要修改,而且会很容易忽略其他地方的修改。
以上就是一些感受,现在想想用高级编程语言真的是一件幸福的事情。
不过天下没有免费的午餐,这些便利是以牺牲性能为代价的,不过话又说回来了,性能这件事看需求;