进程间通信——管道

1、管道

管道通信——只能在一台电脑上面运行。

管道:一定是半双工的通信,只能流向一个方向(规定流向);

管道是一个进程间通信的概念,在要通信的进程间构建一个单向的数据流动的通道。数据通过该通道从一个进程流向另一个进程时是具有时间先后顺序的。就像是在进程间架起了一个"管道"。

模型分析

管道的实现:在Linux(Posix标准)的操作系统下,管道是通过文件来实现的。在后来的操作系统的发展中,依然使用了文件的访问方式来使用管道,但是具体的管道已经从外存挪到了内存。

2、无名管道

  只能用于亲缘关系的父子进程。

pipe()方法: int pipe(int pipefd[2]);

pipe方法会创建一对文件描述符,他们指向同一个管道文件,这2个文件描述符会放在参数pipefd数组中。pipefd[0]持有对管道文件的读,pipefd[1]持有对管道文件的写。

管道文件在pipe操作的时候会被打开2次,分别以只读方式和只写方式打开。从管道的基本逻辑出发,其实就是管道的出口和入口。只要将这2个文件描述符分别交给不同的进程,就能够实现2个进程之间的单向通信。

管道通信的简单代码:

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<fcntl.h>

int main(void){
    pid_t pid;
    int fd[2];
    char buf[80];

    int res = pipe(fd);  //fd[0]  fd[1]
    if(res < 0){ 
        perror("");
        return -1; 
    }   
    pid = fork();
    if(pid == 0){ 
        close(fd[0]);
        sleep(2);
        sprintf(buf, "I am child, who are you");
        write(fd[1], buf, strlen(buf));
        close(fd[1]);
    }else if(pid > 0){
        close(fd[1]); 
        read(fd[0], buf, sizeof(buf));
        printf("Received a msg buf = %s\n", buf);
        close(fd[0]);
    }else{
        perror("");
    }

    return 0;
}

运行结果

中间暂停了2秒,才出现这个结果。

管道通信的特征:

(1)、如果管道为空,从管道读取数据的一方会阻塞。直到管道中有新的数据为止。

  (2)、管道的数据通信具有FIFO特性,这样可以避免数据的混乱。

  (3)、管道数据的读取与发送并没有次数限制,而是管道是否为空时最重要的指标。

  (4)、这种管道的使用具有一个最大的局限性:只适用于父子进程之间。从程序的设计中可以看到,管道的创建是父进程完成的,而且是在创建子进程之前,从而才使得子进程拥有了管道文件描述符,才能够使得父子进程约定持有管道的入口或出口。

  (5)、一个管道只能实现单向的数据流动。

3、问题 : 怎样实现一个父子进程一问一答的通信模式?

分析:因为管道具有单向的数据流动的特性,是半双工的,要实现双方的通信,那就必须是全双工。此时就需要2个管道完成这个功能。

模型分析

代码实现父子进程之间的一问一答的通信:

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

int main(void){
    int fd1[2];  //管道1的文件描述符
    int fd2[2];  //管道2的文件描述符
    int res = pipe(fd1);
    if(res == -1){
        perror("");
    }   
    res = pipe(fd2);
    if(res == -1){
        perror("");
    }   

    pid_t pid;
    pid = fork();
    if(pid == 0){ 
        char child_speak[80] = {‘h‘};;
        char buf[80];
        close(fd1[0]);
        close(fd2[1]);

        while(strncmp(child_speak, "quit", 4) != 0){ 
            printf("child :>");
            scanf("%s", child_speak);
            sprintf(buf, "%s", child_speak);
            write(fd1[1], buf, strlen(buf));

            read(fd2[0], buf, sizeof(buf));

        }

        close(fd1[1]);
        close(fd2[0]);

    }else if(pid > 0){
        char parent_speak[80] = {‘w‘};
        char buf[80];
        close(fd1[1]);
        close(fd2[0]);

        while(strncmp(parent_speak, "quit", 4) != 0){
            read(fd1[0], buf, sizeof(buf));

            printf("parent :>");
            scanf("%s", parent_speak);
            sprintf(buf, "%s", parent_speak);
            write(fd2[1], buf, strlen(buf));
        }
            
        close(fd1[0]);
        close(fd2[1]);
        int status;
        wait(&status);

    }else{
        perror("");
    }

    return 0;
}

运行结果

(2)、popen()方法

popen()能够执行命令行内容,fork一个子进程,让子进程execv命令行,将命令行的标准输出内容写入管道文件,然后将管道文件的FIFE指针作为返回值返回。

popen依然是父子进程间的通信。

实现代码如下:

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>

int main(void){
    FILE *fp = popen("ls -l", "r");
    if(fp == NULL){
        perror("popen Error");
        return -1; 
    }   

    char buf[80];
    int ret;
    while(!feof(fp)){
        ret = fread(buf, sizeof(buf), 1, fp);
        if(ret == 0){ 
            perror("");
            break;
        }   
        printf("%s", buf);

    }   
    
    return 0;
}

运行结果

4、有名管道(命名管道)

普通的管道只能在父子进程之间通信,如果需要使用管道达成非父子进程间的通信,则需要特殊手段。有名管道就是这个技术,其含义就是:给一个管道命名,只要知道该管道名称的进程就可以使用该管道进行通信。

需要使用的API mkfifo

初级使用的有名管道

代码如下:写的一方

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

int main(void){
    int ret;
    char buf[80];

    ret = mkfifo("./tmp", 0755);  //创建一个管道文件
    if(ret != 0){ 
        perror("");
        return -1; 
    }   

    int fd = open("./tmp", O_CREAT | O_WRONLY);  //只写方式打开管道文件。返回管道文件的文件描述符。
    if(fd < 0){ 
        perror("");
        return -1; 
    }   

    while(1){
        memset(buf, 0, sizeof(buf));
        sprintf(buf, "This is writter");
        write(fd, buf, strlen(buf));
        sleep(1);
    }

    return 0;
}

代码如下:读的一方

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include<fcntl.h>

int main(void){
    int fd; 
    char buf[80];

    fd = open("./tmp", O_APPEND, O_RDONLY); //以只读方式打开文件
    if(fd < 0){ 
        perror("");
        return -1; 
    }   

    while(1){
        memset(buf, 0, sizeof(buf));
        read(fd, buf, sizeof(buf));
        printf("Resd a msg : %s\n", buf);
    }   

    return 0;
}

运行结果

对有名管道文件的总结:

(1)、两个非父子进程通过有名管道来通信;

  (2)、管道文件只需要一个进程去创建,其它进程只要有,直接用就行了;

  (3)、mkfifo会在文件系统中创建一个管道文件,然后使其映射内存的一个特殊区域,凡是能够打开mkfifo创建的管道文件进程,都可以使用该文件实现FIFO的数据流动。

  (4)、数据在有名管道中如果被读取,则不会继续存在于有名管道中。

  (5)、管道文件不是磁盘上真真正正的文件,它是一块内存区域;

时间: 2024-08-07 00:18:43

进程间通信——管道的相关文章

Linux程序设计学习笔记----进程间通信——管道

转载请注明出处: http://blog.csdn.net/suool/article/details/38444149, 谢谢! 进程通信概述 在Linux系统中,进程是一个独立的资源管理单元,但是独立而不孤立,他们需要之间的通信,因此便需要一个进程间数据传递.异步.同步的机制,这个机制显然需要由OS来完成管理和维护.如下: 1.同一主机进程间数据交互机制:无名管道(PIPE),有名管道(FIFO),消息队列(Message Queue)和共享内存(Share Memory).无名管道多用于亲

Linux 进程间通信-管道

进程间通讯———管道 Linux 进程间通信-管道 进程是一个独立的资源分配单位,不同进程之间的资源是相互独立的,没有关联,不能在一个进程中直接访问另一个进程中的资源.但是,进程不是孤立的,不同的进程之间需要信息的交换以及状态的传递,因此需要进程间数据传递.同步与异步的机制. 此篇博文记录管道. 管道pipe 管道是进程间通信的主要手段之一.一个管道实际上就是个只存在于内存中的文件,对这个文件的操作要通过两个已经打开文件进行,它们分别代表管道的两端.管道是一种特殊的文件,它不属于某一种文件系统,

进程间通信---管道

每个进程各自有着不同的用户地址空间,任何一个进程的全局变量在另一个进程中是看不到的,所以进程之间要交换数据必须通过内核,在内核中开辟一块缓冲区,进程1把数据从用户空间拷到内核缓冲区,进程2在从内核中把数据读走,内核提供的这种机制称为进程间通信. 进程间通信的本质:让不同的进程看到同一份系统资源. 进程通信方式:管道(pipe) pipe函数: #include <unistd.h> int pipe(int fd[2]); 调用pipe函数时在内核中开辟一块缓冲区(管道)用于通信,它有一个读端

进程间通信 ---- 管道与FIFO 用法技巧

1.管道的创建 1.1 mkfifo(const char *pathname,mode_t mode); 函数已隐含指定O_CREAT|O_EXCL,所以它要么创建一个新的FIFO,要么返回EEXIST错误(已存在). 所以在创建已存在FIFO或新的FIFO,应该先调用mkfifo,并检查返回值 是否是EEXIST错误,若是EEXIST错误,则调      用open函数. 2.FIFO或管道读写 2.1对管道或FIFO的write 总是往末尾添加数据,对管道或FIFO的read总是从头开始读

Linux学习笔记(12)-进程间通信|管道

Linux的进程间通信有几种方式,包括,管道,信号,信号灯,共享内存,消息队列和套接字等-- 现在一个个的开始学习! -------------------------------------------------- 管道是一个进程链接另一个进程的数据通道,它通常是把一个进程的输出,接到另一个进程的输入,从而传递数据. 在Linux的终端上,用单竖线|来表示,那么,这个符号可以做什么呢? 举个栗子,如果我用ps -ef命令,可以查看我当前所有的进程: 正如上图表示,显示出来的东西太多了,让人眼

进程间通信管道的应用

管道的概念: 管道是一种最基本的IPC机制,由pipe函数创建: #include<unistd.h> int pipe(int fileds[2]); 调用pipe函数时在内核中开辟一块缓冲区用于通信,它有一个读端一个写端,然后通过filedes参数传出给用户程序两个文件描述符,filedes[0]指向管道的读端,filedes[1]指向管道的写端. 进程在管道间通信: 1.父进程调用pipe开辟管道,得到两个文件描述符指向管道的两端. 2.父进程调用fork创建子进程,子进程有两个文件描述

【linux高级程序设计】(第九章)进程间通信-管道 3

有名管道 无名管道和有名管道: 1. 管道是特殊类型的文件,在满足先入先出的原则写可以读写,不能定位读写位置. 2.管道是单向的. 3.无名管道阻塞于读写位置,而有名管道阻塞在创建位置. 4.无名管道一般只用于亲缘关系进程间通信:有名管道以磁盘文件的方式存在,可以实现本机任意两进程间通信. shell创建有名管道 mknod 管道名 p  //创建名为PIPETEST的有名管道 mknod为命令 p是参数,表示有名管道 指令 > 管道名 &   //将指令结果输入到到管道文件中 指令 <

Linux进程间通信--管道

管道概述及相关API应用 1.1 管道相关的关键概念 管道是Linux支持的最初Unix IPC形式之一,具有以下特点: 管道是半双工的,数据只能向一个方向流动:需要双方通信时,需要建立起两个管道: 只能用于父子进程或者兄弟进程之间(具有亲缘关系的进程): 单独构成一种独立的文件系统:管道对于管道两端的进程而言,就是一个文件,但它不是普通的文件,它不属于某种文件系统,而是自立门户,单独构成一种文件系统,并且只存在与内存中. 数据的读出和写入:一个进程向管道中写的内容被管道另一端的进程读出.写入的

linux学习:进程间通信—管道

1.进程间通信当中一种比較简单的方法是管道操作 /* ============================================================================ Name : Test.c Author : wangchuan Version : Copyright : Your copyright notice Description : Hello World in C, Ansi-style ==========================