MIT 6.s081 lab1.3–primes
No.1 写在前面的话
这是我目前为止觉得最难的实验【比xargs和find要难】,在看完提示后也丝毫没有进展,因为想要自己做不依靠他人文章,费了不少时间。不过,在理清思路后写起来就容易得多【虽然中间实现素数筛读取那里不会,参考了他人】,但是基本的框架已经形成。那么,开始吧!
No.2 预备知识
为了搞懂素数筛以及如何创建多个管道,下面的链接是必要参考的:
埃拉托色尼筛:primes【里面的代码必须亲自调试理解】
进程拷贝:[创建多个进程](fork 循环创建多个子进程_fork多个子进程-CSDN博客)【进一步理解管道】
通过理解素数筛的机制,大概知道,它通过数组置一,如果为倍数则置零排除的方法取得结果;而如果要利用fork,父进程写入数字,由子进程进行连续筛选,那么就得是嵌套结构。自然,main函数如果在fork中又fork,筛选进程就得套很多fork,这必然不现实。那么就利用了递归思想。
初步的思路如下:【实际上,在我参看文章时,发现自己的思路已经基本正确了】
画图示意;
No.3 primes的实现
有了上述的思路,实现就好写多了,根据实验提示,代码如下:
来自[爱你哦小猪猪](操作系统MIT6.S081:Lab1->Unix utilities_mit6s081详解_爱你哦小猪猪的博客-CSDN博客),我的代码和他写的基本一致,但是不知为何总是prime 0?,所以在这里为了避免大家出错,贴上正确的代码。如果有大佬知道怎么解决,请评论我,谢谢(_)
#include "kernel/types.h"
#include "user/user.h"
#define N 35
void solve(int* pl){
int prime;
//读取一个质数,如果没有数据了则直接返回
if(read(pl[0], &prime, sizeof(prime)) == 0) return;
printf("prime %d\n", prime); //打印输出
int pr[2];
pipe(pr); //right管道
int pid = fork();
if(pid > 0){ //父进程
close(pr[0]); //关闭right管道读取端
//将当前质数的倍数筛出去,剩下的写入right管道写入端
for(int t; read(pl[0], &t, sizeof(t)) != 0;){
if(t % prime != 0)
write(pr[1], &t, sizeof(t));
}
close(pr[1]); //关闭right管道写入端
close(pl[0]); //关闭left管道读取端
wait(0); //等待子进程完成
exit(0); //正确退出
}else if(pid == 0){
close(pr[1]); //关闭right管道写入端
close(pl[0]); //关闭left管道写入端
solve(pr); //递归处理右邻
close(pr[0]); //关闭right管道读取端
exit(0); //正确退出
}
else{ //创建进程失败
close(pl[0]);
close(pr[1]);
close(pr[0]);
exit(1); //错误退出
}
}
int main(int argc, char argv[]){
int pl[2]; //lefts
pipe(pl); //创建left管道
int pid = fork();
if(pid > 0){ //主进程
close(pl[0]); //关闭left管道读取端
for(int i =2 ; i <= N; ++i){ //将2-35写入left管道写入端
write(pl[1], &i, sizeof(i));
}
close(pl[1]); //关闭left管道写入端
wait(0); //等待子进程完成
exit(0); //成功退出
}else if(pid == 0){ //子进程
close(pl[1]); //关闭left管道写入端
solve(pl); //进行筛选
close(pl[0]); //关闭left管道读取端
exit(0); //正确退出
}else{ //创建进程错误
close(pl[1]);
close(pl[0]);
exit(1);
}
}
接下来这个是传入数组的代码,我不太理解为什么中途没有关闭管道,但运行后结果也是对的,贴上链接供大家参考:MIT操作系统实验lab1(案例:primes(质数筛选)附代码、详解)
虽然自己的代码因为未知原因通不过,但看完他人的文章知道自己的代码写的是基本正确的,也满足了,了解思想很重要。【用时两天】