简介
最近一段时间,除了毕设就没有别的什么事情了,于是开始看起了鼎鼎大名的MIT(麻省理工学院)6.S081操作系统课程(实际是三天打鱼两天晒网…之前沉迷于战地1无法自拔)
对于操作系统这门课程,我之前只是自己看书自学了一遍,有很多知识都是一知半解,并没有深入了解其中的原理。这次也算是用这个课程来恶补一下我的基础吧。感谢MIT网课的两位教授,从课堂中我学到了很多很多东西,虽然网课总共只看了1/3,但也可以称得上受益匪浅了。
环境配置
本人的配置:Ubuntu 20.04 WSL2
根据官网上的教程配置即可
注意开启GDB的方式可能和网课上讲的不太一样,查阅资料后发现步骤应该是:
# 在xv6文件夹中
gdb-multiarch kernel/kernel
# 进入GDB后
set architecture riscv:rv64
target remote localhost:26000
代码详解
这个实验相对还是比较简单的,主要是起了热身的作用吧。除了prime,其他任务都很快就完成了。
sleep
// user/sleep.c
void Error_Handler()
{
fprintf(2, "Usage: sleep [time]\n");
exit(1);
}
int main(int argc, char const *argv[])
{
if (argc != 2)
{
Error_Handler();
}
int time = 0;
if ((time = atoi(argv[1])) == 0)
{
Error_Handler();
}
sleep(time);
exit(0);
}
pingpong
// user/pingpong.c
#include "kernel/types.h"
#include "user/user.h"
#include "kernel/fcntl.h"
int main()
{
int p[2];
pipe(p);
if (fork() == 0)
{
close(p[1]);
char buf[5];
read(p[0], buf, 4);
fprintf(1, "%d: received ping\n", getpid());
close(p[0]);
exit(0);
}
else
{
close(p[0]);
write(p[1], "test", 4);
close(p[1]);
wait(0);
fprintf(2, "%d: received pong\n", getpid());
exit(0);
}
}
前面两个都没什么好说的…
primes
#include "kernel/types.h"
#include "user/user.h"
#include "kernel/fcntl.h"
int primes[11] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31};
void prime_sieve(int *data_pipe, int n)
{
// 注意此处一定要关闭前管道的写端,不然会无法退出子进程!
close(data_pipe[1]);
if (n > 10)
{
exit(0);
}
int inner_p[2];
pipe(inner_p);
if (fork() == 0)
{
prime_sieve(inner_p, n + 1);
}
else
{
close(inner_p[0]);
int num;
read(data_pipe[0], &num, 4);
printf("prime %d\n", num);
while (read(data_pipe[0], &num, 4) != 0)
{
if (num % primes[n] != 0)
{
write(inner_p[1], &num, 4);
}
}
close(data_pipe[0]);
close(inner_p[1]);
wait(0);
exit(0);
}
}
int main()
{
int p[2];
pipe(p);
if (fork() == 0)
{
prime_sieve(p, 0);
}
else
{
close(p[0]);
int i;
for (i = 2; i <= 35; i++)
{
write(p[1], &i, 4);
}
close(p[1]);
wait(0);
}
exit(0);
}
这个task还是有点难度的,想出解决的思路不难,但很容易出bug,光debug就花了我三四个小时…
大坑是在创建子进程后,必须先把之前管道的写端给关掉,如果不关掉的话,后面继续fork子进程时,会把之前残留的这个管道写端又复制到子进程,导致子进程会有多个写端,则子进程会不断地等待写入,造成死循环,所有子进程都无法正确关闭。
这个bug隐藏地太深,也许也是我技术太菜的原因,我花了非常多的时间才发现了这个bug,不得不说是个大坑
find
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
#include "kernel/fs.h"
void find(char *path, char *name)
{
int fd;
struct stat st;
struct dirent de;
char buf[512], *p;
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;
}
strcpy(buf, path);
p = buf + strlen(buf);
*p++ = '/';
while (read(fd, &de, sizeof(de)) == sizeof(de))
{
if (de.inum == 0)
continue;
memmove(p, de.name, DIRSIZ);
p[DIRSIZ] = 0;
if (strcmp(de.name, name) == 0)
{
printf("%s\n", buf);
}
if (stat(buf, &st) < 0)
{
fprintf(3, "find: cannot stat %s\n", buf);
continue;
}
if (st.type == T_DIR && strcmp(".", de.name) != 0 && strcmp("..", de.name) != 0)
{
find(buf, name);
}
}
close(fd);
}
int main(int argc, char *argv[])
{
find(argv[1], argv[2]);
exit(0);
}
这个task内容也算是比较简单的,比较麻烦的部分在于要参考其他程序中对于文件系统的处理方式,稍花一点时间就可以完全搬过来了
xargs
#include "kernel/types.h"
#include "kernel/stat.h"
#include "kernel/param.h"
#include "user/user.h"
#include "kernel/fs.h"
int main(int argc, char *argv[])
{
// 构造参数列表
char **args = (char **)malloc(MAXARG * sizeof(char *));
int i;
for (i = 0; i < argc - 1; i++)
{
args[i] = malloc(strlen(argv[i + 1]) + 1);
strcpy(args[i], argv[i + 1]);
}
int command_i = i;
char ch;
char buf[50];
i = 0;
while (read(0, &ch, 1) != 0)
{
if (ch == '\n')
{
buf[i] = 0;
args[command_i] = (char *)malloc(strlen(buf) + 1);
strcpy(args[command_i], buf);
memset(buf, 0, 50);
i = 0;
if (fork() == 0)
{
exec(args[0], args);
}
else
{
wait(0);
int j;
for (j = 1; j <= command_i; j++)
{
free(args[j]);
}
free(args);
// 重新构造参数列表
args = (char **)malloc(MAXARG * sizeof(char *));
int i;
for (i = 0; i < argc - 1; i++)
{
args[i] = malloc(strlen(argv[i + 1]) + 1);
strcpy(args[i], argv[i + 1]);
}
command_i = i;
}
}
else if (ch == ' ')
{
buf[i] = 0;
args[command_i] = (char *)malloc(strlen(buf) + 1);
strcpy(args[command_i], buf);
memset(buf, 0, 50);
command_i++;
i = 0;
}
else
{
buf[i++] = ch;
}
}
exit(0);
}
由于做这个实验已经过了很久,对这个task的印象不是很深了,我记得我当初是读错了题目要求,所以刚开始怎么都无法解决。后面重新审了题,再加上参考了网上一些大佬的程序,最终还是完成了,总体而言确实是moderate的难度。
总结
对于OS基础薄弱的我来说,这个课程和配套的Lab确实教会了我很多,不光是概念上的知识,也有实际动手的经验。在Lab中,很多晦涩难懂、抽象的东西都变成了实实在在的代码,在将每个步骤拆解、拼装成程序后,自然地就将理论知识和实践融会贯通了。希望学完这门课之后,如果在面试中遇到操作系统原理相关的问题,我能够侃侃而谈,信心十足吧。
感谢MIT,感谢互联网,让全球的每个学生都能享受顶级的计算机知识教育!
(未完待续…)