1、隔离操作系统与进程
主要目标就是将原来运行在0x80000000以下init_task_entry移到0x80000000。后续所有的、】非特权级的进程都运行在0x80000000以上。主要分成三部分工作:
1)代码分离
1)将进程first_task的代码与内核代码从文件上分离,但是由于没有文件系统,二者仍然是一同编译的,所以最终汇编出来的文件中,first_task和内核的代码是混在一起的。
2)告诉编译器工具链first_task是运行在虚拟地址0x80000000以上。
因为loader加载内核时,它并不知道first_task的存在,因此无法将first_task的代码正确加载到0x800000000上开始运行。
2)配置加载地址和运行地址
一个程序在编译时,其中的代码和数据等会被划分为一下几块: .text(放置代码) .rodata(放置常量) .data .bss 还有堆和栈。这些区域的分布,可以通过链接脚本来配置,通过kernel.lds中的配置,可以控制这些代码和数据在内存中应该存在的位置。
GCC编译器在编译工程时,会依据lds中的文件,在生成的ELF文件中包含相应的地址信息(位于program header)
由于first_task 是在kernel.elf中,且加载kernel.elf时,虚拟内存未启用,所以只能和kernel中其它代码一同加载到1MB以下。当first_task运行之前需要将其搬运到0x80000000上运行。因此涉及两个地址:加载地址,运行地址。
对于进程运行,由于其不是从磁盘加载而是从内存加载,需要在运行前,拷贝到指定的虚拟地址空间。另外,在链接脚本中我们可以指定.text / .data等对应的加载地址和运行地址,示例如下
3)搬运代码
在kernel.elf被加载到内存中后,first_task中的所有的代码和数据还只存在于1MB以下的内存中。在运行之前,还需要将其搬却被到0x80000000以上。
为什么不直接让first_task编译成一个独立的可执行程序放在磁盘上运行呢?
这是完全可以的。只不过这里涉及磁盘读取和文件系统的处理,有些复杂。此处并未涉及到这些知识点。
2、调整应用特权级
1)四个权限
从0-3,数值越大,权限越小。操作系统运行于最高权限0,应用程序运行于最低权限3。权限1和权限2可用于运行一引起其它服务
2)与权限相关的配置位
段描述符中的DPL(2位):段描述符中的特权级别,表示一个段的访问权限。值
段选择子RPL(2位):指定当前任务请求的特权级别。
段寄存器CS中的CPL:用于表示当前正在执行的代码的特权级别
DPL、RPL和CPL它的值范围从 0
到 3
,其中 0
表示最高特权级别(内核模式),3
表示最低特权级别(用户模式)
简化设计:将操作系统的CPL、DPL、RPL全部设置成0,应用程序的全部设置成3
采用权限分离后,会使用分页机制的权限来处理内存访问上的保护。即利用页表中的权限。
页表中的权限配置:
R/W 位(Read/Write):1位,控制页面的可写权限。R/W=0表示页面只读;R/W=1表示页面可读写。
U/S 位(User/Supervisor):1位,控制用户模式(Ring 3)和超级用户模式(Ring 0)对页面的访问权限
因此页表权限设置采用U/S来完成。
3、相应的特权级调整
1)调整异常处理函数
2)修该空闲任务的特权级
由于空闲任务需要执行CPU停机指令HLT,需要较高的权限,需要工作在特权级0下。
具体实现方法是:
在初始化TSS时,根据任务是否是系统任务,设置相应的权限。
int code_sel, data_sel;
if (flag & TASK_FLAG_SYSTEM) { //判断是否是系统任务
code_sel = KERNEL_SELECTOR_CS;
data_sel = KERNEL_SELECTOR_DS;
} else {
// 注意加了RP3,不然将产生段保护错误
code_sel = task_manager.app_code_sel | SEG_RPL3;
data_sel = task_manager.app_data_sel | SEG_RPL3;
}
4、特权级切换
1)切换至用户特权级为最低
init_main运行在权限级0的模式下,而first_task需要运行在特权级3,因此这里涉及到从高特权级切换到低特权级的情况。
修改了init_main中的跳转代码,不再采用jmp指令直接跳转到first_task,而是采用iret特权级返回的方式。
由于权限发生了变化,因此栈也发生了变化(硬件自动处理),会有一系列的弹栈过程。
2)为进程添加特权级0的栈空间
应用程序运行时需要使用两种栈:
- 用于运行应用程序自己代码的特权级3对应的栈,
- 后续运行系统调用、中断等高特权级0对应的栈