1、为系统应用程序增加系统调用接口
将进程切换到level3后,进程对操作系统的代码和数据访问不能直接进行,权限不够。具体表现为会检查CS和DS中相关的权限位,当发现权限不够时,硬件将会触发异常,然后由操作系统处理这个异常,比如杀死进程。
此外,前面设置的分页机制中也支持权限处理,即通过U/S位设置是否允许应用程序是否访问。当其值为0时,限定只允许操作系统访问。
系统调用并不只是执行代码,还需要请求操作系统完成某些工作,比如读取磁盘上的文件。采x86硬件提供的调用门去完成,通过调用门,应用程序能够请求操作系统调用其内部的某个函数。
具体流程:
- 配置GDT表:在GDT表中配置好相应的调用门描述符。包括调用门权限、不同特权级之间拷贝的参数个数、被调用函数所在代码段、被调用函数所在代码段中的偏移。
- 执行系统调用:使用Call指令,硬件会自动从调用门中取出操作系统中相应函数的入口地址
- 参数传递:自动压栈,同时从原调用栈中拷贝参数到新栈
- 执行被调函数
- 执行ret指令返回
在使用之前,需要先在GDT表中配置好相应的调用门描述符
当执行系统调用时,使用call指令即可,硬件会自动从调用门中取出操作系统中相应函数的入口地址
与此同时,还会进行参数的传递。参数传递的过程与【前置课】中介绍的一致。
采用调用门的好处就是硬件会自动将参数复制到内核中。在向被调用的操作系统
2、实现sys_sleep系统调用
由于只使用一个调用,因此需要通过额外的参数来指示当前要调用的是那个系统函数。具体的话是:定义了一个sys_table来存储系统调用的函数表。然后在执行系统调用时,从传入的参数中获得需要调用的函数对应的id。再利用该id查找表取得函数入口指针,进而调用该函数。
为了取得系统调用传入的函数,课程定义了syscall_frame_t结构来解析栈中的寄存器和参数值,通过该结构体,即可获得系统调用传入的参数,以及当前执行系统调用时的状态。
根据GCC对函数返回值的处理规则:函数调用的返回值保存在eax寄存器中,在系统调用之后,执行frame->eax=ret,用于将执行结果返回给应用程序。
3、使用int $0x80 实现系统调用
实现系统调用除了使用调用门之外,还有其他方式:比如任务门、中断门。因此,可以自行执行int序号来触发一个中断门对应的处理程序执行。
使用向量号为0x80的中断门,当需要执行系统调用时,执行int 0x80指令,参数也是通过寄存器传递。调用门使用压栈和出栈来实现