前言

已经是第二章的内容了,这一章不再是停在用户态去完成实验,而会深入到内核态,增加内核态的系统调用。

实验使用win10 + wsl2 Ubuntu 20.04完成。

内容总览

在上一个实验中,您使用系统调用编写了一些实用程序。在本实验中,您将向 xv6 添加一些新的系统调用,这将帮助您了解它们的工作原理,并让您了解 xv6 内核的一些内部结构。您将在以后的实验中添加更多系统调用。

包括:

  • trace:建一个新的系统调用trace来控制跟踪。它应该接受一个参数,一个整数"mask",代表了要追踪的系统调用。

    例如,为了跟踪 fork 系统调用,程序调用 trace(1 << SYS_fork),其中 SYS_fork 是来自 kernel/syscall.h 的系统调用号。

  • Sysinfo:新建系统调用Sysinfo,收集有关正在运行的系统的信息。

reference

十分感谢以上大佬开源的贡献。知识参考:课程内容翻译 github

内容1:System call tracing

在本作业中,您将添加一个系统调用跟踪功能,该功能可能会在以后调试实验时帮助您。

您将创建一个新的系统调用trace来控制跟踪。它应该接受一个参数,一个整数"mask",代表了要追踪的系统调用。

例如,为了跟踪 fork 系统调用,程序调用 trace(1 << SYS_fork),其中 SYS_fork 是来自 kernel/syscall.h 的系统调用号。

**如果掩码中设置了系统调用的编号,则必须修改 xv6 内核以在每个系统调用即将返回时打印出一行。**该行应包含进程id、系统调用的名称和返回值;您不需要打印系统调用参数。系统调用trace应该启用对调用它的进程以及它随后派生的任何子进程的跟踪,但不应影响其他进程。

提示和理解

题目理解

这个trace()函数的作用是,第一个参数是掩码,因为int是32位最多能表示31个系统调用,被置为1的那个系统调用位就会被跟踪并打印信息。例如trace 32 grep hello README就会跟踪grep hello README这个过程下的所有read命令,因为1<<SYS_read就是等于32

前置知识

网上有很多关于怎么完成这个实验的步骤说明,ref2ref3我亲测都可以可以完成实验并且通过测试,但是很仔细说明原理的。所以我来解说下。

首先按照要求阅读 xv6 book,以及一些文件:

  • 书本内容:Before you start coding, read Chapter 2 of the xv6 book, and Sections 4.3 and 4.4 of Chapter 4, and related source files:
  • 用户态理解:The user-space code for systems calls is in user/user.h and user/usys.pl.
  • 内核态理解:The kernel-space code is kernel/syscall.h, kernel/syscall.c.
  • 进程设计:The process-related code is kernel/proc.h and kernel/proc.c.

但是这里缺少了关键的需要阅读文件,如果不看就不懂得做这个实验:

kernel/kalloc.c

1. Xv6内核源文件

这里是列出是为了让大家知道哪些文件决定着整个内核的功能,不然会对程序流程很懵逼。

文件说明
bio.c文件系统的磁盘块高速缓存
console.c连接到用户的键盘和屏幕
entry.S第一次启动指令
exec.c系统调用
fs.c文件系统
kalloc.c物理页面分配器
kernelvec.S处理来自内核的陷阱,以及计时器中断
log.c文件系统日志记录和崩溃恢复
main.c控制引导过程中其他模块的初始化
pipe.c管道
plic.cRISC-V中断控制器
printf.c格式化输出到控制台。
proc.c进程和调度
sleeplock.c占据cpu的锁
spinlock.c不占据cpu的锁
start.c早期机器模式启动代码。
string.cC的字符串
swtch.S线程切换
syscall.c映射:调度系统调用 -> 处理功能
sysfile.c与文件相关的系统调用
sysproc.c与进程相关的系统调用
trampoline.S在用户和内核之间切换的汇编代码
trap.c处理陷阱和中断的返回
uart.c串口控制台设备驱动程序
virtio_disk.c磁盘设备驱动程序
vm.c管理页表和地址空间
2. xv6 的进程视图

请添加图片描述

流程与代码

下面这张图很清晰地展示了做的流程。

具体的代码请参考reference2

请添加图片描述

# 功能测试

$ trace 32 grep hello README
3: syscall read -> 1023
3: syscall read -> 966
3: syscall read -> 70
3: syscall read -> 0
$ trace 2147483647 grep hello README
4: syscall trace -> 0
4: syscall exec -> 3
4: syscall open -> 3
4: syscall read -> 1023
4: syscall read -> 966
4: syscall read -> 70
4: syscall read -> 0
4: syscall close -> 0
$ grep hello README
$ trace 2 usertests forkforkfork
usertests starting
6: syscall fork -> 7
test forkforkfork: 6: syscall fork -> 8
8: syscall fork -> 9
9: syscall fork -> 10
9: syscall fork -> 11
10: syscall fork -> 12
...
9: syscall fork -> -1
10: syscall fork -> -1
12: syscall fork -> -1
OK
6: syscall fork -> 69
ALL TESTS PASSED

内容2:Sysinfo

在这个作业中,您将添加一个系统调用 sysinfo,它收集有关正在运行的系统的信息。

系统调用接受一个参数:指向struct sysinfo的指针(参见 kernel/sysinfo.h)。

内核应填写此结构的字段:

  • freemem 字段应设置为空闲内存的字节数
  • nproc字段应设置为状态不是UNUSED的进程数。

我们提供了一个测试程序sysinfotest;如果它打印“sysinfotest:OK”,你就通过了这个任务。

提示

都很直白,按照步骤走。

  1. Add $U/_sysinfotest to UPROGS in Makefile

  2. Runmake qemu; user/sysinfotest.c will fail to compile. Add the system call sysinfo, following the same steps as in the previous assignment. To declare the prototype for sysinfo() in user/user.h you need predeclare the existence of struct sysinfo:

    struct sysinfo;
    int sysinfo(struct sysinfo *);
      
    

    Once you fix the compilation issues, run sysinfotest; it will fail because you haven’t implemented the system call in the kernel yet.

  3. sysinfo needs to copy a struct sysinfo back to user space; see sys_fstat() (kernel/sysfile.c) and filestat() (kernel/file.c) for examples of how to do that using copyout().

  4. To collect the amount of free memory, add a function to kernel/kalloc.c

  5. To collect the number of processes, add a function to kernel/proc.c

操作流程

这里ref4讲得很详细,跟着ref4就行,逻辑跟内容1差不多,就不画流程图了。

请添加图片描述

后话

这部分的实验,让我们知道了怎么新添一个系统调用,以及如何在用户态和系统态之间交换和打交道。第一个实验不熟悉,比较困难,画了流程图之后就清晰明了许多。第二个实验就熟悉了。

欢迎点赞和留言交流。

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐