MIT 6.s081 lab1.2–pingpong
No.1 写在前面的话
这一节虽说难度是easy,但是我也依旧花了将近一天时间去了解pipe的概念,前后完成差不多两天。同时因为一些不可控的原因(自己的代码和别人的一致但是测试不通过),走了很长时间弯路,尽管不知道为什么会发生上述原因,但至少通过换另外一种方法通过测试了。【理解思想能写的差不多已经很棒了我!】
No.2 pipe的概念
pipe属于进程间通信的一种,无名和有名的区别在这里并不涉及。可以知道的是,进程会通过创建一个在内核中的管道来给另外的进程传输数据。
管道利用文件描述符进行数据的读写操作,0为读端,1为写端,fd[1]的输出是fd[0]的输入。管道的示意图如下:
必须要注意的是,父与子进程的文件描述符是相互独立的,pipe()只是开启了一个通道,并不是说管道的文件描述符是0&1,而是父与子进程读取管道的操作规则是0&1。
管道读取数据的四种的情况
(1)读端不读,写端一直写
(2)写端不写,但是读端一直读
(3)读端一直读,且fd[0]保持打开,而写端写了一部分数据不写了,并且关闭fd[1]。
如果一个管道读端一直在读数据,而管道写端的引⽤计数⼤于0决定管道是否会堵塞,引用计数大于0,只读不写会导致管道堵塞。
(4)读端读了一部分数据,不读了且关闭fd[0],写端一直在写且f[1]还保持打开状态。
这些将在实验之中用到,非常头疼。
No.3 管道的实现
- 题目要求两个管道,故需要开辟两个pipe。
- 父进程向子进程发送一个字节,利用write和read操作。那么这个字节需要什么来存储?利用一个缓冲区char buf进行初始化,随便什么。
- 需要退出,那么就要用exit,通过了解xv6文档中的实例,似乎需要父进程利用wait等子进程打印退出后再继续,结果双管道栽了,不需要。【原因后面写】
接下来就是具体的步骤,类似于两人远程开开关:
//创建两个管道
int fd1[2];
int fd2[2];
char buf[10];//暂时拿来存储数据,缓冲区
pipe(fd1);
pipe(fd2);
int procress = fork();
if(pipe(fd1) == -1){
printf("pipe failed.\n");
}
if(pipe(fd2) == -1){
printf("pipe failed.\n");
}
//开始写入
//父进程
if(procress > 0){
//向fd1里写入【1】
close(fd1[0]);//关闭fd1的读取端
write(fd1[1], "ping", 4);//随便什么都可以
close(fd1[1]);//写入后即时关闭写入端
//读取子进程发来的字节【4】
close(fd2[1]);
read(fd2[0], buf, 4);
printf("%d: received pong\n", getpid());
close(fd2[0]);
exit(0);
}
//子进程
else if(procress == 0){
//读取fd1【2】
close(fd1[1]); //关闭fd1的写入端,一定要记住父子是独立的
read(fd1[0], buf, 4);
printf("%d: received ping\n", getpid());
close(fd1[0]);//即时关闭读取端
//接下来由子进程写入fd2【3】
close(fd2[0]);//关闭读取端
write(fd2[1], "pong", 4);
close(fd2[1]);//写入完毕后关闭。
//为什么仅仅注释这个qemu可以通过但是text就乱了?
exit(0);
}
//错误的
else{
printf("Are u sure u have a procress?\n");
exit(1);
}
return 0;
整体思路如示意图:
但,就像我上面所述,它未通过。看了其他人的文章进行修改后也依然报错,让我很迷惑,最后采纳了单管道【没错就是为了通过用例】:
int main(int argc, char const *argv[])
{
int fd[2];
pipe(fd);
char buf[] = {'a'};
if(pipe(fd) == -1){
printf("pipe failed.\n");
}
if(fork() == 0){
read(fd[0], buf, sizeof(buf));
printf("%d: received ping\n", getpid());
write(fd[1], buf, sizeof(buf));
close(fd[0]);
close(fd[1]);
exit(0);
}
else{
write(fd[1], buf, sizeof(buf));
wait(0);//单管道与双管道不同,因为读写口在最后关闭,所以必须加以限制,不然会有冲突。【原因就在此,双管道读写灵活,所以不能wait】
read(fd[0], buf, sizeof(buf));
printf("%d: received pong\n", getpid());
close(fd[0]);
close(fd[1]);
exit(0);
//实际上,buf怎样应该是无所谓的,因为只是一个中转站,主要看何时close和wait
}
return 0;
}
然后就通过了,很是不知所措。【用时一天】