【Linux】进程间通信——管道

管道

  • 管道是Unix中最古老的进程间通信的形式。
  • 我们把从一个进程连接到另一个进程的一个数据流称为一个“管道”
  • 管道是半双工通信方式(指数据可以在一个信号载体的两个方向上传输,但是不能同时传输)
  • 本质:操作系统在内核中创建的一块缓冲区
  • 操作:通过IO接口实现管道这个缓冲区的操作
  • 分类:匿名/命名管道
    在这里插入图片描述

匿名管道

匿名管道,内核中没有名字的缓冲区

#include <unistd.h>

int pipe(int fd[2]);
  • 功能:创建一无名管道
    一个进程通过系统调用接口,在内核创建的一块没有明确标识的缓冲区,内核返回给创建的一块没有明确标识的缓冲区,内核返回给创建进程两个文件描述符供进程来操作管道;其中一个描述符用于从管道读数据,一个向管道中写入数据
  • 参数
    fd:文件描述符数组,其中fd[0]表示读端, fd[1]表示写端
  • 返回值:成功返回0,失败返回错误代码
管道的读写特性:

1)若管道中没有数据,则read会阻塞,直到数据被写入(缓冲区中有数据)
2)若管道中数据满了,则write会阻塞,直到数据被读取(缓冲区有空闲空间)
3)若管道的所有读端被关闭,则write会触发异常,进程退出
4)若管道所有写端被关闭,则read会返回0
   管道的read返回0,不仅仅指的是没有读到数据,-----所有写端被关闭
在这里插入图片描述

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <sys/wait.h>
int main() {
  //ls | grep make
  int pipefd[2];
  int ret = pipe(pipefd);
  if (ret < 0) {
    perror("pipe error");
    return -1;
  }
  int pid1 = fork();
  if (pid1 == 0) {
    //ls
    close(pipefd[0]);
    dup2(pipefd[1], 1);   //将标准输出重定向到管道写入端
    execlp("ls", "ls", NULL);
    //如果替换失败直接退出
    exit(0);
  }
  int pid2 = fork();
  if (pid2 == 0) {
    //grep make
    close(pipefd[1]);   //关闭所有写端,grep循环读取数据,read返回0,就不在读
    dup2(pipefd[0], 0);   //将标准输入端重定向到管道读取端
    execlp("grep", "grep", "make", NULL);
    exit(0);
  }
  close(pipefd[0]);
  //父进程的写端也要关闭
  close(pipefd[1]);
  waitpid(pid1, NULL, 0);
  waitpid(pid2, NULL, 0);
  return 0;
}
匿名管道特性
  • 只能用于具有共同祖先的进程(具有亲缘关系的进程)之间进行通信;通常,一个管道由一个进程创建,然后该进程调用fork,此后父、子进程之间就可应用该管道。
  • 管道提供流式服务
  • 一般而言,进程退出,管道释放,所以管道的生命周期随进程
  • 一般而言,内核会对管道操作进行同步与互斥
  • 管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道

命名管道

  • 匿名管道应用的一个限制就是只能在具有共同祖先(具有亲缘关系)的进程间通信
  • 如果我们想在不相关的进程之间交换数据,可以使用FIFO文件来做这项工作,它经常被称为命名管道。
  • 命名管道是一种特殊类型的文件
创建命名管道
  1. 命名管道可以从命令行上创建,命令行方法是使用下面这个命令:
	mkfifo [filename]

在这里插入图片描述

#include <sys/types.h>
#include <sys/stat.h>

int mkfifo(const char *pathname, mode_t mode);
  • 参数:
    pathname:管道文件名称
    mode:管道文件权限
  • 返回值:0 失败:-1
  • 打开特性
    若文件没有被已经以读的方式打开,则以O_WRONLY打开时会阻塞
    若文件没有被已经以写的方式打开,则以O_RDONLY打开时会阻塞
  • 读写特性与匿名管道相同

“fifo_write.c”

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/stat.h>
#include <string.h>
int main() {
  char* fifo = "./test.fifo";
  int ret = mkfifo(fifo, 0664);
  if (ret < 0) {
    //如果文件存在,并不让它退出
    if (errno != EEXIST) {
      perror("mkfifo error");
      return -1;
    }
  }
  printf("open fifo-----\n");
  int fd = open(fifo, O_WRONLY);
  if (fd < 0) {
    perror("open error");
    return -1;
  }
  printf("fifo:%s open success!\n", fifo);
  while (1) {
    char buf[1024] = { 0 };
    printf("i say:");
    fflush(stdout);
    scanf("%s", buf);
    write(fd, buf, strlen(buf));
  }
  close(fd);
  return 0;
}

“fifo_read.c”

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/stat.h>
int main() {
  char* fifo = "./test.fifo";
  int ret = mkfifo(fifo, 0664);
  if (ret < 0) {
    if (errno != EEXIST) {
      perror("mkfifo error");
      return -1;
    }
  }
  printf("open fifo-----\n");
  int fd = open(fifo, O_RDONLY);
  if (fd < 0) {
    perror("open error");
    return -1;
  }
  printf("fifo:%s open success!\n", fifo);
  while (1) {
    //字节流服务,睡眠5秒钟,将缓冲区数据一起读入
    sleep(5);
    char buf[1024] = { 0 };
    read(fd, buf, 1023);
    printf("peer say:%s\n", buf);
  }
  close(fd);
  return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值