【Linux】匿名管道

橙色

匿名管道

  • 管道也叫无名(匿名)管道,是 UNIX 系统 IPC(进程间通信) 的最古老的形式。

  • 统计一个目录中文件的数目命令 ls | wc -l ,为了执行该命令,shell创建了两个进程来分别执行 ls 和 wc,ls 将数据写入管道,wc 从管道中读取数据(单向字节流管道)。

在这里插入图片描述

管道特点

  • 管道其实是一个在内核内存中维护的缓冲器。

  • 管道拥有文件特质:读操作、写操作,匿名管道没有文件实体,有名管道有文件实体但不存储数据。

  • 一个管道是一个顺序字节流,类似于双指针环形队列。

  • 匿名进程只用用于具有关系的进程之间的通信。

  • 在管道中的数据的传递方向是单向的,一端用于写入,一端用于读取,管道是半双工的。

  • 从管道读数据是一次性操作,数据一旦被读走,它就从管道中被抛弃,释放空间以便写
    更多的数据,在管道中无法使用lseek ()来随机的访问数据

  • 匿名管道只能在具有公共祖先的进程(父进程与子进程,或者两个兄弟进程,具有亲缘关系)之间使用。

在这里插入图片描述

父子进程通过匿名管道通信

/*
    #include <unistd.h>
    int pipe(int pipefd[2]);
        功能:创建一个匿名管道,用来进程间通信。
        参数:int pipefd[2] 这个数组是一个传出参数。
            pipefd[0] 对应的是管道的读端
            pipefd[1] 对应的是管道的写端
        返回值:
            成功 0
            失败 -1

    管道默认是阻塞的:如果管道中没有数据,read阻塞,如果管道满了,write阻塞

    注意:匿名管道只能用于具有关系的进程之间的通信(父子进程,兄弟进程)
*/

代码示例:

// 子进程发送数据给父进程,父进程读取到数据输出
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h> 

int main() {

    // 在fork之前创建管道,如果在之后的话父、子进程都会创建,那就不可能是同一个文件了
    int pipefd[2];
    int ret = pipe(pipefd);
    if(ret == -1) {
        perror("pipe");
        exit(0);
    }

    // 创建子进程
    pid_t pid = fork();
    if(pid > 0) {
        // 父进程
        printf("i am parent process, pid : %d\n", getpid());

        // 关闭写端
        close(pipefd[1]);
        
        // 从管道的读取端读取数据
        char buf[1024] = {0};
        while(1) {
            int len = read(pipefd[0], buf, sizeof(buf));
            printf("parent recv : %s, pid : %d\n", buf, getpid());
            
            // 向管道中写入数据
            //char * str = "hello,i am parent";
            //write(pipefd[1], str, strlen(str));
            //sleep(1);
        }

    } else if(pid == 0){
        // 子进程
        printf("i am child process, pid : %d\n", getpid());
        // 关闭读端
        close(pipefd[0]);
        char buf[1024] = {0};
        while(1) {
            // 向管道中写入数据
            char * str = "hello,i am child";
            write(pipefd[1], str, strlen(str));
            sleep(1);

            // int len = read(pipefd[0], buf, sizeof(buf));
            // printf("child recv : %s, pid : %d\n", buf, getpid());
            // bzero(buf, 1024);
        }
        
    }
    return 0;
}

在这里插入图片描述
可以看到,父进程会一直的读取子进程往管道中写入的内容。

该结果显示的是子进程写入,父进程读取。但父进程写入,子进程读取也是可以的。看代码中父进程和子进程注释的部分

匿名管道通信案例

该程序的作用:通过fork创建了子进程,子进程通过execlp(也就是ps aux)来读取每个进程的相关信息,并将其写入到创建好的匿名通道中。而父进程则从匿名通道中读取相关的信息,并可以对特定的我们想要的信息作筛选(本代码中并未进行筛选,仅仅将父进程从匿名通道中所读取到的所有信息打印了出来)

#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wait.h>

int main() {

    // 创建一个管道
    int fd[2];
    int ret = pipe(fd);

    if(ret == -1) {
        perror("pipe");
        exit(0);
    }

    // 创建子进程
    pid_t pid = fork();

    if(pid > 0) {
        // 父进程
        // 关闭写端
        close(fd[1]);
        // 从管道中读取
        char buf[1024] = {0};

        int len = -1;
        while((len = read(fd[0], buf, sizeof(buf) - 1)) > 0) {
            // 过滤数据输出
            printf("%s", buf);
            memset(buf, 0, 1024);
        }

        wait(NULL);//最后wait去回收子进程的资源

    } else if(pid == 0) {
        // 子进程
        // 关闭读端
        close(fd[0]);

        // 文件描述符的重定向 stdout_fileno -> fd[1],这样后面操作的输出就会在fd[1],而不是终端
        dup2(fd[1], STDOUT_FILENO);
        // 执行 ps aux
        execlp("ps", "ps", "aux", NULL);
        perror("execlp");
        exit(0);
    } else {
        perror("fork");
        exit(0);
    }


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

努力学习的小马

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值