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)); 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]; char *p; for (p = path + strlen(path); p >= path && *p != '/'; p--) ; p++;
if (strlen(p) >= DIRSIZ) return p; memmove(buf, p, strlen(p)); 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); }
|