哈工大操作系统实验(二)系统调用实现

本文详述了哈工大操作系统实验中关于系统调用的实现,涵盖描述符表、特权级、中断过程和系统调用使用。通过实验,探讨了如何在Linux 0.11上添加系统调用,以及执行系统调用的基本过程,旨在深化对系统调用接口的理解和掌握系统调用的全面控制。
摘要由CSDN通过智能技术生成

实验背景

1. 描述符表

操作系统加载程序 setup.s 读取系统参数至 0x90000 处覆盖 bootsect.s 程序, 然后将 system 模块下移动到 0x00000 处。.同时,加载中断描述符表寄存器 idtr 和全局描述符表寄存器 gdtr,设置CPU的控制寄存器 CR0/程序状态字 PSW,从而进入32位保护模式,跳转到 head.s 开头执行。

为了能让 head.s 在32位保护模式下运行,程序临时设置中断描述符表 idt 和全局描述符表 gdt,并在 gdt 中设置当前内核代码段的描述符和数据段的描述符。
在这里插入图片描述
数据段描述符和代码段描述符存放在gdt 表内,寄存器 gdtr 由基地址和段限长组成,处理器通过寄存器 gdtr 定位 gdt 表。

在这里插入图片描述

段选择符由描述符索引、表指示器 TI 和请求者特权级字段组成,描述符索引用于选择指定描述符表中 8192 ( 2 1 3 ) 8192(2^13) 8192(213) 个描述符的一个,表指示器 TI 值为 0 0 0 表示指定 gdt 表,值为 1 1 1 表示指定 idt 表,而请求者特权级用于保护机制。
在这里插入图片描述

2. 特权级

在这里插入图片描述
处理器的段保护机制可以识别4个特权级 R0~R3,数值越大特权越小。环中心为核心态,最外层为用户态。处理器利用特权级防止运行在较低特权级的程序或人物访问具有较高特权级的一个段。

为了在各个代码段和数据段之间进行特权级检测处理,处理器可以识别以下三种类型的特权级:

  • 当前特权级 CPL(Current Privilege Level)CPL 存放在 CSSS 段寄存器的位0和位1,代表正在执行的程序或任务的特权级。
  • 描述符特权级 DPL(Descriptor Privilege Level):当程序访问数据时,DPL 存放在数据段的 DPL 字段,代表访问当前数据段所需要的特权级。
  • 请求特权级别 RPL(Request Privilge Level)RPL 通过段选择符的第0和第1位表现出来的,RPL 相当于附加的一个权限控制,防止低特权级程序出现高特权级代码,从而能够越权访问数据段,但只有当 RPL>DPL 的时候,才起到实际的限制作用。

在这里插入图片描述

2. 中断过程

中断来源包括外部硬件和内部软件两部分,系统调用需要使用 int 0x80 软件中断指令修改 CPL 值,实现处理器内核态和用户态的切换。

中断描述符表 idt 可以驻留在内存的任何地方,处理器使用启动时设置的 idtr 寄存器定位 idt 表的位置。idtr 寄存器包含 idt 表32位的基地址和16位的长度值。

在这里插入图片描述

idt 表可存放中断门、陷阱门和任务门三种类型的门描述符。中断门含有一个长指针(段选择符和偏移值),处理器使用该长指针把程序执行权转移到代码段的中断处理过程中。

在这里插入图片描述
在这里插入图片描述

系统调用包含中断指令切换CPU的用户态和内核态,而此处阐述中断指令发出后寻找中断描述符表和中断处理程序的过程,详细的中断过程可阅读 《操作系统导论》学习笔记(三):CPU虚拟化(机制)

3. 系统调用使用

在这里插入图片描述
库函数 printf() 对应的指令本质实际上是将一些数据 write()到显存的某些位置,而且输出到屏幕是IO操作,所以需要使用中断指令进入内核执行系统调用例程。

下面给出库函数API和C代码中嵌入汇编代码两种方式使用同一个系统调用 write()

#include <fcntl.h>			/* open() 	*/
#include <unistd.h>			/* write() 	*/
#include <string.h>			/* strlen() */

int main()
{
   
	int fd = open("write.txt", O_RDWR|O_CREAT);
	char *buf = "hello,world!\n";
	int count = strlen(buf);

	write( fd, buf, count);
	close( fd );
	
	return 0;
} 

编译执行,成功生成文件并写入字符串:
在这里插入图片描述
下面使用C代码内嵌汇编的方法自制系统调用 my_write()

查询当前LINUX操作系统的系统调用表(System Call Table),可知 write 系统调用号为 1,而LINUX0.11的 write 系统调用号为4
在这里插入图片描述
内嵌汇编的语法如下:

_asm_ _volatile_ (
	汇编语句模版;
	输出部分;
	输入部分;
	破坏描述部分;
);

汇编语言部分将 write 系统调用号保存至 rax,使用 syscall 而非 int $0x80 触发系统调用,输入部分从内存 m 获取文件描述符 fd,字符串指针 buf 和字符串长度 count,输出部分返回错误信息 res

int my_write(int fd, const void *buf, int count)
{
   
	int res = 0;
	asm("mov  $1, %%rax\n\t"		/* 系统调用号sys_write(1) */
        "syscall\n\t"				/* 触发系统调用 */  
		:"=a"(res)					/* 输出部分:变量res */
		:"m"(fd), "m"(buf), "m"(count)	/* 输入部分:文件描述符fd, 字符串指针buf,字符串长度count */
    );
	return res;
}

C代码中嵌入汇编代码方式使用系统调用 my_write()

#include <fcntl.h>			/* open() 	*/
#include <unistd.h>			/* write() close() */
#include <string.h>			/* strlen() */

int my_write(int fd, const void *buf, int count)
{
   
	int res = 0;
	asm("mov  $1, %%rax\n\t"		/* 系统调用号sys_write(1) */
        "syscall\n\t"				/* 触发系统调用 */  
		:"=a"(res)					/* 输出部分:变量res */
		:"m"(fd)
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值