进程之间通信的方式有很多种,主要包括

  1. 管道
  2. 命名管道
  3. 信号
  4. 消息队列
  5. 共享内存
  6. 信号量
  7. 套接字

其中,管道是最早的一种进程间通信机制,主要适用于具有亲缘关系之间的进程间通信,比如,父进程与子进程之间,或者同一个父进程的两个子进程之间。同时,管道是一中半双工的通信,数据只能单向流动,从一段写入,另外一段读出。下面通过几个例子来看一下管道如何使用。

1. 函数原型

 #include <unistd.h>
int pipe(int fd[2]);

pipe函数创建一个管道,其声明在unistd.h当中,传入参数是一个int[2]数组,返回值如果为0表示,pipe创建成功,同时fd数组中存储两个文件描述符,fd[1]指向管道的写端,fd[0]指向管道的读端;如果小于0,表示创建失败。

2. 第一个例子

下面看一个最简单的例子

#include<unistd.h>
#include<stdio.h>
int main()
{
    int n ;
    int fd[2];
    char buf[1024];
    // 创建管道
    if (pipe(fd) < 0) {
        perror("pipe error");
    }
    write(fd[1], "hello world\n", 12);
    n = read(fd[0], buf, 1024);
    printf("%s",buf);
    return 0;
}

上面的代码很简单,就是创建了一个管道,然后向写端写入“hello world\n”字符串,然后从读端读出,存储到buf数组中,最后打印到屏幕。这个例子可以用下面的示意图来表示:
进程通信(1)管道

管道像一根单向的水管,数据像水一样从一端流入,从另一端流出。管道也是有缓冲空间的,如果一直写入不读取,那么缓冲空间会被占满,再往里面写数据就会失败(就像水管的流出端被关闭,水不能再流入一样),同样的,如果只读不写,那么数据被读完之后,就没有东西可读了,再次读取也会失败。这个例子显然是没什么用途的,但是可以帮助我们理解什么是管道。

2. 第二个例子

再来看第二个例子

#include<unistd.h>
#include<stdio.h>
int main()
{
    int n ;
    int status;
    pid_t pid;
    int fd[2];
    char buf[1024];
    // 创建管道
    if (pipe(fd) < 0) {
        perror("pipe error");
    }
    // 创建子进程
    if ((pid = fork()) < 0) {
        perror("fork error");
    } else if (pid == 0) {
        // 子进程读取管道
        n = read(fd[0], buf, 1024);  
        printf("%s",buf);
    } else {
       // 父进程写入管道
        write(fd[1], "hello world\n", 12);
        // 父进程等待子进程结束
        if (wait(&status) < 0) {
            perror("wait error");
        }
    }
    return 0;
}

第二个例子比第一个例子稍微复杂了一些。首先,父进程创建了一个管道,然后fork出一个子进程。在子进程中读取管道内容,并打印内容,然后返回结束进程;在父进程中向管道写入字符串,然后等待子进程结束,最后结束进程。这个例子可以用下面的图来表示
进程通信(1)管道
执行fork之后,主进程创建了一个子进程,子进程完全复制父进程的虚拟内存空间(这个说法其实不严谨,见后文),同时继承父进程打开的文件等资源,所以两个描述符也被继承下来,两个进程的fd数组具有相同的值,并且指向同样的pipe端口。因此,父子进程之前可以通过pipe实现通信。一般地,我们会在pipe写入端进程关闭读端,在pipe读入端关闭写端。在上面的代码中,加入两行

#include<unistd.h>
#include<stdio.h>
int main()
{
    int n ;
    int status;
    pid_t pid;
    int fd[2];
    char buf[1024];
    // 创建管道
    if (pipe(fd) < 0) {
        perror("pipe error");
    }
    // 创建子进程
    if ((pid = fork()) < 0) {
        perror("fork error");
    } else if (pid == 0) {
    	// 关闭写入端文件描述符
    	close(fd[1]);
        // 子进程读取管道
        n = read(fd[0], buf, 1024);  
        printf("%s",buf);
    } else {
    	// 关闭写入端文件描述符
    	close(fd[0]);
       // 父进程写入管道
        write(fd[1], "hello world\n", 12);
        // 父进程等待子进程结束
        if (wait(&status) < 0) {
            perror("wait error");
        }
    }
    return 0;
}

那么进程模型变成这样
进程通信(1)管道

相关文章:

  • 2021-06-07
  • 2021-10-02
  • 2022-12-23
  • 2021-07-21
  • 2021-12-04
  • 2021-12-04
  • 2021-12-04
  • 2021-09-25
猜你喜欢
  • 2021-12-04
  • 2021-09-19
  • 2021-12-04
  • 2021-08-17
  • 2021-09-15
  • 2021-08-25
相关资源
相似解决方案