Lab:Xv6 和 Unix 实用程序

Lab:Xv6 和 Unix 实用程序

本实验将使您熟悉xv6及其系统调用。

准备工作

获取实验室的xv 6源代码并切换到 util 分支:

1
2
3
4
5
6
7
$ git clone git://g.csail.mit.edu/xv6-labs-2020
Cloning into 'xv6-labs-2020'...
...
$ cd xv6-labs-2020
$ git checkout util
Branch 'util' set up to track remote branch 'util' from 'origin'.
Switched to a new branch 'util'

编译并运行XV6

1
$ make qemu

如果成功即可进入sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
$ ls
. 1 1 1024
.. 1 1 1024
README 2 2 2059
xargstest.sh 2 3 93
cat 2 4 24256
echo 2 5 23080
forktest 2 6 13272
grep 2 7 27560
init 2 8 23816
kill 2 9 23024
ln 2 10 22880
ls 2 11 26448
mkdir 2 12 23176
rm 2 13 23160
sh 2 14 41976
stressfs 2 15 24016
usertests 2 16 148456
grind 2 17 38144
wc 2 18 25344
zombie 2 19 22408
console 3 20 0

xv 6没有 ps 命令,但是,如果您键入 Ctrl-p ,内核将打印有关每个进程的信息。如果你现在尝试,你会看到两行: init 一个, sh 一个。

退出XV6:

1
Ctrl-a x

您可以运行 make grade 以使用评分程序测试您的解决方案

sleep

为 xv6 实现 UNIX 程序 sleep;您的睡眠程序应在用户指定的 ticks 数量内暂停。tick 是 xv6 内核定义的时间概念,即定时器芯片两次中断之间的间隔时间。您的解决方案应放在 user/sleep.c 文件中。

  • 在开始编码之前,请阅读xv 6手册的第1章。
  • 查看 user/ 中的一些其他程序(例如, user/echo.cuser/grep.cuser/rm.c ),了解如何获取传递给程序的命令行参数。
  • 如果用户忘记传递参数,sleep应该打印一条错误消息。
  • 命令行参数作为字符串传递;您可以使用 atoi 将其转换为整数(请参阅user/ulib. c)。
  • 使用系统调用 sleep
  • 参见 kernel/sysproc.c ,了解实现 sleep 系统调用的xv 6内核代码(查找 sys_sleep );参见 user/user.h ,了解可从用户程序调用的 sleep 的C定义;参见 user/usys.S ,了解从用户代码跳转到 sleep 内核的汇编代码。
  • 确保 main 调用 exit() 以退出程序。
  • 将你的 sleep 程序添加到Makefile中的 UPROGS ;一旦你完成了, make qemu 将编译你的程序,你就可以从xv 6 shell运行它了。
1
2
3
4
5
6
$ make qemu
...
init: starting sh
$ sleep 10
(nothing happens for a little while)
$

注意这里的make grade等判分程序都是运行在linux的shell中而不是xv6的系统中

每个程序的开始都需要

1
#include "/kernel/tpyes.c" // 包含uint等数据类型

添加程序到Makefile

1
$U/_sleep\
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//
// Created by lyh on 23-9-10.
//
#include "kernel/types.h"
#include "user/user.h"
int
main(int argc, char *argv[]) {
if (argc < 2) {
fprintf(2, "Usage: sleep time\n");
exit(1);
}
// 将传入的参数转换为整数
sleep(atoi(argv[1]));
exit(0); //exit(0)表示程序正常退出,非0表示非正常退出
}

请注意, make grade 运行所有测试,包括下面的分配。如果要运行一个作业的成绩测试,请键入:

1
2
3
./grade-lab-util sleep
或者
make GRADEFLAGS=sleep grade

pingpong

编写一个程序,使用UNIX系统调用通过一对管道(每个方向一个管道)在两个进程之间“乒乓”一个字节。父代应该向子代发送一个字节;子进程应该打印“:received ping”,其中是它的进程ID,将管道上的字节写入父进程,然后退出;父节点应该从子节点读取字节,打印“:received pong”,然后退出。您的解决方案应该在文件 user/pingpong.c 中。

  • 使用 pipe 创建管道。
  • 使用 fork 创建子进程
  • 使用 read 从管道读取,使用 write 写入管道。
  • 使用 getpid 查找调用进程的进程ID。
  • 将程序添加到Makefile中的 UPROGS
  • xv 6上的用户程序只有一组有限的库函数可供使用。您可以在 user/user.h 中查看列表;源(系统调用除外)在 user/ulib.cuser/printf.cuser/umalloc.c 中。

管道是一个小的内核缓冲区,作为一对文件描述符暴露给进程,一个用于读取一个用于写入。将数据写入管道的一端,就可以从管道的另一端读取数据。管道为进程提供了一种通信方式。

运行结果应该如下所示:

1
2
3
4
5
6
7
$ make qemu
...
init: starting sh
$ pingpong
4: received ping
3: received pong
$
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
#include "kernel/types.h"
#include "user/user.h"
int main(int argc,char* argv[])
{
// 如果传入的参数数量大于1,则输出错误信息
if(argc>1)
{
fprintf(2,"Only 1 argument is needed!\n");
exit(1);
}
// 创建两个管道
int p1[2];
int p2[2];
// char* s="a";
// 创建管道
pipe(p1);
pipe(p2);
// 创建子进程
// write(p[1],s,1);
int pid;
if((pid=fork())<0)
{
fprintf(2,"fork error\n");
exit(1);
}
else if(pid==0)
{
// 关闭管道的右侧
close(p1[1]);
// 关闭管道的左侧
close(p2[0]);
// 创建字符串
char buf[10];
// 读取管道的左侧
read(p1[0],buf,4);
// 关闭管道的左侧
close(p1[0]);
// printf("aa:%s\n",buf);
// 打印读取的字符串
printf("%d: received %s\n",getpid(),buf);
// 写入管道的右侧
write(p2[1],"pong",strlen("pong"));
// 关闭管道的右侧
close(p2[1]);
}
else
{
// 关闭管道的左侧
close(p1[0]);
// 关闭管道的右侧
close(p2[1]);
// 写入管道的右侧
write(p1[1],"ping",strlen("ping"));
// 关闭管道的右侧
close(p1[1]);
// 等待子进程结束
// wait(0);
// 读取管道的左侧
char buf[10];
// 读取管道的左侧
read(p2[0],buf,4);
// 打印读取的字符串
printf("%d: received %s\n",getpid(),buf);
// 关闭管道的左侧
close(p2[0]);
}
// 退出程序
exit(0);
}

find

编写一个简单的UNIX find程序:在目录树中查找具有特定名称的所有文件。您的解决方案应该在文件 user/find.c 中。

  • 查看user/ls.c以了解如何读取目录。
  • 使用递归允许查找下降到子目录。
  • 不要递归到.和..
  • 对文件系统的更改在qemu的运行过程中保持不变;要获得干净的文件系统,请运行 make clean ,然后运行 make qemu 。
  • 你需要使用C字符串
  • 注意==不像Python中那样比较字符串。使用strcmp()代替。
  • 将程序添加到Makefile中的 UPROGS
1
2
3
4
5
6
7
8
9
10
$ make qemu
...
init: starting sh
$ echo > b
$ mkdir a
$ echo > a/b
$ find . b
./b
./a/b
$

fstat 系统调用可从文件描述符指向的 inode 中获取信息。它在 stat.h (kernel/stat.h)中定义为

1
2
3
4
5
6
7
8
9
10
11
#define T_DIR     1   // Directory
#define T_FILE 2 // File
#define T_DEVICE 3 // Device

struct stat {
int dev; // File system's disk device
uint ino; // Inode number
short type; // Type of file
short nlink; // Number of links to file
uint64 size; // Size of file in bytes
};

xargs

编写一个简单版本的UNIX xargs程序:从标准输入中读取行,并为每行运行一个命令,将该行作为命令的参数提供。您的解决方案应该在文件 user/xargs.c 中。

1
2
3
$ echo hello too | xargs echo bye
bye hello too
$
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
#include "kernel/types.h"
#include "user/user.h"
#include "kernel/fs.h"
#include "kernel/stat.h"
#define FDSTDIN 0 // standard input file descriptor
#define FDSTDOUT 1

int main(int argc,char* argv[])
{
char buf[10];
char* cur[MAXARG];// pointer array cur[i] is a pointer
int i=0,j=0;
for(i=0;i<argc-1;i++)
{
cur[i]=argv[i+1];
}
cur[i]=malloc(512);
while(read(FDSTDIN,buf,1)==1)
{
if(i==argc-1) printf("%s,%s\n",buf,cur[i]);
switch(buf[0])
{
case '\n':
{

if(fork()==0)
{
// printf("%s\n",cur[argc-1]);
exec(cur[0],cur);
exit(0);
}
wait(0);
for(int k=argc-1;k<=i;k++)
{
// printf("%s\n",cur[k]);
// for(int l=0;l<strlen(cur[k),l++) cur[k][l]=0;
memset(cur[k],0,512);
free(cur[k]);
}
i=argc-1;
cur[i]=malloc(512);
j=0;
break;
}
case ' ':
{
i++;
j=0;
if(i<MAXARG) cur[i]=malloc(512);
break;
}
default :
{
// strcat(cur[i],buf);
cur[i][j++]=buf[0];
break;
}

}
// printf("%s,%c\n",buf,cur[i][j]);
}
exit(0);
}

Lab:Xv6 和 Unix 实用程序
https://macbook-pro-gala.github.io/2023/10/26/Lab:Xv6-和-Unix-实用程序/
作者
lyh
发布于
2023年10月26日
许可协议