进程间通信---管道

来源:互联网 发布:剑灵女灵族捏脸数据图 编辑:程序博客网 时间:2024/06/08 01:16

1.什么是管道?
管道是一种最基本的进程通信机制,其实质是由内核管理的一个缓冲区,可以形象地认为管道的两端连接着两个需要进行通信的进程,其中一个进程进行信息输出,将数据写入管道; 另一个进程进行信息输入,从管道中读取信息。可以用下图表示管道的通信机制
2.管道的分类
(1)匿名管道,我们平时进行操作时,曾经接触过“ls | wc -l”之类的组合。这种命名由管道符号“|”连接的两个或者多个命令组成。这里使用的管道就是匿名管道。
在程序中使用匿名管道时,需要先创建一个管道, Linux 系统中创建匿名管
道的函数为 pipe()。

int pipe(int pipefd[2]);

其参数实质是一个文件描述符匿名管道只能在有亲缘关系的进程间使用。
匿名管道的通信过程:匿名管道利用 fork 机制
建立,刚创建出的管道,读写两端都连接在同一个进程上,当进程中调用 fork()
创建子进程后,父子进程共享文件描述符,因此子进程拥有与父进程相同的管道。pipe()创建管道后读端对应的文件描述符为 fd[0], 写端对应的文件描述符为fd[1].
fork()后的父子进程中文件描述符与管道的关系如下图
这里写图片描述
管道通过pipe()创建时自动打开,关闭应由用户实现。我们要进行父子之间通信。首先关闭父进程的读端(即close(fd[0])),关闭子进程的写端(close(fd[1]))。这样就可以父子通信,当程序结束时,匿名管道自动删除
父子通信的文件描述符与管道间的关系如下图
这里写图片描述
有亲缘关系的进程,除父子外,还有兄弟进程等具备其它联系的进程。实现这些进程间通信的实质,是关闭多个进程中多余的文件描述符,只为
待通信进程保留读端写端。


(2)命名管道端,匿名管道没有名字,只能用于有亲缘关系的进程间通信,为了打破这一局限,Linux 中设计了命名管道命名管道又名 FIFO*(first in first out),它与匿名管道
的不同之处在于: 命名管道与系统中的一个路径名关联,以文件的形式存在于文件系统中, 如此,系统中的不同进程可以通过 FIFO 的路径名访问 FIFO 文件,实现彼此间的通信。通信结束后,命名管道不销毁。
linux可以通过kfifo -m 权限 名称称 来创建命名管道。也可以通过下面的函数来创建

int mkfifo(const char* pathname,mode_t mode);

参数pathname,表示管道文件的路径,参数mode用于指定FIFO的权限。成功调用返回,否则返回-1


3.命名管道和匿名管道的特点
(1)只能进行单向通信
(2)命名和匿名管道的不同,匿名只能进行由血缘关系进程间的通信,而命名管道可以进行任何不相干进程间的通信,当然也可以进行血缘间的通信。
(3)面向流式服务。
(4)生命周期随进程。
(5)自带同步与互斥机制。


4匿名管道进行父子间通信的代码

#include<stdlib.h>#include<string.h>#include<sys/types.h>#include<sys/wait.h>#include<unistd.h>int main(){    int fd[2];    int ret=pipe(fd);                //创建管道    if(ret<0)                        //判断是否成功    {        perror("pipe");        exit(1);    }    pid_t pid=fork();               //创建子进程    if(pid>0)    {        //父进程写信息        close(fd[0]);               //打开写端fd[1]              char* msg="hello world";        write(fd[1],msg,strlen(msg)+1);        close(fd[1]);        sleep(2);    }    else if(pid==0)    {        //子进程读信息        close(fd[1]);   //关闭写端        char buf[1024]={0};        ret=read(fd[0],buf,sizeof(buf));        close(fd[0]);        sleep(3);        write(1,buf,sizeof(buf));    }    return 0;}buf,sizeof(buf));    }    return 0;}

父进程给管道写入“hello world",子进程接收到,并打印到屏幕,所以通信成功
命名管道通信
fifo_write.c 表示向管道写数据

#include<stdio.h>#include<stdlib.h>#include<sys/types.h>#include<string.h>#include<unistd.h>#include<sys/stat.h>#include<fcntl.h>int main(int argc,char* argv[]){    int ret=access("./myfifo",F_OK);//判断fifo是否存在    if(ret<0)                       //若fifo不存在就创建fifo    {        int r=mkfifo("./myfifo",0664);         if(r<0)                     //判断文件是否创建成功        {            perror("mkfifo");            exit(2);        }        else        {            printf("fifo creat succsee\n");        }    }    int fd=open("./myfifo",O_WRONLY);  //以读的方式打开文件    while(1)                           //循环写入数据    {        char *p="hello bit!";        write(fd,p,strlen(p)+1);        sleep(2);    }    close(fd);    return 0;}

fifo_read.c 表示从管道拿数据

#include<stdio.h>#include<stdlib.h>#include<unistd.h>#include<sys/types.h>#include<sys/stat.h>#include<string.h>#include<fcntl.h>int main(){    int fd=open("./myfifo",O_RDONLY);  //以只读的方式打开    if(fd<0)                           //是否打开文件成功{    perror("open");    exit(1);}    while(1)                           //循环读入数据{     char buf[1024]={0};    read(fd,buf,sizeof(buf));    printf("buf=%s\n",buf);    sleep(2);}    close(fd);    return 0;}

运行结果
这里写图片描述
接收端结果
这里写图片描述

原创粉丝点击