Linux从头学13:想彻底搞懂“系统调用”的底层原理?建议您别错过这篇【调用门】
扫描二维码
随时随地手机看文章
作 者:道哥,10 年嵌入式开发老兵,专注于:C/C 、嵌入式、Linux。目录关注下方公众号,回复【书籍】,获取 Linux、嵌入式领域经典书籍;回复【PDF】,获取所有原创文章( PDF 格式)。
-
门描述符
-
调用门特权级检查规则
-
调用门的使用过程
-
安装调用门
-
把调用门告诉用户程序
-
用户程序通过调用门进入系统函数
-
栈在不同特权级下的切换
例如:应用程序的 CPL 和 RPL 都为 3,而操作系统中的函数所在的段 DPL = 0,不能通过特权级的检查。看过上一篇文章的小伙伴一定知道,如果把目标代码段的描述符中,TYPE.C标志设置为1,也就意味着这是一个依从(或者叫一致性)代码段,就允许低特权级的用户程序调用了。
门描述符
所谓的门,就是一个通道。通过这个通道,可以进入另一个代码段中进行执行。
调用门:用于低特权级代码转移到高特权级代码;门描述符与之前介绍的段描述符本质是一样的,都是用来描述一个代码段的信息,只不过门描述符增加了一层间接性。任务门:用于不同任务之间的调度;
中断门:用于异步执行中断处理程序;
陷阱门:也用于执行中断处理程序,不过这里的中断是处理器内部产生的;
- 所谓的任务门可以简单理解为用于任务切换。
- 因为一个 TSS 段中,保存的就是一个任务的上下文信息快照。
- 只要处理器发现选择子指向的描述符是一个任务门(通过 TYPE 字段),它就执行任务切换:
a. 保存当前 CPU 中的上下文到当前任务的 TSS 段中;b. 再把 TSS 选择子中所指向的那个 TSS 段中的上下文内容,加载到 CPU 寄存器中,这样就实现了任务切换。
调用门特权级检查规则
从调用门的名字就可以看出,它是为系统调用服务的。
参数个数:调用者传递多少个参数给目标代码(是通过栈空间来传参的);从以上这些字段来看,这简直就是为:从低特权级的用户代码,调用高特权级的操作系统代码,量身定做的,只要处理器在特权级上放过用户程序一马就可以了。DPL:表示这个调用门本身的特权级;
目标代码段选择子:最终调用的目标代码段的选择子,需要用这个选择子到 GDT 中寻找目标代码段的基地址;
偏移量:调用的代码距离目标代码段开始地址的偏移字节数;
从以上规则可以再次看出:即使通过调用门,目标代码段只允许相同或者更低的特权级代码进入,也验证了之前所说的:高特权级代码不会主动转移到低特权级的代码中。
- 当前特权级 CPL (用户程序)和请求特权级 RPL,必须 [高于或等于] 调用门中的 DPL;
即在数值上:CPL <= DPL,RPL <= DPL。(注意:这是调用门描述符里的 DPL)
- 当前特权级 CPL(用户程序),必须 [低于或等于] 目标代码段中的 DPL;
即在数值上:CPL >= 目标代码段描述符中的 DPL。
TYPE.C = 1:CPL 保持不变,仍然为用户程序中的特权级 3;TYPE.C = 0: CPL 改变,变成目标代码段的特权级;
调用门的使用过程
安装调用门
所谓的安装,就是在GDT中构造一个调用门描述符,让它的目标代码段选择子指向真正的代码段。
把调用门的选择子告诉用户程序
按照之前的惯例,操作系统可以在用户程序的头部header中的约定位置处,填写调用们的选择子以及函数偏移地址:
RPL = 3;到 GDT 中去查找;
索引号 index = 8;
用户程序通过调用门进入系统函数
当用户程序请求调用系统函数时,处理器就开始对这3 方的特权级展开检查:
以上这些特权级的数值满足调用门的特权级规则要求,于是就进入系统函数所在的代码中执行了。
- 用户程序的 CPL = 3, RPL = 3;
- 调用门自身的 DPL = 3;
- 调用门中的目标代码段选择子所指向的描述符(index = 7)中 DPL = 0;
栈的切换
x86处理器要求:当前特权级 CPL 必须与目标栈段的 DPL 相同。
------ End ------