Lab 1 是让我们在 user 文件夹下编写五个用户程序

Sleep(easy)

Implement the UNIX program sleep for xv6; your sleep should pause for a user-specified number of ticks. A tick is a notion of time defined by the xv6 kernel, namely the time between two interrupts from the timer chip. Your solution should be in the file user/sleep.c.
这里的目的即是停顿一段时间,时间即是 sleep 后的 ticks 参数
Run the program from the xv6 shell:

1
2
3
4
5
6
$ make qemu
...
init: starting sh
$ sleep 10
(nothing happens for a little while)
$

这里只需要使用 sleep 系统调用即可,代码如下:

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

int main(int argc, char *argv[]) {
    if (argc != 2) { // 检查参数数量
        exit(1);
    }
    int ticks = atoi(argv[1]); // 命令行中的数字是字符串,将其转化成int型
    if (ticks < 0) { // 判断时间参数
        exit(1);
    }
    sleep(ticks); // sleep系统调用
    exit(0);
}

在 makefile 中添加应用程序
$U/_sleep
对其进行单独测试

1
$ make GRADEFLAGS=sleep grade

pingpong(easy)

Write a program that uses UNIX system calls to ‘’ping-pong’’ a byte between two processes over a pair of pipes, one for each direction. The parent should send a byte to the child; the child should print “: received ping”, where is its process ID, write the byte on the pipe to the parent, and exit; the parent should read the byte from the child, print “: received pong”, and exit. Your solution should be in the file user/pingpong.c.
即通过pipe管道实现父子进程的通信


这里需要用到几个系统调用,xv6-book第一章开头有提供系统调用的介绍

  • int pipe(int p[])
  • int write(int fd, char *buf, int n)
  • int read(int fd, char *buf, int n)
    系统调用 描述
    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 *file, char *argv[]) 加载一个文件并使用参数执行 它只有在出错时才返回
    char *sbrk(int n) 按n字节增长进程的内存 返回新内存的开始
    int open(char *file, int flags) 打开一个文件 flags表示read/write 返回一个fd(文件描述符
    int write(int fd, char *buf, int n) 从buf写n个字节到文件描述符fd 返回n
    int read(int fd, char *buf, int n) 将n个字节读入buf 返回读取的字节数 如果文件结束 返回0
    int close(int fd) 释放打开的文件fd
    int dup(int fd) 返回一个新的文件描述符 指向与fd相同的文件
    int pipe(int p[]) 创建一个管道 把read/write文件描述符放在p[0]和p[1]中
    int chdir(char *dir) 改变当前的工作目录
    int mkdir(char *dir) 创建一个新目录
    int mknod(char *file, int, int) 创建一个设备文件
    int fstat(int fd, struct stat *st) 将打开文件fd的信息放入*st
    int stat(char *file, struct stat *st) 将指定名称的文件信息放入*st
    int link(char *file1, char *file2) 为文件file1创建另一个名称(file2)
    int unlink(char *file) 删除一个文件

其中特别注意当使用两个管道进行父子进程通信时,如果管道的写端没有关闭,那么管道中数据为空时对管道的读取将会阻塞.因此,为了避免读端一直阻塞,需要尽快关闭不需要的管道描述符,特别是写端。这样可以让read()系统调用在没有数据时返回0,而不是无限期地阻塞。

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

#define RE 0 // pipe read port
#define WR 1 // pipe write port

int main(int argc, char *argv[]) {
char byte = 'A'; //the byte will be sent

int p_fd[2];
int c_fd[2];
pipe(p_fd);
pipe(c_fd);

int pid = fork();
int exit_status = 0;

if (pid < 0) {
fprintf(2, "fork() error!\n");
close(c_fd[RE]);
close(c_fd[WR]);
close(p_fd[RE]);
close(p_fd[WR]);
exit(1);
} else if (pid == 0) { // child process
close(c_fd[RE]);
close(p_fd[WR]);

if (read(p_fd[RE], &byte, sizeof(char)) != sizeof(char)) {
fprintf(2, "child read() error!\n");
exit_status = 1;
} else {
fprintf(1, "%d: received ping\n", getpid());
}

if (write(c_fd[WR], &byte, sizeof(char)) != sizeof(char)) {
fprintf(2, "child write() error!\n");
exit_status = 1;
}

close(c_fd[WR]);
close(p_fd[RE]);
exit(exit_status);
} else { //parent process
close(p_fd[RE]);
close(c_fd[WR]);

if (write(p_fd[WR], &byte, sizeof(char)) != sizeof(char)) {
fprintf(2, "parent write() error!\n");
exit_status = 1;
}

if (read(c_fd[RE], &byte, sizeof(char)) != sizeof(char)) {
fprintf(2, "parent read() error!\n");
exit_status = 1;
} else {
fprintf(1, "%d: received pong\n", getpid());
}

close(c_fd[RE]);
close(p_fd[WR]);
exit(exit_status);
}
}

在 makefile 中添加应用程序

1
$U/_pingpong\

对其进行单独测试

1
$ make GRADEFLAGS=pingpong grade

Primes(Moderate/Hard)

Write a concurrent version of prime sieve using pipes. This idea is due to Doug McIlroy, inventor of Unix pipes.Your solution should be in the file user/primes.c.
参考资料

1
2
3
4
5
6
p = get a number from left neighbor
print p
loop:
n = get a number from left neighbor
if (p does not divide n)
send n to right neighbor


本质上是一个递归,通过管道传输数字

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

#define RE 0 // pipe read port
#define WR 1 // pipe write port

int lpipe_fir(int lpipe[2], int *dst) { //接受左管道的第一个数字,如果没有返回-1
if (read(lpipe[RE], dst, sizeof(int)) == sizeof(int)) {
printf("prime %d\n", *dst);
return 0;
}
return -1;
}

void trans(int lpipe[2], int rpipe[2], int first) { //传递给右管道
int data;
while (read(lpipe[RE], &data, sizeof(int)) == sizeof(int)) {
if (data % first) {
write(rpipe[WR], &data, sizeof(int));
}
}
close(lpipe[RE]);
close(rpipe[WR]);
}

void primes(int lpipe[2]) {
close(lpipe[WR]);
int first;
if (lpipe_fir(lpipe, &first) == 0) {
int fd[2];
pipe(fd);
trans(lpipe, fd, first);
int pid = fork();

if (pid == 0) {
primes(fd); //递归
} else {
close(fd[RE]);
wait(0);
}
}
exit(0);
}

int main(int argc, char *argv[]) {
int init_fd[2];
pipe(init_fd);

for (int i = 2; i <= 35; i++) {
write(init_fd[WR], &i, sizeof(int));
}
int pid = fork();

if (pid == 0) {
primes(init_fd);
} else {
close(init_fd[WR]);
close(init_fd[RE]);
wait(0);
}
exit(0);
}

在 makefile 中添加应用程序
$U/_primes
对其进行单独测试

1
$ make GRADEFLAGS=pingpong grade

find (Moderate)

基本就是copy user/ls.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
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
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
#include "kernel/fs.h"

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

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

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

if (st.type != T_DIR) {
fprintf(2, "usage: find <DIRECTORY> <filename>\n");
return;
}

if (strlen(path) + 1 + DIRSIZ + 1 > sizeof buf) {
fprintf(2, "find: path too long\n");
return;
}
strcpy(buf, path);
p = buf + strlen(buf);
*p++ = '/'; //p指向最后一个'/'之后

while (read(fd, &de, sizeof de) == sizeof de) {
if (de.inum == 0) {
continue;
}
memmove(p, de.name, DIRSIZ); //添加路径名称
p[DIRSIZ] = 0; //字符串结束标志
if (stat(buf, &st) < 0) {
fprintf(2, "find: cannot stat %s\n", buf);
continue;
}
//不要在“.”和“..”目录中递归
if (st.type == T_DIR && strcmp(p, ".") != 0 && strcmp(p, "..") != 0) {
find(buf, target);
} else if (strcmp(target, p) == 0) {
printf("%s\n", buf);
}
}

close(fd);
}


int main(int argc, char *argv[]) {
if (argc != 3) {
exit(1);
}
find(argv[1], argv[2]);
exit(0);
}