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,“进入内核”(是怎么做到的?下面笔记有解释)- 这是用户程序发起的调用内核代码的唯一方式
系统调用的核心
- 用户程序中包含一段包含t指令的代码(库函数)
- 操作系统写中断处理,获取想调程序的编号(所有的中断处理代码都是操作系统提前设置的)
- 操作系统根据编号执行相应代码
以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 总结
加油