哈工大os学习笔记二(系统调用的实现)
第二篇笔记 操作系统的调用文章目录
一、三个问题
1.应用程序为什么不能直接访问操作系统,而必须通过接口?
osl内的很重要,如root用户密码,假如网上下载的一个代码能直接访问,就能获取root,或者是随意更改计算机的配置,不安全,不好管理。
2.怎么才能不直接进入内核?
只有硬件设计,才能不让应用程序直接进入内核。
计算机对内存的使用都是一段一段的使用
而这个硬件设计,靠段寄存器实现(CS DS)
只要明白CPL,DPL
初始化的时候DPL就在GDT表中,而操作系统数据段代码段对应的DPL都为0
DPL:是用来描述目标内存段的特权级别,也就是要跳往的访问的
CPL:是用来描述当前的特权级,取决于执行的是什么指令 ,执行指令都要有pc(CS:IP)
cs表示一段内存区 ,cs中的一部分表示当前程序所处于的特权级
目标特权级的数字要大于或等于当前特权级的数字(DPL>=CPL)
这种特权级也叫保护环/特权环
小结:
- 在OS初始化的时候(head.s执行的时候)会针对内核态的代码和数据建立GDT表向,然后他对应的DPL就等于0(当时已经初始化好了0)DPL在GDT中
- 最后转到用户态进行执行,启动用户执行程序,cs里的CPL=3,这个CPL=3也是初始化时候设置的,(推到用户态时候设成3)之后就一直保持3
- 当跳到内核时候CPL=0,回到用户时候CPL就又等于3
- 每一次跳转移动(jmp,mov)都要访问GDT表,因为访问段寄存器嘛,要通过段进入内核
- 这时候检查DPL>=CPL?
- 由于DPL=0,CPL=3 直接挡住不让进
3.那要通过什么方式进入内核?
接上面,既然不让进去,那怎么访问呢?
硬件提供主动进入内核的方法——中断指令int
并不是所有的中断都能进入内核,只有硬件专门 设计的一部分才能进入内核
系统调用就是上层应用调用操作系统提供的接口(一些函数)。中断(系统调用使用int 0x80)是硬件提供的用户态进入内核态的唯一方式。
二、系统调用的详细过程:
说白了就是接口的参数转换,上一个接口函数的参数怎么转换成下面的接口函数的参数
1.首先我们应用程序调用printf("%s",…)
2.在C库函数中变成printf():
由于下面要传给库函数write(…),所以要把应用程序调用的printf转化成write能接收的参数,PPT里显示了write传参的格式,所以要先经过c库函数printf(…)的中转变成write所需要的buf缓冲区,和字符个数。
3.在调用write(…)变成一段包含int 0x80 的中断代码
4.然后中断再通过系统调用,进入操作系统里面
三、 系统调用细节:
把系统调用号__NR_write写入eax寄存器,并在ebx,ecx,edx寄存器中传入参数,调用int 0x80号中断进入内核完成系统调用。
系统调用号置给%eax,然后调用int 0x80,调用int 0x80就到内核了,所以下面就要明白int 0x80是什么
int 0x80中断的处理
int 0x80查IDT表
由于大家都是从int 0x80接口进入的,所以设置系统调用号作为具体导向。
初始化时候做的,int 0x80通过system_call这个函数进行处理
怎么做到的,设置system_gate中断处理门
实际每一个表向就是中断处理门
int 0x80需要查询中断表IDT,找到对应的中断门。而内核初始化的时候,恰好设置了0x80号中断对应的中断门。在文件linux/init/main.c中132行调用了sched_init()函数,而sched_init()函数在linux/kernel/sched.c中,sched_init()函数最后一行代码就是设置0x80号对应的中断门描述符,如下图。
set_system_gate在linux/include/asm/system.h中,如下,就是设置中断门描述符的,段选择子设为0x8,段内偏移设置为&system_call(中断处理函数地址),DPL设为了3。而用户态CPL=3,正好可使用该中断门,而该中断门里面的段选择子是0x8,0x8对应的二进制是b0000_0000_0000_1_0_00,对应的CPL=0,于是乎进入内核态。在中断程序执行完之后,CPL会设置为3,回到用户态。
这时cs =8, ip=&system_call
0x8=b0000_0000_0000_1_0_00 cs最后为00,而cs最后两位为cpl,所以cpl=0,当前特权级为0,现在在通过中断以后,然后在执行中断处理程序的时候就就是特权级为0 了,就到内核态进行执行了。
回顾一下,在初始化的时候DPL设置为0,在初始化的时候80号中断的DPL设置成3,故意让cpl为3的用户能进来,进来后cpl就设置为0了。就进入内核了
在返回的时候会执行一条指令,这条指令将cpl又设为3,就又变成用户态
接下来是中断处理程序system_call
中断处理程序就是linux/kernel/system_call.s,如下。
在linux/include/linux/sys.h中,对应了系统调用的所有函数列表,如下。
_sys_call_table函数指针数组
call _system_call_table (,&eax,4) //a(,%eax,4)=a+4*eax
_sys_call_table+4/eax*就是相应系统调用处理函数入口
call _system_call_table (,&eax,4) 就是call sys_write
到此,就要开始真正去调用sys_write了,而sys_write内部是怎么样的呢?后面课程会讲。
系统调用大致过程