linux--管道

什么是管道

管道本质上是内核中的一段缓冲区,它可以连接两个进程,进行数据流的传输。

匿名管道

匿名管道字如其名是匿名的。他不可见于文件系统,所以只能用于具有亲缘关系的进程间通信。

#include <unistd.h>
功能:创建⼀⽆名管道
原型
int pipe(int fd[2]);
参数
fd:⽂件描述符数组,其中fd[0]表⽰读端, fd[1]表⽰写端
返回值:成功返回0,失败返回错误代码

父进程创建管道。再用fork()函数创建子进程,所以他们具有相同的文件描述符表。根据管道的数据传输方向,关闭父子进程不用的那一端文件描述符即可。
在这里插入图片描述

代码实现

模拟实现 grep ssh | ps -ef

  1 #include <stdio.h>
  2 #include <unistd.h>
  3 #include <stdlib.h>
  4 #include <errno.h>
  5 #include <string.h>
  6 
  7 int main()
  8 {
  9     int pipefd[2];
 10 
 11     if (pipe(pipefd) < 0) {
 12         perror("pipe error");
 13         return -1;
 14     }
 
 15     int pid = fork();
 16     if (pid < 0) {
 17         exit(-1);
 18     }else if (pid == 0) {//child
 19         //grep ssh
 20         //grep这个进程本身从标准输入获取数据,但是我们想的是,不要
 21         //从标准输入拿数据了,而是从管道中取数据
 22         //因此将标准输入进行重定向到管道读取端
 23         close(pipefd[1]);
 24         dup2(pipefd[0], 0);
 25         execlp("grep", "grep", "ssh", NULL);
 26     }else {
 27         close(pipefd[0]);
 28         //ps这个命令最终将结果是打印到显示器上,然而,我们并不想让
 29         //它打印到界面,而是将结果写入到管道
 30         //因此将标准输出重定向到管道写入端
 31         dup2(pipefd[1], 1);
 32         execlp("ps", "ps", "-ef", NULL);
 33         close(pipefd[1]);
 34     }
 35     return -0;
 36 }

管道读写规则

当没有数据可读时
O_NONBLOCK 不启用: read调⽤阻塞,即进程暂停执⾏,⼀直等到有数据来到为⽌。
O_NONBLOCK 启用: read调用不阻塞,调⽤返回-1, errno值为EAGAIN。

当管道满的时候
O_NONBLOCK 不启用: write调⽤阻塞,直到有进程读⾛数据
O_NONBLOCK 启用:write调用不阻塞,调⽤返回-1, errno值为EAGAIN

如果所有管道写端对应的⽂件描述符被关闭,则read返回0
如果所有管道读端对应的⽂件描述符被关闭,则write操作会产⽣信号SIGPIPE,进⽽可能导致write
进程退出
当要写⼊的数据量不⼤于PIPE_BUF时, linux将保证写⼊的原⼦性,不会被打断。
当要写⼊的数据量⼤于PIPE_BUF时, linux将不再保证写⼊的原⼦性,可能被打断。

匿名管道的特点
  1. 匿名管道是半双工的,只能实现单向通信,倘若要达到相互通信的目的,需要创建两个管道。
  2. 匿名管道的生命周期随进程,当连接管道的两个进程都退出后,管道自动释放
  3. 管道是面向字节流传输的
  4. 匿名管道仅能用于亲缘间进程通信。
  5. 管道传输自带同步与互斥:
    同步:数据访问的可控时序型(数据访问按顺序)
    互斥:同一时间数据访问的唯一性

命名管道

相对于匿名管道,命名管道是可见于文件系统的,所以适用于同一主机上任意两个进程间的数据传输。
可以通过命令行方式和接口函数实现:

命令行: mkfifo+ filename
接口函数: int mkfifo(const char* pathname,mode_t mode);
		pathname:管道文件的路径名
        mode:管道文件的权限
        返回值: 成功:0        失败:-1
代码实现:
  1 #include<stdio.h>
  2 #include<unistd.h>
  3 #include<errno.h>
  4 #include<string.h>
  5 #include<fcntl.h>
  6 #include<stdlib.h>
  7 
  8 
  9 int main()
 10 {
 11     //1.创建命名管道
 12     if (mkfifo("./named_pipe",0664) < 0)//倘若创建不成功,需要考虑该管道是否已经存在,已经存在则不用退出.
 13     {
 14         if (errno != EEXIST)
 15         {
 16             perror("mkfifo error!");
 17             return -1;
 18         }
 19     }
 20 
 21     //2.连接命名管道
 22     //打开规则:命名管道在打开时,必须以读写方式打开,单独以只读,只写方式打开都会阻塞
 23     //此处为了演示不同的两个进程间通信,故在此只以读方式打开,另一端以写方式打开
 24     int fd = open("./named_pipe",O_RDONLY);
 25     if (fd < 0)
 26     {
 27         perror("open error!");
 28         return -1;
 29     }
 30 
 31     printf("named_piped is working!\n");
 32     //3.开始进行数据传输
 33     while (1)
 34     {
 35         //本进程是以只读方式打开的,所以只能从管道中读数据,同时注意管道的读写规则:
 36         //
 37         //1.如果管道没有数据,读取操作会阻塞(描述符的默认属性---阻塞属性),
 38         //如果描述符被设置为非阻塞属性,那么这个操作不会被阻塞,而是直接报错返回。
 39         //
 40         //2.如果管道数据已满,写入操作会阻塞(描述符的默认属性---阻塞属性),
 41         //如果描述符被设置为非阻塞属性,那么这个操作也不会被阻塞,而是直接报错返回。
 42         //
 43         //3.如果管道的写端全部关闭,那么read读取数据的时候会返回0
 44         //
 45         //4.如果管道的读端全部关闭,那么write写入数据的时候会触发异常,
 46         //操作系统会发送SIGPIPE信号到进程,导致进程退出。
 47         //
 48         char buff[1024]={0};
 49         int ret = read(fd,buff,1023);
 50 
 51 
 52          if(ret > 0)//读到数据
 53         {
 54             printf("read point get the data:%s\n",buff);
 55         }
 56         else//根据读写规则,当所有写端都关闭时,read返回0
 57         {
 58             printf("all write point is closed!!\n");
 59             sleep(1);
 60         }
 61     }
 62     close(fd);
 63     return 0;
 64 }

当我们运行这个程序时会阻塞在open处,因为没有另一个进程以只写方式打开,我们利用echo将本应该输出到屏幕上的信息重定向到管道named_pipe中,就实现了数据传输。如图:
在这里插入图片描述

命名管道的打开规则(重要)

命名管道的打开特性:

   1.如果以只读打开,会阻塞等待这个命名管道被其他进程以写方式打开
   2.如果以只写打开,会阻塞等待这个命名管道被其他进程以读方式打开
   3.如果以读写打开,则不会阻塞
命名管道的读写特性(重要)

1.如果管道没有数据,读取操作会阻塞(描述符的默认属性—阻塞属性)。
如果描述符被设置为非阻塞属性,那么这个操作不会被阻塞,而是直接报错返回。

2.如果管道数据已满,写入操作会阻塞(描述符的默认属性—阻塞属性),
如果描述符被设置为非阻塞属性,那么这个操作也不会被阻塞,而是直接报错返回。

3.如果管道的写端全部关闭,那么read读取数据的时候会返回0

4.如果管道的读端全部关闭,那么write写入数据的时候会触发异常,操作系统会发送SIGPIPE信号到进程,导致进程退出。

5.当写入的数据大小超过PIPE_BUF,则这个操作是一个非原子操作,有可能被打断。

匿名管道与命名管道的区别(非常重要)

  1. 匿名管道由pipe函数创建并打开,而命名管道要由mkfifo函数创建,并手动将两个进程绑定在管道两端。

  2. 匿名管道是依附于进程的,进程退出后匿名管道会随之释放,而命名管道是一个实实在在的文件,创建后就一直存在,他并不会跟着进程的退出而释放.

  3. 他们的区别只是在创建和打开方式不同, 但一旦管道开始使用,他们的意义都是一样的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值