哈工大操作系统实验OSLab2-系统调用

本文详细介绍了哈工大操作系统实验OSLab2关于系统调用的内容。首先解释了系统调用的必要性和分级制度,接着阐述了系统调用的实现思路,包括CPL和DPL的概念以及如何通过中断进入内核态。然后通过printf系统调用的例子展示了具体实现代码,并提及实验步骤。最后提到了内核态和用户态数据交换的接口get_fs_byte和set_fs_byte的实现。
摘要由CSDN通过智能技术生成

HIT OSLab2 系统调用

一、什么是系统调用?

首先,系统调用是操作系统分级制度下的产物。当操作系统内核加载到内存后,它便一直常驻在那里;假设操作系统没有没有分级制度,那么我在编写某一段C语言程序的时候就可以读和写这台机器的任意物理内存,这显然是不能被接受的,因为无限制地修改内存意味着我也可能会更改内存中关于内核的那部分内容,导致死机或更严重的后果。

再来说一下分级制度。操作系统将程序运行的状态和物理内存分为两个(或多个)状态和区域:内核态、内核段&&用户态、用户段。当程序处于用户态时,它只能访问同为用户段数据,不能访问内核段数据;当程序处于内核态时,它可以访问任何数据。分级制度保证了处于用户态的程序不能随意访问或更改内核态的数据,保证了内核的数据安全。

可我们在某些情况下仍然需要访问内核中的数据内容,比如输入输出、读写文件等等,处理这些工作的函数都处于内核中。为了使用户能够使用内核提供的内容,但又不想让用户有意或无意地破坏这些数据,便有了系统调用。简而言之,系统调用就是一些内核提供的访问其中数据或者代码的接口。这和Java中常常把类中的变量设为私有并通过getter和setter来访问是同样的道理——不是不能访问,但要遵守一定的规则,接口就是在遵守这些规则的前提下对这些数据进行访问的一个入口。

二、系统调用的实现思路

好了,有了对于保护内核数据的基本构思,现在我们可以试着去实现它了。前面提到了分级制度,为了实现这个制度,我们需要在计算机中的存储部件储存两个字段,一个为前的特权级别,记为CPL(Current Priviledge Level),另一个是我想要访问的数据的特权级别,记为DPL(Destination Priviledge Level),保存这个两个字段的存储单元可以是寄存器或者内存。CPL被存储在CS或SS段寄存器的第0和第1位,而DPL字段被存储在了内存中的某个段描述符或中断描述符中。这是因为当前执行的代码是可知的,可以直接对寄存器查询特权级别,而目的代码是未知的,需要根据跳转地址去查表看其对应的是哪个段,再根据这个段的段描述符得到代码的特权级别。

x86架构将CPL和DPL字段均设置成了2比特长,允许了4种不同的特权级别,但对于Linux操作系统只用到了其中的两个状态:用户态和内核态,其特权级(Priviledge Level)分别为3和0(特权越高特权级越小)。

举个例子,CPL为3的程序只能访问DPL为3的目的代码或数据,而不能访问DPL为0的内核代码;而CPL为0的内核程序则可以访问任意级别的代码和数据。这在操作系统代码中可以简单地写为一个判断语句。

当执行系统调用时,x86架构首先执行一个中断指令(interrupt),这是用户程序发起的进入内核态的唯一方式。此时CPL=3而DPL=0,为了能够使判断语句通过,操作系统会将中断描述符中的DPL字段改为3,通过人为地更改目的代码的特权级别,使CPL=DPL,从而判断通过,系统进入内核态。进入内核态之后,再将段选择子中的RPL字段改为0,稍后段选择子中的RPL字段将更新CS中的CPL字段使其也置为0。如此一番折腾后,CPL=3而DPL=0,特权级别完全反过来了,代表了系统已经进入了内核态。

当然以上只是操作系统在设计时的实现思路,具体的实现细节见下面的代码。

三、系统调用具体实现代码


哈工大的这页ppt举了printf系统调用的例子。首先printf函数在内部又调用了write函数。write函数在linux/include/unistd.h中用宏来定义,这个宏函数里面因为要产生0x80号中断,所以使用了一些内嵌汇编代码。有关于内嵌汇编的简单知识可以参考本课程的参考书《Linux-0.11内核完全注释》的第159页。上图中的这段代码的大致意思是使用%eax作为输出寄存器存储返回值,其他参数分别存入%eax, %ebx, %ecx, %edx中,然后执行0x80号中断。其中的__NR_##name在传入具体参数后,比如write,会变为__NR_write,这是在unistd.h中定义的另外一组宏,它作为函数数组的指针执行具体的系统调用。

在这里插入图片描述
操作系统在接受到0x80号中断后,利用set_system_gate函数来设置0x80号中断的处理。这也是一个宏函数,其使用了另外一个宏函数_set_gate并传入了一些参数。第一个参数&idt[n],它是中断门的地址;第三个参数传入了3,它作为新的DPL,在下面的汇编代码中体现了这个"3"的作用。通过左移13位将3,用二进制表示即为11b,赋给了IDT表中的DPL字段,现在IDT表中的这个中断门描述符中的DPL字段被置为了11b,处于用户态的程序可以访问内核了。与此同时,上面的汇编代码的最后一行"a"(0x00080000)将前四位0x0008的两字节数据赋给了图中的段选择符部分,其低四位的8,表示了二进制是1000b,低两位是00b,是CPL字段的所在位置,所以现在CPL被置为0了,表示系统已经进入了内核态。

四、实验代码

首先在kernel文件夹中新建一个who.c文件,里面写入iam和whoami函数的系统调用sys_iam和sys_whoami。

#define __LIBRARY__
#include <unistd.h>
#include <errno.h>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值