MIT6.S081笔记——写写OS
人生浪费指南三流写手/吃肉肉
2 人赞同了该文章
MIT 6.S081
lab tool
环境要求主要是3个项目:
一个是riscv64的toolchain,另一个就是虚拟机qemu,剩下的就是一个源码库,装好这三个就可以开始写代码了
关于环境一些坑
首先就是toolchain,这个项目仅仅源代码就有3个多G,按照国内访问git的速度几乎不可能下载完,(这个项目是一个套娃项目,其中里面的子module对应着github上的别的项目,其中qemu的部分还不是在github的服务器上,需要请求另一个美国的服务器,即使翻墙走代理速度也是慢的离谱)
解决方法:
1.翻墙走代理,对git进行配置,该项目是套娃项目所以在clone时要进行–recursive.
2.使用网上的百度云,没会员也是慢的离谱,https://zhayujie.com/mit6828-env.html 中有博主分享的百度云链接,加上会员很丝滑
配置好之后编译大约个把小时
Lab: Xv6 and Unix utilities
Boot xv6
这部分主要介绍的就是如何编译,如何使用qemu运行程序,通过Makefile文件在UPROGS加上自己的文件进行编译用户程序,使用make grade进行打分
sleep
这部分代码倒是不难,可以直接对sleep进行系统调用
pipe
这里使用管道对父子进程进行ipc,逻辑上也不难
primes
这个模块很有意思,要求实现一个并发的素数筛,网页给了一个csp的论文进行参考
其中素数筛得基本思路也就是多开线程每个线程筛一个质因子,然后把没有被筛掉的数字向子进程write然后通过进程的不断fork达到一个素数筛的目的
一个错误我卡了很久,就是没有检测read函数的返回值,我认为既然read函数是阻塞io既然没读到就会持续地阻塞下去,实际上不是
并发素数筛的核心逻辑
while(1)
{//child thread
parent_fd[0]=child_fd[0];
close(child_fd[1]);
pipe(child_fd);
buffer_num=0;
if(read(parent_fd[0],&buffer_num,sizeof(buffer_num))==0)
{
exit();
}
//printf("sta=%d %d\n",sta,parent_fd[0]);
thread_num=buffer_num;
//int pid=getpid();
printf("prime %d\n",thread_num);
if(fork())//parent thread
{
close(child_fd[0]);
while(read(parent_fd[0],&buffer_num,sizeof(buffer_num))==4)
{
if(buffer_num%thread_num)
{
//pid=getpid();
//printf("write buffer_num=%d thread_num=%d,pid=%d\n",buffer_num,thread_num,pid);
write(child_fd[1],&buffer_num,sizeof(buffer_num));
}
}
exit();
}
pipe
关于pipe的一段代码
int p[2];
char *argv[2];
argv[0] = "wc";
argv[1] = 0;
pipe(p);
if(fork() == 0) {
close(0);
dup(p[0]);
close(p[0]);
close(p[1]);
exec("/bin/wc", argv);
} else
{
write(p[1], "hello world\n", 12);
close(p[0]);
close(p[1]);
}
这是一段很有意思的代码
这段代码通过pipe,close和dup成功把wc的标准输入绑定到了另一个程序的管道中
阻塞read直到不可能到达新数据的一个原因是在执行wc之前,让子进程关闭管道的写入端非常重要。因为如果wc的一个文件描述符指向了管道的写入端,wc将永远不会看到文件末尾。
find
ls 的递归版使用dfs搜索所有的文件夹
其中目录是一种特殊的文件
注意不能递归./ ../否则会无限递归
xargs
这个程序可以执行shell命令 并且把这个程序的标准输入当作argv传入执行的shell
可以用来使用管道把别的程序的输出用管道成为另一个程序的参数
注意好处理字符串 使用fork配合exec即可实现
xv6 shell
features
1.可以调用一些用户态的程序
2.允许cd切换目录
3.对调用的程序传参
4.使用|进行输出输入和管道的绑定
5.使用<>进行io重定向
6.使用&后台运行
7.使用;分割指令从做到右依次执行
shell只是一个用户程序,而不是内核的一部分,shell没有什么特别之处反倒说明了系统调用接口的力量。
完成以上的features就符合了shell的基本需求,正要写一个shell,参考xv6的shell源码,下面从整体上来记录一下xv如何实现shell
parser
还记之前ccf的第一是实现一个计算器似乎要完成加减乘除,当时好像就过了样例点.现在回想起来实现其实就是语法树这一套
由于|();等调用会产生相当多复杂的命令和处理方式,实质上的处理时的各种递归调用其实就是一个语法树,这个树由5种基本命令构成
#define EXEC 1
#define REDIR 2
#define PIPE 3
#define LIST 4
#define BACK 5
struct cmd {
int type;
};
struct execcmd {
int type;
char *argv[MAXARGS];
char *eargv[MAXARGS];
};
struct redircmd {
int type;
struct cmd *cmd;
char *file;
char *efile;
int mode;
int fd;
};
struct pipecmd {
int type;
struct cmd *left;
struct cmd *right;
};
struct listcmd {
int type;
struct cmd *left;
struct cmd *right;
};
struct backcmd {
int type;
struct cmd *cmd;
};
以上代码在c语言中完成了继承和多态的性质,配合这两个性质,所有的