做该实验前建议先去xv6-labs-2025/user/user.h里看一下有哪些函数,有个映象

Sleep

要实现sleep,直接调用pause即可,(有些文章使用的是sleep,版本更新后使用的其实是pause)

1
2
3
4
5
6
7
8
9
10
11
12
#include "kernel/types.h"
#include "user/user.h"

int main(int argc, char *argv[]) {
if (argc != 2) {
fprintf(2, "Usage: sleep <seconds>\n");
exit(1);
}
int seconds = atoi(argv[1]);
pause(seconds);
exit(0);
}

然后在makefile里修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
UPROGS=\
$U/_cat\
$U/_echo\
$U/_forktest\
$U/_grep\
$U/_init\
$U/_kill\
$U/_ln\
$U/_ls\
$U/_mkdir\
$U/_rm\
$U/_sh\
$U/_stressfs\
$U/_usertests\
$U/_grind\
$U/_wc\
$U/_zombie\
$U/_logstress\
$U/_forphan\
$U/_dorphan\
$U/_sleep\ #添加这个

之后在终端make qemu ,使用sleep 10测试

在xv6-labs-2025里新建终端,输入 ./grade-lab-util sleep测试程序

1
2
3
4
5
huzayn@huzayn-VMware-Virtual-Platform:~/xv6/xv6-labs-2025$ ./grade-lab-util sleep
make: “kernel/kernel”已是最新。
== Test sleep, no arguments == sleep, no arguments: OK (2.9s)
== Test sleep, returns == sleep, returns: OK (2.4s)
== Test sleep, makes syscall == sleep, makes syscall: OK (2.0s)

sixfive

写一个读取文本里5或6整倍数的程序,主要考察对open,read等函数的使用

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
#include "kernel/types.h"
#include "user/user.h"
#include "kernel/fcntl.h"

// 1. 定义分隔符 (注意第一个是空格)
char *separators = " -\r\t\n./,";

// 2. 检查纯数字
int is_pure_number(char *s) {
if (*s == 0) return 0;
for (; *s; s++) {
if (*s < '0' || *s > '9') return 0;
}
return 1;
}

// 3. 打印逻辑
void try_print(char *buf) {
if (is_pure_number(buf)) {
int n = atoi(buf);
if (n > 0 && (n % 5 == 0 || n % 6 == 0)) {
printf("%d\n", n);
}
}
}

// 4.封装处理逻辑,以便复用
void process(int fd) {
char c;
char buf[512];
int i = 0;

while (read(fd, &c, 1) > 0) {
if (strchr(separators, c)) {
// 遇到分隔符
if (i > 0) {
buf[i] = 0; //或buf[i] = '\0' 都是字符串结束符,如果要表示数字0,则是buf[i] = '0';
try_print(buf);
i = 0;
}
} else {
// 遇到非分隔符
if (i < sizeof(buf) - 1) {
buf[i++] = c;
}
}
}

// 处理文件末尾 (EOF)
if (i > 0) {
buf[i] = 0;
try_print(buf);
}
}

// 5. 主函数
int main(int argc, char *argv[]) {
// 场景 A: 如果没有参数,读取标准输入 (fd = 0)
if (argc <= 1) {
process(0);
exit(0);
}

for (int k = 1; k < argc; k++) {
int fd = open(argv[k], O_RDONLY);
if (fd < 0) {
fprintf(2, "sixfive: cannot open %s\n", argv[k]);
exit(1);
}

process(fd); // 处理这个文件
close(fd); // 处理完关闭,准备下一个
}

exit(0);
}

输入./grade-lab-util sixfive测试程序

1
2
3
4
5
huzayn@huzayn-VMware-Virtual-Platform:~/xv6/xv6-labs-2025$ ./grade-lab-util sixfive
make: “kernel/kernel”已是最新。
== Test sixfive_test == sixfive_test: OK (1.2s)
== Test sixfive_readme == sixfive_readme: OK (1.7s)
== Test sixfive_all == sixfive_all: OK (1.3s)

memdump

该实验要写一段能够根据指定格式的代码将内存转换成整数、字符或字符串的程序,主要考察指针

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
#include "kernel/types.h"
#include "user/user.h"
#include "kernel/fcntl.h"

void memdump(char *fmt, char *data);

int
main(int argc, char *argv[])
{
if(argc == 1){
printf("Example 1:\n");
int a[2] = { 61810, 2025 };
memdump("ii", (char*) a);

printf("Example 2:\n");
memdump("S", "a string");

printf("Example 3:\n");
char *s = "another";
memdump("s", (char *) &s);

struct sss {
char *ptr;
int num1;
short num2;
char byte;
char bytes[8];
} example;

example.ptr = "hello";
example.num1 = 1819438967;
example.num2 = 100;
example.byte = 'z';
strcpy(example.bytes, "xyzzy");

printf("Example 4:\n");
memdump("pihcS", (char*) &example);

printf("Example 5:\n");
memdump("sccccc", (char*) &example);
} else if(argc == 2){
// format in argv[1], up to 512 bytes of data from standard input.
char data[512];
int n = 0;
memset(data, '\0', sizeof(data));
while(n < sizeof(data)){
int nn = read(0, data + n, sizeof(data) - n);
if(nn <= 0)
break;
n += nn;
}
memdump(argv[1], data);
} else {
printf("Usage: memdump [format]\n");
exit(1);
}
exit(0);
}

void
memdump(char *fmt, char *data)
{
for(int i=0;fmt[i];i++){
char f = fmt[i];
switch(f){
case 'i': {
printf("%d\n", *(int*)data);
data += 4;
break;
};
case 'p': {
printf("%x\n", *(uint*)data);
data += 8;
break;
};
case 'h': {
printf("%d\n", *(short*)data);
data += 2;
break;
};
case 'c': {
printf("%c\n", *(char*)data);
data += 1;
break;
};
case 's': {
printf("%s\n", *(char**)data);
data += 8;
break;
};
case 'S': {
printf("%s", (char*)data);
data += strlen(data) + 1;
break;
};
}
}
}

测试如下:

1
2
3
4
5
huzayn@huzayn-VMware-Virtual-Platform:~/xv6/xv6-labs-2025$ ./grade-lab-util memdump
make: “kernel/kernel”已是最新。
== Test memdump, examples == memdump, examples: OK (1.6s)
(Old xv6.out.memdump_examples failure log removed)
== Test memdump, format ii, S, p == memdump, format ii, S, p: OK (0.9s)

find&exec

我把两个实验放在了一起,find练习探索路径名和目录,系统调用 open、read 和 fstat。而exec练习系统调用fork、exec 和 wait。

实现find的关键在于理解fstat以及使用memmove进行路径拼接

实现exec的关键在于子进程的调用

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
#include "kernel/fs.h"
#include "kernel/fcntl.h"

void find(char *path, char *target, char **exec_argv, int exec_argc, int in_exec_mode) {
char buf[512], *p;
int fd;
struct dirent de;
struct stat st;

if ((fd = open(path, 0)) < 0) {
fprintf(2, "find: cannot open %s\n", path);
return;
}

if (fstat(fd, &st) < 0) {
fprintf(2, "find: cannot stat %s\n", path);
close(fd);
return;
}

switch (st.type) {
case T_FILE:
// 提取文件名
for (p = path + strlen(path); p >= path && *p != '/'; p--);
p++;

// 检查是否匹配目标文件名
int match = 0;
if (strcmp(target, ".") == 0) {
// 匹配所有文件
match = 1;
} else if (strcmp(p, target) == 0) {
match = 1;
}

if (match) {
if (in_exec_mode) {
// 执行 -exec 命令
int pid = fork();
if (pid == 0) {
// 子进程
char *args[16];
int i;

// 复制命令参数
for (i = 0; i < exec_argc; i++) {
args[i] = exec_argv[i];
}

// 将当前文件路径作为最后一个参数
args[i] = path;
args[i + 1] = 0;

// 执行命令
exec(args[0], args);

// 如果 exec 失败
fprintf(2, "find: exec failed for %s\n", path);
exit(1);
} else if (pid > 0) {
// 父进程等待子进程完成
wait(0);
} else {
fprintf(2, "find: fork failed\n");
}
} else {
// 普通模式,直接打印文件路径
printf("%s\n", path);
}
}
break;

case T_DIR:
if (strlen(path) + 1 + DIRSIZ + 1 > sizeof buf) {
printf("find: path too long\n");
break;
}

strcpy(buf, path);
p = buf + strlen(buf);
*p++ = '/';

while (read(fd, &de, sizeof(de)) == sizeof(de)) {
if (de.inum == 0) continue;

// 跳过 . 和 ..
if (strcmp(de.name, ".") == 0 || strcmp(de.name, "..") == 0) {
continue;
}

memmove(p, de.name, DIRSIZ);
p[DIRSIZ] = 0;
find(buf, target, exec_argv, exec_argc, in_exec_mode);
}
break;
}

close(fd);
}

int main(int argc, char *argv[]) {
int exec_start = 0;
int exec_argc = 0;
char *exec_argv[16];

// 解析参数
if (argc < 3) {
fprintf(2, "usage: find <path> <name> [-exec <command> [args...]]\n");
exit(1);
}

char *path = argv[1];
char *target = argv[2];
int in_exec_mode = 0;

// 检查是否有 -exec 参数
for (int i = 3; i < argc; i++) {
if (strcmp(argv[i], "-exec") == 0) {
in_exec_mode = 1;
exec_start = i + 1;

// 收集 exec 参数
for (int j = exec_start; j < argc; j++) {
exec_argv[exec_argc++] = argv[j];
}
break;
}
}

// 如果没有找到 -exec,但参数大于3,则第二个参数可能是文件名模式
if (argc > 3 && !in_exec_mode) {
// 可能是 find . wc 的形式
find(path, target, exec_argv, exec_argc, 0);
} else if (in_exec_mode) {
find(path, target, exec_argv, exec_argc, 1);
} else {
find(path, target, exec_argv, exec_argc, 0);
}

exit(0);
}

然后make grade 即可通过测试(除开最后time.txt带来的报错)

1
2
git add .
git commit -m"huzayn完成修改"