操作系统接口
目录
操作系统接口连接的不是用户
- 用户通过命令行、图形按钮、应用程序等使用计算机,shell和图形界面本质也是程序,在操作系统启动之后就会一直运行的程序。
- 用户通过程序来使用操作系统接口
操作系统接口
- 用户通过程序使用计算机,一般与操作系统直接交互的就是一些普通的C代码与一些重要的函数,这些重要的函数就是操作系统接口,因为是操作系统提供的,因此被称为系统调用。系统调用即操作系统提供的一些函数,系统调用提供一种进入内核的手段。
- POSIX标准,即Portable Operating System Interface of Unix,IEEE关于类Unix系统定义的一套系统调用的标准,类Unix中系统调用一般就是以C函数的形式提供
![]()
内核段与用户段
- 计算机对内存的使用都是以段来使用的,即对内存进行分段管理。
- GDT表用来进行分段管理,其表项如下:
![]()
当操作系统启动时,head.s会初始化gdt表,gdt表的每个表项用于控制一段内存,操作系统的代码和数据都在内核段。注意表项中的DPL,代表当前段的特权级,这个很重要,要根据DPL判断当前进程执行的代码能不能访问这个段。 - GDT表的表项被称为段描述符。
内核态与用户态

- CS是代码段寄存器,IP是指令指针寄存器。用 CS:IP找到代码中将要取指执行的那条指令,此时CS的最低两位用来表示该指令运行在什么态,0表示内核态,3表示用户态。
- 运行在内核态时可以访问任何数据(即访问任何段),运行在用户态时不能访问内核数据。
- 区分内核态与用户态依赖处理器硬件设计,这句话的实际含义是,内核态和用户态其实不是操作系统的性质,而是CPU的两种工作模式,实际上,intel的cpu分了4级的工作模式,只是linux只使用其中两级分成了内核态和用户态。
CPL&DPL&RPL
DPL:描述符特权级(Descriptor Privilege Level)
- 存储在GDT表的表项中,用于描述某个段(可能是代码段、数据段、栈等)所属的特权级。由于GDT表初始化好之后一般保持不变,因此DPL一般也是不变的。系统初始化时,由head.s将所有内核段的描述符的DPL都设置成了0。
CPL:当前执行代码的特权级(Current Priviledge Level)
- CPL保存在CS寄存器中的最低两位,是针对CS而言的。表示当前执行代码的特权级,如果没有设置RPL的话,当段选择子装进CS之后,将RPL与索引到的段描述符中的DPL比较,如果RPL小于等于DPL(值越小表示特权级越高),则可以访问这个段,否则不行。
RPL:请求特权级(Request Privilege Level)
- RPL存储在选择子的最低两位,其值可由程序员自己来设置。当把段选择子装入CS寄存器时,会对比RPL和CPL,然后取其大者来与描述符特权级DPL比较。因为数值越大特权级越低,因此其实是选择RPL和CPL中特权级更低的来与DPL比较,因此想通过设置RPL来访问一些原先不能访问的段是不可能的。因此,其实RPL的作用是程序员主动把自己的程序降级运行,即把RPL设置成大于CPL的值,从而避免程序访问到程序员不想让其访问的段。
段选择子、段描述符、段寄存器
CPU根据段选择子到GDT表中寻找段描述符,用段描述符来填充段寄存器。
RPL在选择子中,CPL在当前执行代码的CS中,DPL在GDT表项即段描述符中。
通过中断进入内核
- 由于用户程序运行在用户态下,其对应得CPL=3,因此是不能直接访问内核中的数据的,为了进入内核需要使用中断。
- 对于Intel x86,使用中断指令int 0X80,将CS中的CPL改成0,从而可以进入内核。
- 中断是用户程序发起的调用内核代码的唯一方式,使用int指令使用中断,到idt表中查找具体的中断处理函数。
- 系统调用也是通过中断实现的
- 用户程序中包含一段包含int指令的代码(比如表面上调用的是open函数,但展开后本质上是一段包含中断的代码)
- 中断使得陷入内核,然后操作系统写中断处理,获取想要调用程序的编号
- 操作系统根据编号执行相应的代码
例子
- printf函数例子
- 高级语言中调用printf
- 库函数将该函数展开成含有int 0x80中断的代码,此时还在用户态,即当前指令的CPL=3,int 0x80中断对应的段描述符的DPL也是3,因此可以访问int 0x80。
- int 0x80中断通过查idt表陷入内核,进入内核态,此时CPL被置为0,内核段中有中断处理函数
- 通过查表找到对应的代码,然后调用sys_write进行写入
- 再接下来就要考虑IO驱动了


浙公网安备 33010602011771号