实验目的与要求:
实验目的:
(1)、掌握计算机操作系统管理进程、处理机、存储器、文件系统的基本方法。
(2)、了解进程的创建、撤消和运行,进程并发执行;自行设计解决哲学家就餐问题的并发线程,了解线程(进程)调度方法;掌握内存空间的分配与回收的基本原理;通过模拟文件管理的工作过程,了解文件操作命令的实质。
(3)、了解现代计算机操作系统的工作原理,具有初步分析、设计操作系统的能力。
(4)、通过在计算机上编程实现操作系统中的各种管理功能,在系统程序设计能力方面得到提升。
实验要求:
(1)、题目1:(30%)
阅读xv6中文文档中的Chapter 4 Traps and system calls;
阅读这个很不错的网上课程翻译中的Lec06:
https://mit-public-courses-cn-translatio.gitbook.io/mit6-s081/
结合xv6-riscv代码,回答以下的代码理解问题:
在user/zombie.c中,sleep(5); (L12)。sleep是一个系统调用。请分析代码,阐述在代码中,这一系统调用如何一步步的转化为一个对核心函数sleep(kernel/proc.c / L536)的调用?
(1)、题目2:(70%)
一个在xv6-riscv上的游戏实例(xv6-riscv-2048.zip,a, d, w, s 分别代表左,右,上,下。):
这个小游戏移植于https://github.com/nyuichi/xv6。
可以读一下这个中文报道:
https://finance.sina.com.cn/tech/2020-10-08/doc-iivhuipp8489972.shtml
但我们只是完成了部分移植。原有版本上还有一些重要的重要功能我们并没有移植到risc-v上。比如原本包含的ioctl系统调用等。Nyuichi的xv6版本上还包含更多有趣的小程序,比如一个迷你的vi,一个扫雷游戏等。
请参考2048的的示例代码,实现一个xv6-riscv上的小游戏,或者小编辑器。
你可以:
- 自己写一个小游戏,或者类似nano的小编辑器;
移植一个开源的小应用程序到xv6-riscv上,比如移植Nyuichi的xv6版本上的mini-vi或扫雷程序到xv6-riscv上。
解析:
(1)、题目1:回答以下的代码理解问题:
在user/zombie.c中,sleep(5); (L12)。sleep是一个系统调用。请分析代码,阐述在代码中,这一系统调用如何一步步的转化为一个对核心函数sleep(kernel/proc.c / L536)的调用?
结合文档、相关资料和xv6源码,下面对sleep系统调用逐步转化为对核心函数sleep(kernel/proc.c)的调用的过程进行分析。
- 用户空间sleep调用
在user/zombie.c文件中,当程序执行到sleep(5);时,实际上是在调用用户库函数sleep,如图1-1所示。
图 1- 1 用户空间调用sleep
这个函数在user.h(L23)中声明,并在usys.S(L30)中定义,如图1-2所示。
图 1- 2 用户库函数sleep声明和定义
在usys.S中,sleep系统调用通过汇编语言实现的,它设置eax寄存器为SYS_sleep的值(这是sleep系统调用的标识符,定义在syscall.h),然后触发中断来发起系统调用,如图1-3所示。
图 1- 3 sleep系统调用标识符定义
- 系统调用的触发
usys.S中的宏SYSCALL(sleep)展开后,生成了相应的汇编指令,这些指令配置了系统调用的环境,并触发了中断int $T_SYSCALL。这个中断将控制权转移到内核的陷阱处理代码。
- 进入内核的陷阱处理
系统调用触发后,控制权首先到达trapasm.S中的alltraps标签处的代码。这是RISC-V硬件触发的所有陷阱的入口点。在trapasm.S中,硬件上下文被保存到trapframe结构中,并且根据陷阱类型调用相应的处理函数。
- 系统调用处理
由于sleep是一个系统调用(T_SYSCALL),alltraps中的代码将调用trap.c中的trap函数,并传入保存的trapframe。在trap函数中,会检查scause寄存器的值来确定发生的是哪种类型的陷阱,并相应地调用处理函数。
- 调用syscall函数
在trap.c的usertrap函数中,syscall函数被调用,如图1-4所示。
图 1- 4 调用syscall函数
这个函数定义在syscall.c中,通过读取a7寄存器中的系统调用号(在我们的情况下是SYS_sleep),并在函数指针数组syscalls中查找对应的函数指针,来确定应该调用哪个系统调用处理函数,如图1-5所示。
图 1- 5 syscall函数定义
- 系统调用分发
syscall函数通过索引syscalls数组找到sleep对应的处理函数sys_sleep(定义在sysproc.c)。syscalls数组在编译时根据系统调用的编号填充正确的函数指针。sys_sleep函数定义如图1-6所示。
图 1- 6 sys_sleep函数定义
- 执行sys_sleep函数
sys_sleep函数被调用,它接收传递给sleep的参数(在我们的情况下是5,表示睡眠5个时钟滴答)。sys_sleep函数内部调用了kernel/proc.c中的sleep函数,将当前进程置于睡眠状态,直到指定的时间过去或者被唤醒。
- 核心函数sleep的执行
kernel/proc.c中的sleep函数执行实际的睡眠逻辑,包括释放进程锁、将进程状态设置为SLEEPING、调用sched函数让出CPU,并在适当的时候重新获取CPU执行。该函数定义如图1-7所示。
图 1- 7 核心函数sleep定义
以上为sleep系统调用逐步转化为对核心函数sleep的调用的详细过程。
(2)、题目2:请参考2048的的示例代码,实现一个xv6-riscv上的小游戏,或者小编辑器。
首先尝试运行移植到xv6上的2048程序。将压缩包解压并移动到WSL下,进入xv6-riscv-2048文件夹。查看Makefile文件,发现编译指令与xv6相同,可以通过2048命令来运行2048小游戏程序,如图2-1所示。
图 2- 1 xv6-2048 Makefile片段
执行make qemu后进入xv6系统。执行2048,即可启动游戏程序,如图2-2所示。
图 2- 2 xv6下的2048游戏程序
接下来将仿照移植2048的代码,对扫雷程序进行移植。分析移植程序的方法,需要完成的工作有:1.获取已实现的扫雷小游戏代码;2.实现程序在xv6下所需要相关的函数等;3.将程序代码加入编译,使其可以被启动。
在xv6-2048中,很多原本xv6缺少的重要的库函数,如首要的终端交互功能等等,示例给出的做法是在xv6目录下新建一个include目录,将改进后的头文件放进当中,如图2-3所示。
图 2- 3 include目录下放进改进后的头文件
其中包括stdio、termios等重要的库,因为xv6本身没有标准输入输出等库来提供一些终端功能,如刷新终端。
然后在include的同级目录下创建lib目录,其中创建lib.c文件用于实现之前 include 文件夹下定义的改进函数。
在user目录中新建2048.c文件,存放实现2048小游戏的代码,并修改Makefile文件,将程序代码加入编译。
有了2048移植的功能库后,移植扫雷便简单了很多。从网络上找到minesweeper的实现代码,将其修改为移植后xv6的依赖项,将代码放到user目录下,并添加编译项,如图2-4所示。
图 2- 4 添加扫雷代码编译项
接着在移植的xv6目录下运行make qemu编译,进入xv6环境,接着运行minesweeper即可进入游戏程序,如图2-5所示。
图 2- 5 成功进入xv6下扫雷程序
此外,尝试对sl小火车进行了移植,同样直接将依赖已有的库实现的代码放到user目录下并添加编译项即可。编译后进入xv6环境,输入sl即可查看效果,实现效果如图2-6所示。
图 2- 6 sl小火车效果预览
至此本次实验圆满完成。
标签:调用,操作系统,代码,2048,xv6,sleep,实验,综合,函数 From: https://blog.csdn.net/weixin_44340944/article/details/140411128