• 哈工大李治军老师操作系统笔记【4】:系统调用的实现(Learning OS Concepts By Coding Them !)


    0 系统调用的直观实现


    在这里插入图片描述


    • 从一个 W h o a m I WhoamI WhoamI的问题引入,系统调用打印用户名,用户名这个字符串是在内核当中的,因为要进到内核当中,所以这是系统调用
    • 当想把用户名从内核当中打出,为什么不能简单的调用printf()这条指令呢?
    • APP在内存中,OS也在内存中,APP想访问OS提供的内容为什么不能直接进行呢?直接jmp或者直接mov为什么不行?OS当中有很多很重要的东西,比如 r o o t root root用户的密码,所以会造成不安全的隐患
    • 任何一种输出数据到外设的系统调用,在某个时刻,这些数据会在操作系统内核的缓冲区中,这个时候就可能被泄漏,比如可以通过缓冲区或者显存看到 w o r d word word里面的内容,这些都是不太安全的,所以尽管都在内存当中,不让mov/jmp的原因

    1 内核态与用户态

    接上方,凭什么不让APP使用jmp指令到内存当中呢?因为硬件电路的原因,只有硬件电路有能力让你“不jmp/mov”。


    在这里插入图片描述


    • 区分内核态和用户态是一种处理器的“硬件设计”,从硬件的层面保证了这个机制(操作系统的很多功能都是需要依靠硬件设计来实现的,软硬件结合)
    • 内核态可以问任何数据,用户态不能访问内核数据:对于指令跳转也是一样
    • 硬件把内存分割成了很多区域(如上图的蓝色与红色的区域,生成了用户段与内核段,如上图的蓝色与红色区域代码),实现了内核与APP(用户程序)的隔离
    • DPL,CPL,RPL,都是基于硬件实现的
    • 就是比较当前态的特权级和目标段的特权级,这个特权级数字的含义见上图中的处理器保护环,数字越小,越接近内核
    • CPL就是CS的最低两位,DPL可以从GDT表中查到,在保护模式下指令地址的翻译是查GDT表,那么这个时候就可以查到目标指令的DPL,和当前态的特权级CPL比较,如果DPL>=CPL,那就说明当前态的特权级足以执行目标指令,否则就不允许执行
    • DPL:DPL是用来描述目标内存段的特权级的(PL就是特权级,D就是目标),例如上方的红色段代码,DPL就在GDT表中,一个GDT的表项就用来描述一段内存,初始化都置成0
    • CPL:当前的特权级,取决于执行的是什么指令,因为指令是PC,PC又是CS:IP组成的,所以CS当中的一部分就用来表示特权级, 什么内核段/用户段,都得靠段寄存器来做,CS:IP是指向当前要执行的指令地址,当前程序处于内核态还是用户态,用CS:IP的最低两位来表示,0是内核态,3是用户态,当前特权级为3,只能访问3,但是是0,就都可以访问,那1,2呢?

    1.1 硬件提供了进入内核态的方法


    在这里插入图片描述


    • 对于Intel x86,那就是中断指令int
    • int指令将使CS中的CPL改成0,“进入内核”(是怎么做到的?下面笔记有解释)
    • 这是用户程序发起的调用内核代码的唯一方式

    系统调用的核心

    1. 用户程序中包含一段包含t指令的代码(库函数)
    2. 操作系统写中断处理,获取想调程序的编号(所有的中断处理代码都是操作系统提前设置的)
    3. 操作系统根据编号执行相应代码

    以C代码库编写的系统调用,在用户程序调用后,会首先进入C代码库函数,然后用汇编代码在约定的位置(栈或者寄存器)设置参数和系统调用编号,最后执行int指令。


    在这里插入图片描述


    1.2 write


    在这里插入图片描述


    • 这是一段C语言内嵌汇编代码
    • 获取_NR_write,这是write的系统调用编号,将它放在eax寄存器中,参数放在ebX、ecx、edx中
    • 然后执行int0x80指令,这个中断执行后,会把返回值放在eax寄存器中
    • eax寄存器的值会赋给res,最终返回res

    1.3 int 0x80

    • int 0x80,需要在OS初始化的时候寻找线索
    • IDT表和GDT表的结构比较像,是特别用于中断处理函数寻址的表可以根据DT表找到这个中断的处理函数的入口地址
    • 下图展示的是操作系统初始化的时候,与it0x80和IDT表初始化相关的代码
    • 下图中,set_system_gatel函数就是在设置DT表,其两个参数说明了0x80中断的处理函数入口地址就是&system_cal,也就是说0x80这个中断专门用于表示系统调用
    • 下图中,可以看到IDT表中也有DPL,且当前情况下DPL被设置成了3,所以int 0x80中断可以在用户态下调用(总结一句话,系统调用对应的中断itOx80位于IDT表中的DPL=3,可以在用户态下调用)
    • 一旦调用int Ox80,CPL就会被置为0,因为这个中断的处理函数本身就在内核空间,那么CS自然就是内核段,那么CPL自然就变成0了
    • 将来中断返回的时候,可以肯定CPL会变回3

    在这里插入图片描述


    • 系统调用对应的中断处理程序system_call
    • ds=es=0x10,表示将数据段置为内核的数据段地址
    • 其中调用了_sys_call_table的第4项

    在这里插入图片描述


    最后的一行sys代码,类似于目录,因为寻址方式,每个地址只需要4个字节就可以表示。所以这里是4。


    在这里插入图片描述


    在这里插入图片描述


    2 总结

    加油

  • 相关阅读:
    在Langchain中使用ConversationSummaryMemory给链增加记忆
    1033 旧键盘打字
    传播问卷调查数据不够?自己生成假数据!
    链接加密逆向
    温湿度计传感器DHT11控制数码管显示verilog代码及视频
    学习编程的目的
    L2-021 点赞狂魔(Python3)
    pytest与unittest对比
    双十二选什么牌子的led台灯质量好?2022最新几款真的能护眼的台灯推荐
    了解 Flutter 开发者们的 IDE 使用情况
  • 原文地址:https://blog.csdn.net/weixin_44673253/article/details/126669205