2021-xv6实验记录(二)

2021-xv6实验记录(二)

忘记中二的少年 Lv3

xv6实验记录(二)

一、实验目的:

  1. 了解系统调用

二、实验内容:

  1. 完成trace()的新系统调用
  2. 完成sysinfo()的新系统调用

三、实验内容

(1)trace()系统调用的实现

(1)首先切换分支,保存实验记录,并清除编译记录

1
2
git checkout syscall
make clean

根据实验提示,在MakeFile文件中找到UPROGS并添加$U/_trace\打开user目录可以看到存在一个user/trace.c文件,观察该文件的源码:

1
2
3
4
if (trace(atoi(argv[1])) < 0) {
fprintf(2, "%s: trace failed\n", argv[0]);
exit(1);
}

看到这一段代码中存在trace()方法的调用【该方法来自user.h】,trace函数接收一个int 类型的整数,返回值类型也是int,如果执行失败返回值为-1。


(2)在user/user.h中添加函数原型

1
int trace(int);		//NEW ADD

user/usys.pl中添加trace的入口

1
entry("trace");		//NEW ADD

syscall.h中添加宏定义

1
2
#define SYS_close 21
#define SYS_trace 22 //NEW ADD

(3)在proc结构体(在kernel/proc.h)里面添加一个新变量用来存储跟踪号,然后在fork()的时候把结构体里新增加的变量也给复制过去,这样就达到了传参的目的。新的变量是不需要加锁的,因为只会被自己所在的进程使用,所以放在char name[16]之后

修改kernel/proc.h添加变量存储跟踪号

1
2
3
4
5
6
7
8
// Per-process state
struct proc {
// ...
// these are private to the process, so p->lock need not be held.
// ,,
char name[16];
char mask[23]; //NEW ADD
};

打开kernel/sysproc.c添加一个trace函数的具体实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
uint64				//新添加的函数定义
sys_trace(void){
int n;
if(argint(0, &n) < 0) {
return -1;
}
struct proc *p = myproc();
char *mask = p->mask;
int i = 0;
while(i < 23 && n > 0){
if(n % 2){
mask[i++] = '1';
}else {
mask[i++] = '0';
}
n >>= 1;
}
return 0;
}

打开kernel/proc.c文件修改fork()函数在函数中添加一句 safestrcpy(np->mask, p->mask, sizeof(p->mask));即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
int
fork(void)
{
int i, pid;
struct proc *np;
struct proc *p = myproc();

// Allocate process.
if((np = allocproc()) == 0){
return -1;
}
// Copy user memory from parent to child.
if(uvmcopy(p->pagetable, np->pagetable, p->sz) < 0){
freeproc(np);
release(&np->lock);
return -1;
}
np->sz = p->sz;

safestrcpy(np->mask, p->mask, sizeof(p->mask)); //NEW ADD

......

为了将trace打印出来,修改kernel/syscall.c中的syscall()函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
extern uint64 sys_trace(void);		//NEW ADD

static uint64 (*syscalls[])(void) = {
// ...
[SYS_close] sys_close,
[SYS_trace] sys_trace, // NEW ADD
};

static char *syscall_names[23] = {"","fork","exit","wait","pipe","read","kill",
"exec","fstat","chdir","dup","getpid","sbrk","sleep",
"uptime","open","write","mknod","unlink","link","mkdir",
"close","trace"};

void syscall(void)
{
int num;
struct proc *p = myproc();
char* syscall_name; // NEW ADD

num = p->trapframe->a7;
if(num > 0 && num < NELEM(syscalls) && syscalls[num]) {
p->trapframe->a0 = syscalls[num]();
if (strlen(p->mask) > 0 && p->mask[num] == '1'){ // NEW ADD
printf("%d: syscall %s -> %d\n", p->pid, syscall_names[num],p->trapframe->a0);
}
} else {
printf("%d %s: unknown sys call %d\n",
p->pid, p->name, num);
p->trapframe->a0 = -1;
}
}

测试效果

1
2
trace 32 grep hello README
trace 2147483647 grep hello README

image-20230925124713029

1
./grade-lab-syscall trace

image-20230925124857961


(2)sysinfo()系统调用的实现

(1)将$U/_sysinfotest添加到 Makefile 中的 UPROGS

(2)在user/user.h中声明sysinfo() 函数的原型,您需要预先声明struct sysinfo的存在:修改user/user.h需要在user.h里再声明一次struct sysinfo的原因是:在参数列表里的参数是看不见本文件外面定义的struct的。

1
2
3
4
5
6
7
struct stat;
struct rtcdate;
struct sysinfo; //New ADD

// system calls
// ...
int sysinfo(struct sysinfo *); //NEW ADD

(3)在user/usys.pl中添加sysinfo的入口

1
entry("sysinfo");		//NEW ADD

(4)在syscall.h中添加宏定义

1
2
3
#define SYS_close 21
#define SYS_trace 22
#define SYS_sysinfo 23

(5)修改kernel/syscall.c中的syscall()函数

1
2
3
4
5
6
7
8
9
10
extern uint64 sys_info(void);
static uint64 (*syscalls[])(void) = {
// ...
[SYS_sysinfo] sys_info,
};

static char* syscall_names[] = {
// ...
[SYS_sysinfo] "sys_info",
};

(6)编辑sysproc.c文件,先添加函数体

1
2
3
uint64 sys_info(void) {
return 0;
}

(7)打开sysproc.c文件添加\#include "sysinfo.h"

(8)要获得空闲内存的数目,请向kernel/kalloc.c添加一个函数kfreemem

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// get free memory bytes by count freelist
uint64 kfreemem(void) {
struct run *r;
uint64 count = 0;

acquire(&kmem.lock);
r = kmem.freelist;
while (r) {
r = r->next;
count++;
}
release(&kmem.lock);
return count * PGSIZE;
}

(9)要获得进程的数量,请在kernel/proc.c中添加一个函数proc_num()

1
2
3
4
5
6
7
8
9
10
11
12
13
// get the number of processes whose state is not UNUSED
uint64 proc_num() {
struct proc *p;
uint64 count = 0;
for(p = proc; p < &proc[NPROC]; p++) {
acquire(&p->lock);
if(p->state != UNUSED) {
count++;
}
release(&p->lock);
}
return count;
}

(10)在kernel/defs.h文件中添加函数原型

1
2
uint64          proc_num(void);         //NEW ADD
uint64 kfreemem(void); //NEW ADD

(11)修改kernel/sysproc.c文件的sys_info()的函数体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
uint64 sys_info(void) {
struct sysinfo info;
uint64 addr;
info.freemem = kfreemem();
info.nproc = proc_num();
if(argaddr(0, &addr) < 0) {
return -1;
}
// copy info(kernel space) to addr(user space)
if (copyout(myproc()->pagetable, addr, (char *)&info, sizeof(info)) < 0) {
return -1;
} else {
return 0;
}
}

测试效果

1
./grade-lab-syscall sysinfo

image-20230925130757238

四、总测试

  • 在xv6-lab-2021的目录下新建time.txt文件,之后输入一个整数代表你做实验的时间,单位是小时。之后就可以运行make grade来测试自己的成绩。
1
make grade		//用来测试自己的实验成绩

image-20230925131445543

如果上述测试失败,检查错误原因是否是因为

  1. trace的打印格式有无错误

  2. 机器硬件不行,测试时间长,qemu认为超时【解决方法打开目录下的gradelib.py文件调整timeout时间】

    image-20230925131647952

实验心得

在Xv6操作系统中完成新增系统调用 trace()sysinfo() 的的实现不仅让我更深入地理解了操作系统内核的工作方式,还提供了一种方法来收集有关系统状态和进程信息的数据。查看源码的过程令我头疼的同时也令我切实的学会了一些东西,了解了系统的部分运行效果

首先,实现 trace() 调用允许用户程序追踪其他进程的系统调用和退出情况。我学到了如何在进程控制块(PCB)中存储额外的信息,以便程序可以访问。这个经验提高了我对内核数据结构的理解

另一个 sysinfo() 系统调用,提供了关于系统整体状态的信息,系统启动时间、总的调用次数等。实现这个系统调用,我了解了操作系统内核如何维护系统状态信息,并且学会了如何将这些信息传递给用户程序。这对于监控系统性能和行为非常有用。

  • 标题: 2021-xv6实验记录(二)
  • 作者: 忘记中二的少年
  • 创建于 : 2023-10-11 17:00:00
  • 更新于 : 2023-10-11 17:15:02
  • 链接: https://github.com/HandsomeXianc/HandsomeXianc.github.io/2023/10/11/xv6实验记录(二)/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。