6.s081 lab1.2--pingpong【详细解释小白向】

MIT 6.s081 lab1.2–pingpong

No.1 写在前面的话

这一节虽说难度是easy,但是我也依旧花了将近一天时间去了解pipe的概念,前后完成差不多两天。同时因为一些不可控的原因(自己的代码和别人的一致但是测试不通过),走了很长时间弯路,尽管不知道为什么会发生上述原因,但至少通过换另外一种方法通过测试了。【理解思想能写的差不多已经很棒了我!】

No.2 pipe的概念

pipe属于进程间通信的一种,无名和有名的区别在这里并不涉及。可以知道的是,进程会通过创建一个在内核中的管道来给另外的进程传输数据。

管道利用文件描述符进行数据的读写操作,0端,1端,fd[1]的输出是fd[0]的输入。管道的示意图如下:

Imgur

必须要注意的是,父与子进程的文件描述符是相互独立的,pipe()只是开启了一个通道,并不是说管道的文件描述符是0&1,而是父与子进程读取管道的操作规则是0&1。

管道读取数据的四种的情况

(1)读端不读,写端一直写

Imgur

(2)写端不写,但是读端一直读

Imgur

(3)读端一直读,且fd[0]保持打开,而写端写了一部分数据不写了,并且关闭fd[1]。

Imgur

如果一个管道读端一直在读数据,而管道写端的引⽤计数⼤于0决定管道是否会堵塞,引用计数大于0,只读不写会导致管道堵塞。

(4)读端读了一部分数据,不读了且关闭fd[0],写端一直在写且f[1]还保持打开状态。

Imgur

这些将在实验之中用到,非常头疼。

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;

整体思路如示意图:

Imgur

但,就像我上面所述,它未通过。看了其他人的文章进行修改后也依然报错,让我很迷惑,最后采纳了单管道【没错就是为了通过用例】:

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;
}

然后就通过了,很是不知所措。【用时一天】

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值