哈工大操作系统实验二(整理自用)

一、实验内容

此次实验的基本内容是:在Linux-0.11 上添加两个系统调用,并编写两个简单的应用程序测试它们。

(1)iam()

第一个系统调用是iam(),其原型为

int iam(const char* name);

完成的功能是将字符串参数name的内容拷贝到内核中保存下来。要求name的长度不超过23个字符。返回值是拷贝的字符数。如果name的字符个数超过23,则返回“-1”,并置errno为EINVAL。

在kernel/who.c中实现此系统调用。

(2)whoami()

第二个系统调用是whoami(),其原型为:

int whoami(char* name,unsigned int size);

它将内核中由iam()保存的名字拷贝到name指向的用户地址空间中,同时确保不会对name越界访存(name的大小由size说明)。返回值是拷贝的字符数。如果size小于需要的空间,则返回“-1”,并置errno为EINVAL。

也是在kernel/who.c中实现此系统调用。

(3)测试程序

运行添加过新系统调用的linux-0.11,在其环境下编写两个测试程序iam.c和whoami.c,最终的运行结果是:

$ ./iam lizhijun

$ ./whoiam

lihzijun

二、实验环境搭建(Ubuntu虚拟机)

(58条消息) ubuntu下 Linux 0.11 编译内核 - 实验环境搭建_~橘子~的博客-CSDN博客https://blog.csdn.net/qq_39557240/article/details/85336730

三、先备知识

1.操作系统的启动(接着实验一)

书接上回,话说bootsect.s将setup和sytem模块加载进内存中之后,就跳到了setup.s中执行程序,即将CPU控制权交给了setup模块。

根据名字就能猜到:setup将完成OS启动前的设置,主要是利用ROM BIOS中断读取机器系统数据(一些硬件参数),并将这些数据保存到0x90000开始的位置(覆盖掉了bootsect程序所在的地方),所取得的参数和保留的内存位置如下表所示:

哈工大操作系统实验二(整理自用)_第1张图片

然后setup会将system模块移动到物理地址为0的地方,然后设置CPU的控制寄存器CR0的PE位,使之从0变为1,表示系统进入32位保护模式,并且跳转到system模块最前面的部分的head.s程序继续运行。

值得注意的是,为了能让head.s在32位保护模式下运行,setup.s临时设置了中断描述符表(IDT)和全局描述符表(GDT),并在GDT中设置了当前内核代码段的描述符和数据段的描述符。

哈工大操作系统实验二(整理自用)_第2张图片

 因为这只是临时设置的GDT和IDT,因此后面我们会看到head.s程序中会根据内核的需要重新设置这些描述符表。

哈工大操作系统实验二(整理自用)_第3张图片

因为boosect.s和setup.s都是在实模式下(16位)下运行的,因此使用的是16位汇编 ,汇编的语法是基于MINIX系统的汇编语言语法。而到了system模块,使用的就是32位汇编,汇编的语法是AT&T系统V的汇编语法,这两种语法之间是有区别的。

head.s里面会调用main函数,这个过程是在汇编程序中调用C函数,这是基于栈结构实现的。实质和C函数之间的调用没有区别。

哈工大操作系统实验二(整理自用)_第4张图片

 进入main函数之后,会陷入死循环,即main函数正常情况下是永远不会返回的,因为操作系统这个程序需要一直运转来管理PC,如果main返回,就死机了。

哈工大操作系统实验二(整理自用)_第5张图片

 至此,操作系统运转起来了。下面就可以介绍系统接口了。

2.系统接口

系统接口就是函数调用,只是这个函数是系统提供的,因此叫做系统调用。

2.1为什么需要系统调用?

有时候用户程序要对内核区的代码和数据进行操作,但是内核里面都是操作系统的核心代码和数据,如果被随意操作甚至恶意破坏,计算机很可能就会崩溃。

但是用户程序在某些情况下又必须对内核进行操作,那么怎么让程序达到这个目的,而同时又使得操作系统不被恶意破坏呢?

这就是系统调用存在的意义。

2.2内核和用户的隔离

这种隔离机制是由硬件提供的,这就实现了对内核的保护。

哈工大操作系统实验二(整理自用)_第6张图片

 2.3 如何进入内核

对于Intel x86,中断指令int是进入内核的唯一途径。

int指令将CS中的CPL改成0,就“进入内核”

哈工大操作系统实验二(整理自用)_第7张图片

2.4 系统调用的过程

哈工大操作系统实验二(整理自用)_第8张图片

 四、实验步骤

1.首先打开linux-0.11/include/unistd.h文件,添加两个系统调用号,这些系统调用符号常数,被用作调用函数表中的索引值:

哈工大操作系统实验二(整理自用)_第9张图片

 2.修改linux-0.11/kenel/system_call.s程序代码,这个程序实现了系统调用中断 int 0x80的入口处理过程。

对于系统调用(system call)这种软中断,其处理过程基本是首先为调用相应C函数处理程序做准备,将一些参数压入堆栈。

系统调用最多可以带3个参数,分别通过寄存器ebx,ecx,edx传入。然后调用C函数进行相应功能的处理,处理返回后再去检测当前任务的信号位图,对值最小的一个信号进行处理并复位信号位图中的该信号。

系统调用的C语言处理函数分布在整个linux内核代码中,由include/linux/sys.h头文件中的系统函数指针数组表来匹配,这个表的索引就是上面unistd.h定义的系统调用号。

哈工大操作系统实验二(整理自用)_第10张图片

 讲了这么多,这里只需要修改系统调用的总数:

哈工大操作系统实验二(整理自用)_第11张图片

 3.修改linux-0.11/include/linux/sys.h文件,将我们自己的两个系统调用函数添加进去。

该头文件列出了内核中所有系统调用函数的原型,以及系统调用函数指针表。需要修改的有两处,已标识

哈工大操作系统实验二(整理自用)_第12张图片

 4.在linux-0.11/kernel目录下创建who.c文件,实现sys_iam和sys_whoami的系统调用处理函数。

#define __LIBRARY__			
#include 		
#include 		/* 要求设置错误为EINVAL */
#include 	/* 使用put_fs_byte和get_fs_byte */
 
 
char temp[64]={0};		/* 存储sys_iam获取的字符串	*/
 
int sys_iam(const char* name)
{
   int i=0;			/* 用户空间数据name长度	*/
   while(get_fs_byte(name+i)!='\0') i++;	
   if(i>23) return -EINVAL;
   printk("%d\n",i);
   
   i=0;			/* 获取name至temp  */
   while((temp[i]=get_fs_byte(name+i))!='\0'){
	i++;
   }   
    return i;
}
 
int sys_whoami(char* name,unsigned int size)
{
    int i=0;			/* 内核空间数据temp长度 */
    while (temp[i]!='\0') i++;
    if (size

5.修改~/oslab/linux-0.11/kernel/Makefile文件,Makefile是make程序的配置文件,修改这个的目的是为了让我们写的who.c文件可以在使用make进行编译链接的时候生成目标文件kernel.o。

哈工大操作系统实验二(整理自用)_第13张图片

哈工大操作系统实验二(整理自用)_第14张图片 6.在~/oslab/linux-0.11/kernel目录下,执行make指令,只要不报错就行。成功后该目录下会生成who.o文件。

哈工大操作系统实验二(整理自用)_第15张图片

哈工大操作系统实验二(整理自用)_第16张图片

 7.在~/oslab下面编写两个测试程序来调用这两个接口。

iam.c文件的代码,功能是调用iam系统将name写入内核,并且返回写入字符串的长度。

/ /  iam.c
/* iam.c  */
#define __LIBRARY__        
#include "unistd.h" 
_syscall1(int, iam, const char*, name); 
int main(int argc, char** argv){
    int wlen = 0;
    if(argc < 1){
        printf("not enougth argument\n");
        return -2;
    }
    wlen = iam(argv[1]);
    return wlen;
}

 whoami.c文件代码,功能是调用whoami查出写入内核的name,并显示,返回值是字符串的长度。

/ / whoami.c
/* whoami.c */
#define __LIBRARY__        
#include "unistd.h" 
_syscall2(int, whoami,char*,name,unsigned int,size);    

int main(int argc, char** argv){
    char buf[30];
    int rlen;
    rlen = whoami(buf, 30);
    printf("%s\n", buf);
    return rlen;
}

 8.添加到linux-0.11中的系统调用只能在bochs虚拟机中运行,不能在终端上运行,在oslab目录下打开终端,输入下面的命令:

sudo ./mount-hdc

9.然后将上面所有的修改都拷贝到hdc/root目录下,确保在bochs环境下所有修改生效。

cp  /home/你自己的用户名/oslab/linux-0.11/include/unistd.h  /home/你自己的用户名/oslab/hdc/usr/include/unistd.h
cp  /home/你自己的用户名/oslab/linux-0.11/include/linux/sys.h  /home/你自己的用户名/oslab/hdc/usr/include/linux/sys.h
cp /home/你自己的用户名/oslab/iam.c  hdc/usr/root
cp /home/你自己的用户名/oslab/whoami.c  hdc/usr/root

10.在~/oslab/linux-0.11目录下使用make all,编译链接,没报错就行。

哈工大操作系统实验二(整理自用)_第17张图片

11.运行内核,在内核使用gcc编译C,然后运行程序进行验证,编译没报错就行。

然后运行程序进行验证。 下面所示就是成功界面。

哈工大操作系统实验二(整理自用)_第18张图片 哈工大操作系统实验二(整理自用)_第19张图片

 (63条消息) 哈工大操作系统实验二:系统调用的实现_哈工大操作系统系统调用_Deteriorate_Kr的博客-CSDN博客icon-default.png?t=N4P3https://blog.csdn.net/weixin_53213086/article/details/124946785

你可能感兴趣的:(linux,windows)