进程间通信---管道

1.进程间通信介绍

  (1) 进程间通信(IPC):多个进程间进行信息交流

  (2) 进程间通信的方式:管道、消息队列、共享内存、信号量、socket等;

  (3) 进程间通信的目的:

          数据传输:一个进程将他的数据发给另一个进程

          资源共享:多个进程间共享同样的资源

          通知事件:一个进程给另一个进程发消息,通知他发生了什么事件

          进程控制:当一个进程想要完全控制另一个进程时,控制进程能拦截另一个进程的所有陷入和异常,并及时知道他的状态的改变

2.管道的引入

2.1 相关概念

(1)管道是最古老的进程间通信方式

(2)将一个进程连接到另一个进程的数据流称为“管道”

 如who | wc -l的实现过程如下图:

                     

who进程将要传输的数据输出到管道当中,wc -l进程从管道当中拿数据

2.2 管道的特点

(1)单向通信:管道是半双工通信,数据只能从管道的一端写入,从管道的另一端读出。可用两个管道实现全双工

(2)匿名管道只有具有亲缘关系(如父子进程)的进程间才可以通信,而命名管道在不相关的进程间也可以通信

(3)管道依赖文件系统:管道的生命周期随进程,进程结束则管道也随之销毁,因为管道的本体是内存,进程退出则这段内存销毁

(4)管道是基于字节流传输的:管道的读写没有规定大小,所以基于字节流传输

(5)管道自带同步与互斥:在管道的一端写入数据,若没有写完则另一端不能读,进入阻塞状态;同理,在一端读数据时若没有读完,则另一端不能写

2.3 管道的分类

管道分为匿名管道和命名管道

3.匿名管道

3.1管道的创建

    头文件:#include<unistd.h>

    功能:创建一个无名管道

    原型:int pipe(int fd[2]);

    参数:

            文件描述符数组,其中fd[0]表示读端,fd[1]表示写端

    返回值:0表示创建成功,非0表示创建失败,失败返回错误码

                 

3.2 创建父子进程,子进程向管道里写数据,父进程从管道里读数据                          

思考:父进程的fd[0]从管道中读数据,fd[1]向管道中写数据;创建子进程后,子进程也有fd[0]从管道中读数据,fd[1]向管道中写数据 。要实现子进程向管道里写数据,父进程从管道里读数据 ,就必须关闭子进程的读端、关闭父进程的写端。 

           

代码实现:

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

int main()
{
    int fds[2];//文件描述符数组
    if(pipe(fds)==-1)
    {   
        perror("pipe");
        exit(0);
    }   

    pid_t id=fork();
    if(id==0){//子进程
        close(fds[0]);//关闭读
        write(fds[1],"hello",5);//向管道写数据
        close(fds[1]);
        exit(0);
    }

    else if(id>0){//父进程
        close(fds[1]);//关闭写
        char buf[1024]={};
        int r=read(fds[0],buf,1024);//从管道中读数据
        if(r==-1)
        {
            perror("read");
            close(fds[0]);
            exit(0);
        }else if(r==0)
        {
            printf("读结束\n");
            close(fds[0]);
            exit(0);
        }else
        {
            printf("buf:[%s]\n",buf);
            close(fds[0]);
            exit(0);
        }
    }

    else{
        perror("fork");
        exit(0);
    }

    return 0;
}

4.命名管道

4.1命名管道创建方式

(1) 命令创建:mkfifo 文件名

          写端打开,若读端没有打开,则写端阻塞,直到读端打开才阻塞结束。即通过管道通信的读端写端同时打开。

                     

 (2)程序创建:

        int mkfilo(const char* filename(文件名),mode_t mode(权限));

  (3)打开管道文件:

          int fd=open(name,O_TDONLY);//读  

          int fd=open(name,O_TDONLY);//写

   4.2 创建一个匿名管道,从a-z,一次写一个字符,一次读一个字符

写端:

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

int main()
{
    int fds[2];
    mkfifo("namepipe",0644);//创建命名管道
    int fp=open("namepipe",O_WRONLY);//打开命名管道
    if(fp==-1){
        perror("open");
        exit(1);
    }   

    char buf[2]={'a'};//缓存区,第0个字符为a
    int i=0;
    while(1){//循环写a-z
        i++;
        int r=write(fp,buf,1);
        printf("write:r=%d [%c]\n",r,buf[0]);
        buf[0]='a'+i%26;//26个字母,下标25
    }
    close(fp);
    return 0;
}

读端:

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

int main()
{
    int fds[2];
    int fp=open("namepipe",O_RDONLY);
    if(fp==-1){
        perror("open");
        exit(1);
    }   

    char buf[2]={};
    while(1){
        read(fp,buf,1);
        printf("read:r=[%c]\n",buf[0]);
        sleep(1);
    }
    close(fp);
    return 0;
}

结果:写端每次一个字符一个字符的不停的写数据,读端每次一个字符一个字符的读数据。

写端比读端快,当数据在命名管道中放满了之后写端阻塞,等读端将数据读走后管道有空间了再继续写。

5.命名管道与匿名管道的区别与联系

(1)匿名管道必须是具有亲缘关系的进程间才可以实现通信,命名管道对于不相关进程也可以实现通信

(2)匿名管道用pipe函数创建,创建的同时也打开;命名管道用mkpipe函数创建,打开用open。

(3)命名管道与匿名管道创建打开的工作完成后,他们具有相同的语义

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值