前言
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操作系统,可以学习本书更多内容
。