pipe管道实现进程间的通信

pipe

1.管道由pipe函数创建
2.管道的本质是伪文件(不占用磁盘空间,只占用内存)
3.管道由两个文件描述符的引用,一个fd[0]读,一个fd[1]写
4.数据从管道的写端流入,读端流出
5.管道的原理是内核缓冲区(4k)借助环形队列机制实现

pipe的局限性

1.进程不能自己写数据自己读数据,需要有两端
2.管道中的数据不可以反复读取,一旦读走,管道不复存在
3.管道四半双工通信,数据只能单向移动
4.只有在拥有共同祖先的时候才能使用管道

pipe函数的定义

pipe函数创建并打开管道

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

fd[0]:读端
fd[1]:写端
返回值:
成功:0
失败:-1, 并设置errno

父子进程利用管道实现通信

1.利用pipe函数创建一个管道;
2.父进程利用fork函数创建子进程,子进程与父进程共享文件描述符,也就是说子进程也有pipe的读端和写端;
3.父进程去关闭读端,子进程关闭写端,如下图,这样就可以实现父子进程之前的通信。
在这里插入图片描述

管道的读写行为

读行为:
1.管道中有数据:
read返回实际读到的字节数
2.管道中无数据:
a.无写端,read返回0(类似于读到文件尾部)
b.有写端,read阻塞等待

写行为:
1.管道被关闭(无读端),进程异常终止(SIGPIPE信号)
2.有读端:
a.管道未满,write将数据写入,并返回实际写入的字节数
b.管道已满,write阻塞

案例一:
利用pipe实现简单的父子进程之间的通信

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


int main()
{
  int fd[2];
  pid_t pid;
  char buf[1024];
  int ret=pipe(fd);
  if(ret==-1)
  {
    perror("pipe error");
    exit(1);
  }
  //创建子进程,实现子进程向父进程发送数据
  pid=fork();
  if(pid==-1)
  {
    perror("fork error");
    exit(1);
  }else if(pid==0){
    //进入子进程
    //1.子进程关闭读端
    close(fd[0]);
    const char *senddata="hello pipe\n";
    int ret=write(fd[1],senddata,strlen(senddata));
    if(ret==-1)
    {
      perror("write error");
      exit(1);
    }
  }else if(pid>0){
    //进入父进程
    //2.父进程关闭写端
    close(fd[1]);
    int ret=read(fd[0],buf,sizeof(buf));
    if(ret==-1)
    {
      perror("read error");
      exit(1);
    }

    int ret2=write(STDOUT_FILENO,buf,ret);
    if(ret2==-1)
    {
      perror("write error");
      exit(1);
    }

  }
  return 0;
}

案例2:
利用父子进程实现 ls | wc -l 命令
子进程实现 ls 命令,父进程实现 wc -l命令

/*
 * 利用pipe父子进程通信实现ls | wc-l
 */
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>

int main()
{
  //变量声明
  pid_t pid;
  char buf[1024];
  int fd[2];

  int ret=pipe(fd);
  if(ret==-1)
  {
    perror("pipe error");
    exit(1);
  }
  pid=fork();
  if(pid==0)
  {
    //子进程
    //子进程关闭读端,去写入到管道中
    //fd[0] 读端 ,fd[1]写端
    close(fd[0]);
    dup2(fd[1],STDOUT_FILENO); //将原本要输入到屏幕上的数据输入到管道中
    execlp("ls","ls",NULL);
    perror("execlp error");
    exit(1);
  }else if(pid>0){
     //父进程
     //父进程读取管道的内容,然后将fd[0]绑定到标准输入
     //以下代码为了看起来简洁省去了返回值的检查
     close(fd[1]); //关闭文件的写端,父进程要读取
     dup2(fd[0],STDIN_FILENO);
     execlp("wc","wc","-l",NULL); //wc指令输入的内容来自显示屏

  }
  return 0;
}

案例3:
实现兄弟进程之间的通信
1.利用循环fork产生两个子进程
2.关闭父进程的读写端,子进程1的读端,子进程2的写端

/*
 * 利用管道实现兄弟进程之间的通信
 */

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


int main()
{
  pid_t pid;
  int fd[2],i;
  char buf[1024];

  //创建管道
  int ret= pipe(fd);
  if(ret==-1)
  {
    perror("pipe error");
    exit(1);
  }
  for(i=0;i<2;i++)  //父进程出口
  {
    pid=fork();
    if(pid==-1)
    {
      perror("fork error");
      exit(1);
    }
    if(pid==0)  //子进程出口
      break;

  }
  if(i==2)
  {
    //父进程
    //父进程需要关闭读端和写端并且需要回收两个子进程
    close(fd[0]);
    close(fd[1]);
    wait(NULL);
    wait(NULL);
  }
  else if(i==0){
    //第一个子进程负责写
    //关闭读端
    close(fd[0]);
    const char *str="hello brother\n";
    int ret= write(fd[1],str,strlen(str));
    if(ret==-1)
    {
      perror("write error");
      exit(1);
    }
    close(fd[1]);
  }else if(i==1){
    //第二个进程负责读
    //关闭写端
    close(fd[1]);
    int ret=read(fd[0],buf,sizeof(buf));
    if(ret==-1)
    {
      perror("read error");
      exit(1);
    }
    write(STDOUT_FILENO,buf,ret);
    close(fd[0]);
  }
  return 0;
}
  • 4
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值