学习笔记
《x86汇编语言:从实模式到保护模式》
https://www.jianshu.com/p/d481cb547e9f
复习
代码运行后的内存状态
任务切换过程中NT位、B位以及TSS指针域变化规则
思考:为什么需要 子程序 terminate_current_task
?
子程序 terminate_current_task 处理任务切换
- 源码(位于内核程序
c15_core.asm
)
terminate_current_task: ;终止当前任务
;注意,执行此例程时,当前任务仍在运行中
;此例程其实也是当前任务的一部分
pushfd
mov edx,[esp] ;获得EFLAGS寄存器内容
add esp,4 ;恢复堆栈指针
mov eax,core_data_seg_sel ;内核数据段选择子
mov ds,eax
test dx,0100_0000_0000_0000B ;测试(EFLAGS寄存器)NT位
jnz .b1 ;当前任务是嵌套的,到.b1执行iretd
mov ebx,core_msg1 ;jmp
call sys_routine_seg_sel:put_string
jmp far [prgman_tss] ;prgman_tss 程序管理器基地址和选择子
.b1:
mov ebx,core_msg0 ;call
call sys_routine_seg_sel:put_string
iretd
- 标号
prgman_tss
定义
----------- 位于 内核程序 数据段 core_data ---------------
;程序管理器的任务信息
prgman_tss dd 0 ;程序管理器的TSS基地址
dw 0 ;程序管理器的TSS描述符选择子
填写
----------- 位于 内核程序 start 标号后 ----------------
mov [prgman_tss+0x00],ecx ;保存程序管理器的TSS基地址
mov [prgman_tss+0x04],cx ;保存程序管理器的TSS描述符选择子
回头看看第14章
- 即在问:为什么不能像 第14章 那样指定返回到内核程序某个具体标号处?
第14章
内核程序参考源码 https://www.jianshu.com/p/1e27f7e6b68e
用户程序参考源码 https://www.jianshu.com/p/17a28f3dcda4
- 第14章,用做返回点的具体标号如下所示:
return_point: ;用户程序返回点
...
...
hlt
- 第14章,用户程序进行返回的指令如下:
call far [fs:TerminateProgram]
- 第14章,任务从用户程序返回到内核程序的原理是什么?使用调用门(首先是,在内核程序写入了返回点的地址,然后是,加载用户程序时,在用户程序头部段重定位了SALT表)
----------- 代码位于 内核程序 数据段 core_data -----------------
salt_4 db '@TerminateProgram'
times 256-($-salt_4) db 0
dd return_point
dw core_code_seg_sel
可以清楚地看到 这里的偏移地址就是 return_point
----------- 代码位于 用户程序 头部段 header --------------
TerminateProgram db '@TerminateProgram'
times 256-($-TerminateProgram) db 0
加载用户程序时,就会完成 SALT的回写,不用在意这里占位用的0
- 第14章的情况是,只有一个任务,即用户程序产生的任务,只有一次返回,来自用户程序返回到内核程序标号
return_point
处的返回;
现在看看本章,第15章
-
第15章的任务切换:TSS 记录 旧任务的 EIP 找到返回点(即执行任务切换前 执行到的下一条语句;
- 此时标号
TerminateProgram
的偏移地址正是子程序terminate_current_task
的入口
----------- 代码位于 内核程序 数据段 core_data-----------------
salt_4 db '@TerminateProgram'
times 256-($-salt_4) db 0
dd terminate_current_task
dw sys_routine_seg_sel
----------- 代码位于 用户程序 头部段 header --------------
TerminateProgram db '@TerminateProgram'
times 256-($-TerminateProgram) db 0
同样的,加载用户程序时,就完成了SALT的回写,只不过这一次的返回是返回到内核程序的子程序
terminate_current_task
处,再经由子程序自己做判断了;子程序
terminate_current_task
的作用在于区分这是来自于call还是jmp指令的返回
如果是call指令的返回需要一条`iretd`指与之搭配,再从TSS中恢复相关的状态;
如果是jmp指令,直接找到TSS,中恢复状态;