本文已参与「新人创作礼」活动,一起开启掘金创作之路。
注:本实验完成周期较长,前半部分摘自本人此前发表在CSDN上的博客文章,后半部分首发于稀土掘金平台。
sleep
环境配置
租了个腾讯云服务器,并将系统设为教程推荐的ubuntu 20.4版本,具体配置细节lab要求里的“lab tools page”有很详细的介绍,这里不再赘述。
Boot xv6
- 在执行
$ git clone git://g.csail.mit.edu/xv6-labs-2021命令时,提示我没有clone权限。
遇到这种情况只需要返回上级目录,然后将打算用于存放仓库代码的目录的权限设为o+w即可。 代码:
sudo chmod o+w dirname
dirname为存放仓库代码的目录
- 生成和运行xv6是以下命令:
make qemu
之后可以进入xv6,这是一个类Unix的操作系统,我们之后的任务就是往其中增加功能。
- 接下来先熟悉一下这个操作系统:
先试试
ls命令 (注:我是先做了sleep功能再写这篇文章,所以此处显示有sleep,一开始打开的时候是没有的)
试试echo:
echo hello world!
试试mkdir:
mkdir test
不过有点bug,我cd进去该目录之后无法使用功能命令了
对此,我在xv6的课本中找到了这样一段描述,不知道会不会和这个有关系:
一方面和cd本身的机制有关,另一方面是因为xv6还没能像Unix一样完善。
- 关于退出:
lab要求中有这样的提示:
我试了试,指的是在同时按下ctrl和字母a键之后快速松手并马上按x键。
关于实现sleep功能:
- 在user/ 下新建sleep.c文件并打开:(使用vim打开一个不存在的文件将会自动创建)
vim sleep.c
- 编写代码
参照教程提示:
代码如下:
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
int main(int argc, char *argv[]){
int n;
if (argc != 2){
fprintf(2, "Please enter a number!\n");
exit(1);
}else{
n = atoi(argv[1]);
sleep(n);
exit(0);
}
}
关于功能添加与评测
-
在.c文件写好代码之后,还要把它添加到功能列表中。
返回上级目录,打开Makefile文件,在UPROGS配置下追加一行代码。 (写到这里的时候没打开相应文件,就不放代码了,不过很简单的,上面的指令什么格式,这个依葫芦画瓢就行) (vim操作回顾:在normal模式下输入/可以搜索关键词,按n查找下一个结果,按N查找上一个结果)
-
之后make qemu,会看到如下结果:
输入ls可以看到ls出现在选项中,如前面的图展示的。
-
来试试功能:
输入该命令后,可观察到几秒内 光标在闪烁,但是没有其他的东西,新的$也没出现,说明进程确实是在休眠。
-
评测
这个make grade命令出现在sleep功能要求之前,好奇宝宝本人手贱没看清楚说明就输入了,结果评测出0分 (现在知道它的含金量了吧?指的是当前lab的所有测试)。
如果做完了其中一个,比如只完成了sleep功能,想只测sleep的,可以使用这个命令:
$ ./grade-lab-util sleep
或者这个:
$ make GRADEFLAGS=sleep grade
我看网上有博客文章说要改这个评测脚本的测试路径和测试命令,但我试了试,反正我这边不改也行的。
结果
看到评测结果,还是很有成就感滴!
pingpong
背景知识
管道可用于进程间通信。
- 从管道一端写入数据(write,文件描述符为 1)),数据在管道中流动,可从另外一端被读取(read, 文件描述符为 0)。
- 数据读取或写入的时候,另一端的管道会被 关闭 。
- fork()之后,子进程的pid为0,父进程的pid为原来的,两者互不干扰。
代码
#include "kernel/types.h"
#include "user/user.h"
int main(int argc,char *argv[]){
int p1[2];
int p2[2];
int pid;
char buf[1];
pipe(p1);
pipe(p2);
pid=fork();
if(pid<0) exit(1);
else if(pid==0){
close(p1[1]);
close(p2[0]);
read(p1[0],buf,1);
printf("%d: received ping\n",getpid());
write(p2[1]," ",1);
close(p1[0]);
close(p2[1]);
}else{
close(p1[0]);
close(p2[1]);
write(p1[1]," ",1);
read(p2[0],buf,1);
printf("%d: received pong\n",getpid());
close(p1[1]);
close(p2[0]);
}
exit(0);
}
一点额外收获(vim相关)
其实这个实验本身是easy程度,不过因为比较头铁,想要直接在vim里面写代码,所以在折腾它的过程中学到了很多vim操作。
关于vim配置:
我想要给文本设置行号,让vim像vscode一样支持符号匹配,代码缩进,于是在~/.vimrc文件中写入如下代码: (参考这篇文章:www.cnblogs.com/write-hua/p… )
set nu
set smartindent
:inoremap ( ()<ESC>i
:inoremap ) <c-r>=ClosePair(')')<CR>
:inoremap { {<CR>}<ESC>O
:inoremap } <c-r>=ClosePair('}')<CR>
:inoremap [ []<ESC>i
:inoremap ] <c-r>=ClosePair(']')<CR>
:inoremap " ""<ESC>i
:inoremap ' ''<ESC>i
比如我后面想要全选文本,如果直接选中文本,会把行号也选中,那就要对 本文件 取消行号: 在命令行模式下:
:set nonu
关于vim模式
在写代码的过程中对vim模式有了更清晰直观的认识。(以下是我本次用到的)
primes
思路
教程中提到的论文是这么写的:
一开始读入所有的数,2是素数,打印它,判断进程管道中的其他数是否能被该数整除,如果不能(说明可能是素数),fork一个子进程,把数传过去(递归实现该过程,也做到了实验要求中的连接管道的左边和右边),这样一层一层传,并打印出当前进程最小的数(一定是素数),结束条件是达到35这个数。
注意要及时关闭管道不用的端。
代码截图
(发现在 独立的 Linux系统写代码,然后想复制vim中的代码到外部始终很麻烦,那就这样吧,我截个图算了)
声明及主函数
子函数:递归操作,数的整除操作
额外收获
- 才知道vim的复制粘贴(yy p)最多只支持50行代码(应该是寄存器容量的原因吧)
注意!!接下来的两个实验都是应该先make clean ,再make qemu!!!
find
评测效果
代码截图
(从服务器里的vim里复制粘贴代码真的太难了,一次只能选中当前页面范围的,手滑还容易无,真是谁用谁知道,我还是贴图叭)
- 教程提到读取目录操作可以参考user/ls.c的代码,其实可以大段大段地用相应代码,只需要改改参数,具体内容修改一点点。
教程说字符串不要用==,用strcmp
vim技巧篇:
- 替换 当前行 整个单词:(命令行模式下)
: s /ori/new
ori为原单词,new为替换后的单词
- 对当前文件启用鼠标复制粘贴:(命令行模式下)
set mouse-=a
我的理解是这里的a指的是all,就是原本键盘能有的功能鼠标也能有(mouse=a),但是我们不会用,反而觉得鼠标啥也干不了了,于是现在把这个功能禁止掉,就反而觉得可以复制粘贴了。
- 把当前vim文件的行号去掉
set nonu
(no number的意思,同理,set nu就是显示行号)
xargs
注意事项
- 教程里有这样一句话:
要注意对分隔命令的回车进行处理。
- kernel/param.h中的 MAXARG:32
代码截图及解析
(尝试理解看懂大佬的代码)
- command指针会依次指向xargs后接的每条命令。
- bf:buffer
- paramv:用于存放命令信息的数组空间
- count:命令条数,遇到空格会加一
- cursor:游标,用于记录读取某条命令的单个字符的位置,每读到一个新命令,游标会置0
- flag:当当前为空格且前面有参数的时候flag为1
- 读取命令,循环结束条件是读取完毕且用户按下回车
- 如果已经读取完了就跳出整个while循环
- 通过创建子进程去执行任务,否则等待
运行结果截图
为什么会有这么多 $ 呢?
教程里说是因为shell内核以为这些命令来源于终端(而不是实际上的只从文件中读取),所以给每条命令都附上了$。
参考资料
- MIT 6.s081课程官网实验部分 pdos.csail.mit.edu/6.S081/2021…
- cs自学指南中的操作系统部分及其中提到的参考博客链接,特别是其中一位叫樊潇的大佬,他的代码和思路对我产生了很大的帮助! csdiy.wiki/%E6%93%8D%E…
樊潇大佬的博客:fanxiao.tech/posts/MIT-6…