本章目标
- 了解保护模式是为操作系统提供的技术,并没有给普通应用程序的编程带来负担。
- 学习操作系统在保护模式下加载和重定位应用程序的一般原理,学习简单的内存动态分配,了解应用程序接口API的简单原理,学习字符串的比较算法。
- 学习若干x86处理器的新指令,包括bswap,cpuid,cmovcc,sgdt,movzx,movsx,cmpsb,cmpsw,cmpsd和xlat等。
13.1 代码清单
c13_mbr.asm
c13_core.asm
c13.asm
13.2 内核的结构,功能和加载
13.2.1 内核的结构
内核代码分为4个部分,分别是初始化代码,内核代码段,内核数据段,公共例程段,主引导程序也是初始化代码的组成部分
13.2.2 内核的加载
定义全局描述符后(GDT)后,将内核代码从硬盘读到内存中
13.2.3 安装内核的段描述符
就是根据各个段的起始地址,结束地址组装成一个段描述符,然后写到GDT中
13.3 在内核中执行
内核代码的主要执行流程:打印欢迎信息到界面上
13.3.1 cpuid指令(CPU Identification)
用于返回处理器的标识和特性信息。
- EAX用于指定要返回什么样的信息,也就是功能,有时还要用到ECX寄存器
- 返回结果放在EAX、EBX、ECX或者EDX中
例如:
mov eax, 0 cpuid
EAX中写0表示要探测处理器最大能支持的功能号。 处理器执行后,将在EAX寄存器返回最大可以支持的功能号。同时,还在EBX、ECX和EDX中返回处理器供应商的信息。对于Intel处理器,返回的信息如下:
EBX <----- 0x756E6547 (对应字符串“Genu”,“G”在BL中,其他类推) EDX <----- 0x49656E69 (对应字符串“ineI”,“i”在DL中,其他类推) ECX <----- 0x6C65746E (对应字符创“ntel”,“n”在CL中,其他类推)
13.4 用户程序的加载和重定位
内核的主要任务就是加载和执行用户程序
13.4.1 用户程序的结构
文件头是必须的。 用户程序必须符合规定的格式,才能被内核识别和加载。通常,流行的操作系统会规定自己的可执行文件格式,一般比较复杂,这种复杂性和操作系统自身的复杂性是息息相关的。即文件头。 事实上,即使是大多数的汇编语言,也不需要亲自构造文件头,那是链接器的工作。
13.4.2 计算用户程序占用的扇区数量
解释内13-2的内核代码load_relocate_program部分代码
cmovcc指令
- 条件传送指令是条件转移指令和传送指令相结合的产物。
- 目的操作数:只允许16位或者32位通用寄存器
- 源操作数:和目的操作数相同宽度的通用寄存器和内存单元
cmovz ax, cx ;为零则传送 cmovnz eax, [0x2000] ;不为零则传送 cmove ebx, ecx ;相等则传送 cmovng cx, [0x1000] ;不大于则传送 cmovl edx, ecx ;小于则传送
13.4.3 简单的动态内存分配
将用户程序从硬盘读到内存中之后,需要制定一个内存区域,然后把程序加载那里。如果要加载的程序很多,这就会成为一个需要仔细规划的工作,这就是内存管理
。
在流行的操作系统里,内存管理是一项重要而又严肃的工作。
13.4.4 段的重定位和描述符的创建
读取用户程序头部信息,根据这些信息创建头部段描述符。 唯一不同的是栈段,栈所用的空间不需要用户程序提供,而是内核动态分配。
sgdt指令(Store Global Descriptor Table Register)
- 用于将GDTR寄存器的基地址和边界信息保存到制定的内存位置
- 指令格式
sgdt m ;m是一个6字节内存区域的首地址。该地址不影响任何标志位。
movzx指令(Move with Zero-Extend)
- 带零扩展的传送
- 目的操作数:16位或者32位的通用寄存器
- 源操作数:8位或者16位的通用寄存器,8位或者16位内存单元地址
movzx r16, r/m8 movzx r32, r/m8 movzx r32, r/m16
movsx指令(Move with Sign-Extension)
- 带符号扩展的传送
movsx r16, r/m8 movsx r32, r/m8 movsx r32, r/m16
13.4.5 重定位用户程序内的符号地址
在用户程序头部的符号地址处,填入该内核函数在内核中的位置,即重定位。
cmps指令(Compare String Operands)
- 字符串比较指令
- 16位模式下,源字符串的首地址由DS:SI指定,目的字符串的首地址由ES:DI指定;在32位模式下,则分别是DS:ESI和ES:EDI
- cmp指令的操作是把两个操作数相减,然后根据结果设置标志寄存器中相应的标志位
- 标志寄存器EFLAGS中的DF位决定了字符串的比较方向:DF=0表明是正向比较;DF=1表明是反向比较
- cmps指令,需要加指令前缀rep进行连续比较,连续次数由CX(ECX)寄存器控制
repe/repz用于搜索第一个不匹配的字节、字或者双字,repne/repnz用于搜索第一个匹配的字节、字或者双字。无论如何,匹配和不匹配的位置分别由(E)SI和(E)DI寄存器指示
13.5 执行用户程序
用户程序执行完成之后,还要调用内核的过程,以返回到内核。 对于操作系统而言,之后的任务是回收前一个用户程序所占用的内存,并启动下一个用户程序。
13.6 代码的编译、运行和调试
// TODO