进程间通信-匿名管道(学习笔记)

匿名管道

管道是一种进程间通信的形式
头文件:#include< unistd.h >
功能:创建一匿名管道
原型 int pipe(int fd[2])
参数:fd是文件描述符数组,其中fd[0]表示读端,fd[1]表示写端
返回值:成功返回0,失败返回-1,并且设置错误码

这里写图片描述
上面这张图形象的诠释了什么是管道,其实管道就是内核里的一段内存,给了读端口 和写端口,首先通过一个简单的例子,我们来感受一下管道,并看下怎么用

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


//实现管道
//从屏幕读入数据,写入管道,读取管道,写入屏幕
int Test1(){
  int fd[2] = {0};
  int ret = pipe(fd);
  if (ret < 0){
    perror("pipe");
    return 1;
  }
  printf("pipe ok\n");
  while (1) {
    char buf[1024];
    ssize_t read_size = read(0, buf, sizeof(buf) - 1);
    if (read_size < 0){
      perror("read");
      return 1;
    }
    if (read_size == 0) {
      // EOF
      printf("read done\n");
      return 0;
    }
    buf[read_size] = '\0';
    write(fd[1], buf, strlen(buf));


    char out_buf[1024] = {0};
    read_size = read(fd[0], out_buf, sizeof(out_buf) - 1);
    if (read_size < 0) {
      perror("read");
      return 1;
    }
    if (read_size == 0) {
      printf("read done\n");
      return 0;
    }
    out_buf[read_size] = '\0';
    write(1, out_buf, strlen(out_buf));
  }
  return 0;
}

运行结果:
这里写图片描述
可以看出fd[0]就是读端口,fd[1]是写端口
By:
A :2018.5.24 buf[read_size]=0;是为了去掉read从标准输入读出来的\n
B:写进程在管道的尾端写入数据,读进程在管道的首端读出数据。
数据读出后将从管道中移走,其它读进程都不能再读到这些数据。

匿名管道的父子进程通信

原理:父进程创建一个管道 ,之后fork出一个子进程,此时父子进程共享此管道
这里写图片描述
父子进程实现:

//父子进程通信
//相当于一个读端 一个写端
void Test2() {
  int fd[2] = {0};
  int ret = pipe(fd);
  //成功返回0
  //失败返回错误码
  if(ret != 0) {
    perror("pipe");
    return;
  }
  ret = fork();
  if (ret > 0) {
    // father用写端
    close(fd[0]);
    while (1) {
      char buf[1024] = {0};
      //从屏幕读
      ssize_t read_size = read(0, buf, sizeof(buf) - 1);
      if (read_size < 0) {
        perror("father read");
        exit(1);
      }
      if (read_size == 0) {
        printf("father read done\n");
        exit(0);
      }
      buf[read_size] = '\0';

      write(fd[1], buf, strlen(buf));
    }
  } else if (ret == 0) {
    // child
    close(fd[1]);
    while (1) {
      char buf[1024] = {0};
      ssize_t read_size = read(fd[0], buf, sizeof(buf) - 1);
      if (read_size < 0) {
        perror("child read");
        exit(1);
      }
      if (read_size == 0) {
        printf("child read done\n");
        exit(0);
      }
      buf[read_size] = '\0';
      printf("child say: %s", buf);
    }
  } else {
    perror("fork");
    return;
  }
}

要注意的是上面这个例子父子进程的通信是单向的,若实现全双工可以再创建一个匿名管道
By:父子进程具体谁先调用这个问题或者是否会遭遇阻塞,其实管道是有自己的同步与互斥机制得以保证

管道的读写规则

//光读不写 
void Test3() {
  int fd[2] = {0};
  int ret = pipe(fd);//阻塞
  //int ret = pipe2(fd, O_NONBLOCK);//非阻塞
  if (ret < 0) {
    perror("pipe");
    return;
  }
  printf("pipe ok\n");
  char buf[1024] = {0};
  ssize_t read_size = read(fd[0], buf, sizeof(buf) - 1);
  if (read_size < 0) {
    perror("read");
    return;
  }
  printf("read ok\n");
}
//光写不读  
void Test4() {
  int fd[2] = {0};
  //int ret = pipe(fd);//阻塞
  int ret = pipe2(fd, O_NONBLOCK);//非阻塞
  if (ret < 0) {
    perror("pipe");
    return;
  }
  printf("pipe ok\n");
  //close(fd[0]);
  ssize_t total_size = 0;
  while (1) {
    char buf[1024] = {0};
    ssize_t write_size = write(fd[1], buf, sizeof(buf));
    if (write_size < 0) {
      perror("write");
      return;
    }
    total_size += write_size;
    printf("total_size = %ld\n", total_size);
  }
}
//关闭写端
void Test5() {
  int fd[2] = {0};
  //int ret = pipe(fd);
  int ret = pipe2(fd,O_NONBLOCK);
  if (ret < 0) {
    perror("pipe");
    return;
  }
  printf("pipe ok!\n");
  close(fd[1]);
  char buf[1024] = {0};
  ssize_t read_size = read(fd[0], buf, sizeof(buf) - 1);
  if (read_size < 0) {
    perror("read");
    return;
  }
  if (read_size == 0) {
    printf("read done\n");
    return;
  }
  printf("read ok\n");
}
//关闭读端
void Test6() {
  int fd[2] = {0};
  int ret = pipe2(fd,O_NONBLOCK);
 // int ret = pipe(fd);
  if (ret < 0) {
    perror("pipe");
    return;
  }
  close(fd[0]);
  char buf[1024] = {0};
  printf("before write\n");
  ssize_t write_size = write(fd[1], buf, sizeof(buf));
  printf("after write\n");
  if (write_size < 0) {
    perror("write");
    return;
  }
  printf("write ok: %ld\n", write_size);
}

总结:
一、阻塞式 pipe
1.关闭对应端口close()
只读不写 read返回等于0 打印出read done
只写不读 当想要写的时候,程序直接停止运行
2.不关闭对应端口
只读不写 发生阻塞
只写不读 阻塞在写 (写了 65536不再写)

二、非阻塞pipe2(,O_NONBLOCK)
1.关闭对应端口close()
只读不写 则read返回等于0 打印出read done
只写不读 当想要写的时候 程序直接停止运行
2.不关闭对应端口
只读不写 read返回小于0 perror打印出错误read: Resource temporarily unavailable
只写不读 写够65536后 当再次写,write返回小于0,perror打印出错误 Resource temporarily unavailable

通过上面的例子我们可以看出
在关闭端口的情况下,阻塞和非阻塞发生状况是一样的,

在不关闭端口的情况下,阻塞和非阻塞的情况就是阻塞和非阻塞特性的最好体现

匿名管道特点:

1.只能用于具有共同祖先的进程(具有亲缘关系的进程)之间进行的通信;通常,一个管道由一个进程创建,然后该进程调用fork,此后父子进程之间就可应用该管道。
2.管道面向字节流
3.管道生命周期随进程
4.内核会对管道操作进行同步与互斥
5.管道是半双工的,数据只能向单个方向流动,需要双方通信时,需要建立两个管道

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值