lab1

xv6地址:6.S081 / Fall 2020 (mit.edu)

环境配置:Mit6.s081环境配置踩坑之旅WSL2+VScode_mit6s081-CSDN博客

视频链接:MIT 6.S081 2020 操作系统 [中英文字幕]_哔哩哔哩_bilibili

译文:mit-public-courses-cn-translatio.gitbook.io

参考:课程介绍 · 6.S081 All-In-One (dgs.zone)

xv6做的比较快,主要是较难lab参考了dalao得。(PS:主要是自己能力就这,能完成中低难度的lab已经是万幸了)

Design is sort of high level structure, and implementation is really about what the code looks like.

设计是一种高层次的结构,而实现则是关于代码真正的样子

OS Purpose:

  • ABSTRACT
  • MULTIPLEX
  • ISOLATION
  • SHARING
  • SECURITY
  • PERFORMANCE
  • RANGE OF OSES

WHY HARD/INTERESTING?

  • TENSIONS
    • EFFICIENT —— ABSTRACE
    • POWERFUL —— SIMPLE
    • FLEXIBLE —— SECURE

一个xv6进程用户空间内存内核私有的进程状态组成。

"user/user.h"列出了可以使用的系统调用,与普通函数不同,系统调用最终会跳转到内核中。

系统调用 描述
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) 打开一个文件;标志指示读/写;返回一个文件描述符(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[]) 创建一个管道,将读/写文件描述符放入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) 将打开文件的信息放入*st中。
int stat(char *file, struct stat *st) 将命名文件的信息放入*st中。
int link(char *file1, char *file2) 为文件file1创建另一个名字(file2)。
int unlink(char *file) 删除一个文件。

utilities

sleep

直接调用sleep系统调用即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include "kernel/types.h"   //数据类型头文件
#include "user/user.h" //系统调用


int
main(int argc, char* argv[])
{
if(argc != 2)
{
char str[15] = "Args must be 2!";
write(2, str, strlen(str));
//0标准输入,1标准输出,2标准错误
exit(1);
}
sleep(atoi(argv[1])); //转化为整型
exit(0);
}

pingpong

这里为什么要用两个pipe呢,这是为了防止自己在输入数据后数据又被自己读到。类似于全双工通信

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


int
main(int argc, char* argv[])
{
int p1[2], p2[2];
int pid;
pipe(p1);
pipe(p2);

if(argc != 1)
{
char str[15] = "Args must be 1!";
write(2, str, strlen(str));
exit(1);
}

if((pid = fork()) == 0) //子进程
{
close(p1[1]); //关闭不使用的文件描述符
close(p2[0]);
char arr2[10] = "pong";
char buf[10];
int n;
n = read(p1[0], buf, 4);
buf[n] = '\0';
printf("%d: received %s\n", getpid(), buf);
write(p2[1], arr2, strlen(arr2));
exit(0);
}
else { //父进程
close(p1[0]); //关闭不使用的文件描述符
close(p2[1]);
char arr1[10] = "ping";
char buf[10];
int n;
write(p1[1], arr1, strlen(arr1));
n = read(p2[0], buf, 4);
buf[n] = '\0';
printf("%d: received %s\n", getpid(), buf);
}

exit(0);
}

prime

主进程将2~35的数据以整型的方式写入到管道中,然后逐个int读取管道内容

  • 若该数为质数,则fork进程,将未读的数据通过管道传给子进程,然后递归调用func判断质数(注意父进程要wait(0)回收僵尸进程
  • 若该数不为质数,则递归调用func判断下一数字
  • 关闭不必要的管道以减少需要的文件描述符数量
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"

int isPrime(int n) {
int i;
if (n < 2)
return 0;
for (i = 2; i <= n / 2; i++) {
if (n % i == 0)
return 0;
}
return 1;
}

void func(int pipe_out) {
int x;
if (read(pipe_out, &x, sizeof(int)) == 0)
return; // 如果读不到数据,直接返回

if (isPrime(x)) {
int p[2];
pipe(p);
if (fork() == 0) { // 子进程
close(pipe_out);
close(p[1]);
printf("prime %d\n", x);
func(p[0]);
close(p[0]);
exit(0);
} else { // 父进程
close(p[0]);
int xx;
while (read(pipe_out, &xx, sizeof(int)) > 0) {
write(p[1], &xx, sizeof(int));
}
close(pipe_out);
close(p[1]);
wait(0); // 等待子进程结束,防止子进程成为僵尸进程
exit(0);
}
} else {
func(pipe_out);
}
}

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

if (argc != 1) {
char str[] = "Args must be 1!";
write(2, str, strlen(str));
exit(1);
}

for (int i = 2; i <= 35; i++) {
write(p[1], &i, sizeof(int));
}
close(p[1]);
func(p[0]);
close(p[0]);
exit(0);
}

find

跟着ls.c照葫芦画瓢即可

注意ls.c文件memset(buf+strlen(p), ' ', DIRSIZ-strlen(p));的设置是为了使输出对齐。我们不能直接ctrl+c,因为我们要匹配字符串,而字符串以\0结尾,可以使用buf[strlen(p)] = 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
65
66
67
68
69
70
71
72
73
#include "kernel/fs.h"
#include "kernel/stat.h"
#include "kernel/types.h"
#include "user/user.h"

char *fmtname(char *path) {
static char buf[DIRSIZ + 1]; // static防止返回时被清理
char *p;
for (p = path + strlen(path); p >= path && *p != '/'; p--)
;
p++;

if (strlen(p) >= DIRSIZ)
return p;
memmove(buf, p, strlen(p)); //memmove防止字符串被覆盖
buf[strlen(p)] = 0; // 确保以零结尾
return buf;
}

int match(char *str1, char *str2) { return strcmp(str1, str2) == 0; }

void find(char *path, char *str) {
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:
if (match(fmtname(path), str))
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 || de.inum == 1 || match(de.name, ".") ||
match(de.name, ".."))
continue;
memmove(p, de.name, strlen(de.name));
p[strlen(de.name)] = 0;
find(buf, str);
}
break;
}
close(fd);
}

int main(int argc, char *argv[]) {
if (argc != 3) {
char str[15] = "Args must be 3!";
write(2, str, strlen(str));
exit(1);
}

find(argv[1], argv[2]);
exit(0);
}

xargs

即将管道符前面的参数放到argv[1]所运行的程序参数列表上。因此我们需要构造参数列表。

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

int
ParseLine(char *buf) {

int i = 0;
while ((read(0, buf+i, sizeof(char))) > 0) {
if(buf[i] == '\0' || buf[i] == '\n')
{
buf[i] = '\0';
return 1;
}
i++;
}
if(i != 0)
{
buf[i] = '\0';
return 1;
}
return 0;
}

int
main(int argc, char *argv[]) {
char *line[MAXARG];
char buf[MAXARG];

for (int i = 1; i < argc; i++) {
line[i - 1] = argv[i];
}
while (ParseLine(buf)) {
if (fork() == 0) {
line[argc - 1] = buf;
line[argc] = 0; //截断
exec(argv[1], line);
exit(0);
} else {
wait(0);
}
}

exit(0);
}