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. 他们的区别只是在创建和打开方式不同, 但一旦管道开始使用,他们的意义都是一样的。

### 回答1: Linux是一种操作系统,而-bash是Linux中的一种shell(命令行解释器),它允许用户在命令行中输入命令并执行它们。-bash是Linux默认的shell之一,它提供了许多有用的功能和命令,使用户能够更轻松地管理和操作系统。 ### 回答2: Linux -bash 是指 Linux 系统中的 Bash(Bourne Again SHell)终端或命令行界面。 Bash 是一种常用的 Unix shell,它是用于管理和执行命令行操作的基础工具。它允许用户在 Linux 系统上输入和执行各种命令,与系统进行交互,并管理文件和文件夹。 Linux -bash 提供了一个交互式环境,用户可以在其中执行各种命令。它可以用于执行系统管理任务,如安装软件包、配置网络设置、管理用户和权限等。此外,还可以使用 bash 脚本编写批量处理任务,实现自动化和脚本化操作。 在 -bash 终端中,用户可以使用各种命令来探索文件系统、浏览文件和文件夹、编辑文本文件等。用户可以通过输入命令和参数,实现不同的操作和功能,比如复制、移动、删除文件,修改文件权限等等。 -bash 终端也提供了强大的命令行编辑和历史纪录功能。用户可以使用上下箭头键浏览之前输入的命令,并进行修改和再次执行。此外,可以使用 Tab 键进行自动完成操作,节省输入命令的时间和工作量。 总而言之,Linux -bash 是 Linux 系统中常用的命令行界面,在系统管理、文件操作和批处理方面提供了丰富的功能和灵活性,是 Linux 用户和系统管理员必不可少的工具之一。 ### 回答3: Linux-bash是一种命令行解释器,也称为Bourne Again SHell。Bash是Unix操作系统中最常用的shell之一,由于Linux是基于UNIX的,因此Bash也成为了Linux中最常用的shell。 -bash表示当前用户所使用的shell环境是Bash。在Linux系统中,当我们打开终端或者SSH登录到服务器时,会自动进入一个shell环境,这个环境中我们可以使用各种命令来执行各种操作。 Bash是一个功能强大且灵活的shell,它支持大量的命令,可以用于管理和操作文件、目录、进程、权限、网络等各个方面。通过Bash,我们可以输入命令并执行,也可以编写shell脚本来进行自动化操作。 在-bash下,我们可以使用各种命令来管理Linux系统,比如ls命令用于列出当前目录下的文件和子目录,cd命令用于切换目录,pwd命令用于显示当前所在的目录,mkdir命令用于创建新的目录,rm命令用于删除文件和目录,等等。 此外,Bash还支持各种运算符和控制结构,允许我们编写复杂的shell脚本来自动化任务。我们可以使用if语句来进行条件判断,使用for和while循环来进行重复操作,还可以通过管道(|)来连接多个命令,实现更复杂的操作。 总之,Linux-bash是一种强大的命令行解释器,它为我们提供了丰富的命令和功能,使我们能够高效地管理和操作Linux系统。无论是日常使用还是系统管理,熟练掌握-bash都是非常重要的。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值