【MIT6.S081】学到哪写到哪(一)

一.安装环境

github:

1
2
3
git clone git://g.csail.mit.edu/xv6-labs-2020
cd xv6-labs-2020
git checkout util

ubuntu终端:

1
sudo apt-get install git build-essential gdb-multiarch qemu-system-misc gcc-riscv64-linux-gnu binutils-riscv64-linux-gnu

测试:

1
2
3
4
5
6
$ riscv64-unknown-elf-gcc --version
riscv64-unknown-elf-gcc (GCC) 10.1.0
...

$ qemu-system-riscv64 --version
QEMU emulator version 5.1.0

进入xv文件夹后再测试

1
2
3
4
5
# 进入xv文件夹
$ make qemu
# 一堆输出
init: starting sh
$

二.SystemCall有些啥玩意

系统调用 描述
int fork() 创建进程,返回子进程的pid
int exit(int status) 结束当前进程;状态变为wait(),没有返回值
int wait(int *status) 等待子进程结束,结束状态再*status里,返回子进程pid
int kill(int pid) 结束 pid 所指进程,返回0,返回-1就是有错误
int getpid() 返回当前进程 pid
int sleep(int n) 睡眠 n 秒
int exec(char *filename,char *argv[]) 加载并执行一个文件,只有当其有错时才返回
char *sbrk(int n) 为进程内存空间增加 n 字节
int open(char *filename, int flags) 打开文件,flags 指定读/写模式,返回fd
int read(int fd, char* buf, int n) 从fd中读 n 个字节到 buf,返回n
int write(int fd, char* buf, int n) 从 buf 中写 n 个字节到fd,返回read的数目
int close(int fd) 释放 fd
int dup(int fd) 复制 fd,返回新的fd
int pipe( int p[]) 创建管道, 并把读和写的 fd 返回到p[0]和p[1]
int chdir(char *dirname) 改变当前目录
int mkdir(char *dirname) 创建新的目录
int mknod(char *name, int major, int minor) 创建设备文件
int fstat(int fd) 返回文件信息
int link(char *f1, char *f2) 给 f1 创建一个新名字(f2)
int unlink(char *filename) 删除文件

Ctrl+c 终止当前执行程序

Ctrl+d 相当于exit命令

三.如何运行

1
2
3
4
make qemu //运行xv6
make grade //Lab评分
make clean //清理缓存
Makefile打开之后在UPROGS里添加程序

四.正式开始

Sleep

为xv6实现UNIX程序sleep;您的sleep应暂停一段用户指定的时间。时间是xv6内核定义的时间概念,即来自定时器芯片的两次中断之间的时间。您的解决方案应该在文件 user/sleep.c中

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

int main(int argc,char *argv[]){
if(argc != 2){
write(2, "we need time\n",128);
}
sleep(atoi(argv[1]));
exit(0);
}

PingPong

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

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
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"

int main(int argc,char *argv){
int p[2];
int q[2];
char inbuf[100];
char onbuf[100];
char* mess1 = "ping\n";
char* mess2 = "pong\n";
pipe(p);
pipe(q);
if(fork()==0){
write(p[1],mess2,50);
read(q[0],inbuf,100);
printf("%d: received %s",getpid(),inbuf);
//close(0);
}else{
write(q[1],mess1,50);
read(p[0],onbuf,100);
sleep(1);
printf("%d: received %s",getpid(),onbuf);
//close(0);
}
exit(0);
}

先write再read(先写再读)

文件描述符(fd)

buf

Primes

使用pipefork来建立管道。第一个过程将数字2到35馈入管道。对于每个素数,您将安排创建一个进程,该进程通过管道从其左边读取,并通过另一管道向其右边写入。由于xv6的文件描述符和进程数量有限,因此第一个进程在35处停止。

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
#include "kernel/types.h"
#include "user/user.h"

void choose(int p){
int n;
while(read(0,&n,sizeof(n))){
if(n%p!=0){
write(1,&n,sizeof(n));
}
}
}

void redirect(int k,int *p){
close(k);
dup(p[k]);
close(p[0]);
close(p[1]);
}

void recivie(){
int pd[2];
int p;
pipe(pd);
if(read(0,&p,sizeof(p))){
printf("prime %d\n",p);
if(fork()){
//parent
redirect(0,pd);
recivie();
}else{
//child
redirect(1,pd);
choose(p);
}
}
}

int main(){
int pd[2];
pipe(pd);
if(fork()){
//parent
redirect(0,pd);
recivie();
}else{
//child
redirect(1,pd);
for (int i = 2; i <= 35; i++)
{
write(1,&i,sizeof(i));
}

}
exit(0);
}

redirect函数是把标准输入/输出k和管道p[k]绑定在一起