MIT操作系统实验lab1(pingpong案例:附代码、详解)

1.题目描述:在xv6上实现pingpong程序,即两个进程在管道两侧来回通信。父进程将”ping”写入管道,子进程从管道将其读出并打印<pid>:received ping ,其中<pid>是子进程的进程ID。子进程从父进程收到字符串后,将”pong“写入另一个管道,然后由父进程从该管道读取并打印<pid>:received pong,其中<pid>是父进程的进程ID。请将代码写在user/pingpong.c文件中。运行效果应该如下:

2. 实验原理:

先附上参考链接。

(1)官方网址:https://pdos.csail.mit.edu/6.828/2020/index.html

(2)xv6 book:https://pdos.csail.mit.edu/6.828/2020/xv6/book-riscv-rev1.pdf

(3)原理概念:

管道(pipe)是一种最基本的进程间通信机制。管道分为 读出端 和 写入端 两个部分,进程可以向写端写入数据,也可以从读端读出数据。通过pipe系统调用,内核会为用户进程创建管道,同时返回两个文件描述符,用以描述管道的读写端,例如:

int p[2];
int ret;
ret = pipe(p); /*正常创建后,p[1]为管道写入端,p[0]为管道读出端*/ 

 通过文件描述符,程序可以按读写文件的形式读写管道,例如:

int write = write(p[1], buffer, n);
int read = read(p[0], buffer, n);

 进程通常只持有某个管道的读出端或者写入端,因此使用的时候需要将另一端关闭,例如(一般通过fork()开启一个新的进程,父进程和子进程分别实现对管道流的读和写):

/* 子进程读管道,父进程写管道 */
int ret = fork();
if (ret == 0) { 
    /* 子进程 */
    close(p[1]); // 关闭写端
    ...
    read(...);
    ...
    close(p[0]); // 读取完成,关闭读端
} else if (ret>0) { 
    /* 父进程 */
    close(p[0]); // 关闭读端
    ...
    write(...);
    ...
    close(p[1]); // 写入完成,关闭写端
}

(4)实现思路: 

思路一:

单管道实现:采用一个管道,父进程实现"ping"的写入和"pong"的读取,子进程实现"pong"的写入和"ping"的读取。

(值得注意的是:由于管道内同时会放入字符串"ping"和"pong",所以如果不对两个字串读出和写入过程进行限制,会导致资源访问的冲突。因此,我们采取调用函数wait()的方式,保证同一时刻管道内只有一个资源的写入和读取)

思路二:

双管道实现:采用两个管道,如:管道1实现"ping"的读取和写入,管道2实现"pong"的读取和写入。实现过程同思路一,仍然采取父子进程的方式。

(虽然,思路二增加了一定的代码量。但是,避免了进程中调用wait()的做法,实现思路更加易于理解,并且两个管道各司其职不易出现错误)

3.代码实现:

(注意:子进程一定要使用exit(0)退出,否则会出现错误。始终记住,在linux系统中:父进程的wait()一定和子进程的exit()成对出现配合使用。同时,父进程也需要调用exit(0))

思路一代码:

#include "kernel/types.h"
#include "user.h"

int main(int argc,char* argv[]){
    //创建两个管道,分别实现ping、pong的读写
    int p[2];
    pipe(p);
    char readtext[10];//作为父进程和子进程的读出容器
    //子程序读出
    int pid = fork();
    if(pid==0){
        read(p[0],readtext,10);
        printf("%d: received %s\n",getpid(),readtext);
        write(p[1],"pong",10);
        exit(0);//子进程一定要退出
    }
    //父程序写入
    else{
        write(p[1],"ping",10);
        wait(0);//父进程阻塞,等待子进程读取
        read(p[0],readtext,10);
        printf("%d: received %s\n",getpid(),readtext);
        //exit(0);//父进程一定要退出
    }
    return 0;
}

(2)思路二代码大同小异,不过多赘述

猜你喜欢

转载自blog.csdn.net/m0_56603583/article/details/127342139