Bootstrap

第二章 xv6操作系统初级实验2——为xv6操作系统定义一个内核全局变量,用于进程间共享


前言

  hello,小伙伴们,本篇文章继续接着该专栏第一章的内容写下去,在第一章中,我们做了一个xv6操作系统的入门实验,就是给xv6操作系统源码中添加一个自己编写的C程序,并且通过修改Makefile文件,完成在xv6操作系统中添加新的应用。
  本篇文章还是继续进行xv6操作系统的入门实验,我们主要要做的是:定义一个内核全局变量,用于进程间的共享设计并实现两个系统调用read_sh_var()和write_sh_var()用于读取和修改该全局变量的值;最后,编写C程序,检验是否能在进程间完成数值的共享。很容易发现,该入门实验分为两部分,一个是定义内核全局变量,并且设计系统调用来对内核全局变量进行访问,另一个部分是编写C程序,验证我们编写的内核全局变量和系统调用是否有效,第二个部分就是我们上一章的内容


一、定义内核全局变量&设计两个系统调用

  第一步:在xv6操作系统源码下的spinlock.h文件中定义内核全局变量int sh_var,使得所有进程可以访问。
在这里插入图片描述
  第二步,在xv6操作系统源码defs.h下声明我们在spinlock.h定义的内核全局变量int sh_var
在这里插入图片描述
  至此,定义内核全局变量已经完成,我们继续设计系统调用,系统调用步骤如下。
  第三步,在xv6操作系统源码syscall.h文件中为两个系统调用read_sh_var()和write_sh_var()增加系统调用号,read_sh_var()系统调用负责读取内核全局变量;write_sh_var()系统调用负责修改内核全局变量
在这里插入图片描述
  第四步,在xv6操作系统源码user.h文件中定义两个系统调用read_sh_var()和write_sh_var(),使得我们后续编写的C程序可以使用这两个系统调用。
在这里插入图片描述
  第五步,在xv6操作系统源码usys.S文件中添加两列SYSCALL,这一步可以理解为将我们定义好的系统调用加入到xv6操作系统的系统调用列表中usys.S文件显示了我们可以使用的系统调用。
在这里插入图片描述
  第六步,在xv6操作系统源码syscall.c中修改系统调用跳转表。
在这里插入图片描述
  第七步,还是和第六步一样,修改syscall.c里面的内容,声明我们的两个系统调用函数为外部函数。
在这里插入图片描述
  第八步,这一步比较关键,我们需要在xv6操作系统源码sysproc.c文件中实现自己的系统调用功能,编写的代码和图示如下。

int
sys_read_sh_var(void)
{
    return sh_var;
}

int
sys_write_sh_var(void)
{
    int tmp;
    if(argint(0, &tmp) < 0){
            return -1;
    }
    sh_var = tmp;
    return sh_var;
}

在这里插入图片描述

  如果读者认真进行到这一步实验的话,可能会对红框框里面的代码部分感到奇怪,我的tmp变量都没有赋初始值,为啥就直接给我们的内核全局变量sh_var = tmp,了呢,这里我解释一下,我们在第四步的时候,对修改内核全局变量函数的声明为:int write_sh_var(int),可以看到,此函数形参列表有int类型(我们使用该系统调用的时候,需要传一个int类型的参数),但是没有说明这个int类型的值赋值给哪个具体的局部变量,然后,我们的tmp是write_sh_var(int)函数的第一个局部变量,所以,tmp就会作为第一个局部变量自动接收形参列表传来的int值
  第九步,这一步和上一步一样,比较关键,我们还需要在xv6操作系统源码proc.c文件中实现自己的系统调用功能,编写的代码和图示如下,注意,和上一步不一样的是,函数名没有了sys_开头

int
read_sh_var(void)
{
    return sh_var;
}

int
write_sh_var(void)
{
    int tmp;
    if(argint(0, &tmp) < 0){
            return -1;
    }
    sh_var = tmp;
    return sh_var;
}

在这里插入图片描述

  至此,添加内核全局变量、读取内核全局变量read_sh_var()系统调用、修改内核全局变量write_sh_var()系统调用都已实现,下一步我们要做的是,通过编写一个C程序来验证我们的内核全局变量和系统调用是否都在xv6操作系统源码中添加成功了。

二、编写C程序验证内核全局变量&系统调用

  在xv6操作系统源码中编写C程序的实验我们在第一章已经做了详细的描述,这里不再累赘。如下图和代码所示,我们利用vim编写了一个Linux下C程序,程序名为my_syscall.c,该程序创建了一个子进程,父进程负责修改内核全局变量,子进程负责读取内核全局变量

#include "types.h"
#include "stat.h"
#include "user.h"

int
main(int argc, char *argv[])
{
       int tmp = 2024;
       printf(1,"I am a parent process, I will write sh_var\n");
       write_sh_var(tmp);		//修改内核全局变量
       int pid = fork();       //创建子进程
       if(pid > 0){
               wait();
       }
       else if(0 == pid){
       		//读取内核全局变量
               printf(1,"I am child process, the sh_var = %d\n",read_sh_var());
       }
       exit();
}

在这里插入图片描述

  编写好我们的C程序后,不要忘记修改Makefile文件,修改完Makefile文件后,在xv6操作系统源码目录下执行make命令,如下图,没报错即表示我们编写的C程序编译成功。
在这里插入图片描述
  接下来,在xv6操作系统源码目录下执行make qemu命令,在QEMU虚拟环境中执行ls命令,查看到我们编写的C程序名,输入my_syscall命令,程序成功执行,内核全局变量被赋值为2024,符合我们预期,并且成功被子进程读取,如下图所示。
在这里插入图片描述
在这里插入图片描述
  至此,我们的xv6操心系统入门实验2成功完成,主要实现了添加内核全局变量,设计读和写内核全局变量两个系统调用


总结

  通过对该实验的完成,加上第一章的实验,我们已经完成了xv6的入门实验部分;总结一下获得的收获就是:学会了在xv6操作系统的源码中增加自己想要实现的功能(程序),学会了如何给xv6操作系统增加系统调用(为用户程序提供接口),从xv6操作系统源码入手,使得我们对操作系统的内核运行有了进一步的认知,这两个实验都是非常好的xv6操作系统入门实验。

注:本专栏所有内容都是参考深圳大学罗秋明老师著的《操作系统原型-xv6分析与实验》,并且提取了较为简单和重要的部分,想要进一步深入xv6操作系统,可以学习本书更多内容

;