管道

2023-11-20

管道(pipe)

管道可用于具有亲缘关系进程间的通信,有名管道克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允许无亲缘关系的进程间通信;

实现机制

管道是由内核管理的一个缓冲区,相当于放入内存的一个纸条。管道的一端连接一个进程的输出,这个进程会向管道中放入信息。管道的另一端连接一个进程的输入,这个进程取出被放入管道的信息。一个缓冲区不需要很大,它被设计成为环形的数据结构,以便管道可以被循环利用。当管道中没有信息的话,从管道中读取的进程会等待,直到另一端的进程放入信息。当管道被放满信息的时候,尝试放入信息的进程会等待,直到另一端的进程取出信息。当两个进程都终结的时候,管道也自动消失。

这里写图片描述

原理上,管道利用fork机制建立,从而让两个进程可以连接到同一个PIPE 上。最开始的时候,上面的两个箭头都连接在同一个进程Process 1上(连接在Process 1上的两个箭头)。当fork复制进程的时候,会将这两个连接也复制到新的进程(Process 2)。随后,每个进程关闭自己不需要的一个连接 (两个黑色的箭头被关闭; Process 1关闭从PIPE来的输入连接,Process 2关闭输出到PIPE的连接),这样,剩下的红色连接就构成了如上图的PIPE。

这里写图片描述

实现细节

在Linux上,管道的实现并没有使用专门的数据结构,而是借助于文件系统的file 结构和VFS 的索引节点inode。通过将两个file 结构指向同一个临时的VFS索引节点,而这个VFS索引节点又指向一个物理页面而实现的,如下图:

这里写图片描述

有两个file数据结构,但它们定义文件操作例程地址是不同的,其中一个是向管道中写入数据的例程地址,另一个是从管道读出数据的例程地址。这样,用户程序的系统调用仍然是通常的文件操作,而内核却利用这种抽象机制实现了管道这一特殊操作。

关于管道的读写

管道的读写源代码在fs/pipe.c中,在pipe.c中有很多函数,其中有两个函数比较重要,即管道读函数pipe_read()和管道写函数pipe_write()。管道写函数通过将字节复制到VFS索引节点指向的物理内存而写入数据,而管道读函数通过复制物理内存中的字节而读出数据。当然,内核必须利用一定的机制同步对管道的访问,为此,内核使用了锁、等待队列和信号。

当写进程向管道写入时,其利用标准的库函数write(),系统根据库函数传递的文件描述符,找到该文件的file结构,file结构中指定了用来进行写操作的函数(即写入函数)地址,于是,内核调用该函数完成写入操作。写入函数在向内存中写入数据之前,必须首先检查VFS索引节点中的信息,同时满足以下条件时,才能进行实际的内存复制工作:

  • 内存中有足够的空间可容纳所有要写入的数据;
  • 内存没有被读程序锁定;

如果同时满足上述条件,写入函数首先锁定内存,然后从写进程地址空间中复制数据到内存。否则,写入进程就休眠在VFS索引节点的等待队列中,接下来,内核调用调度程序,而调度程序选择其他进程运行。写入进程实际处于可中断的等待状态,当内存有足够的空间可以容纳写入数据,或内存被解锁时,读取进程会唤醒写入进程,这时,写入进程将接收信号。当数据写入内存之后,内存被解锁,而所有休眠在索引节点的读取进程会被唤醒。

管道的读取过程和写入过程类似。但是,进程在没有数据或内存被锁定时立即返回错误信息,而不是阻塞进程,这依赖于文件或管道的打开模式。反之,进程可以休眠在索引节点的等待队列中等待写入进程写入数据。当所有的进程完成了管道操作之后,管道的索引节点被丢弃,而共享数据页也被释放。

Linux函数原型:

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

filedes[0]用于读出数据,读取时必须关闭写入端,即close(filedes[1]);
filedes[1]用于写入数据,写入时必须关闭读取端,即close(filedes[0])。

代码实例:

int main(void)
{
    int n;
    int fd[2];
    pid_t pid;
    char line[MAXLINE];

    if(pipe(fd) < 0){                 /* 先建立管道得到一对文件描述符 */
        exit(0);
    }

    if((pid = fork()) < 0)            /* 父进程把文件描述符复制给子进程 */
        exit(1);
    else if(pid > 0){                /* 父进程写 */
        close(fd[0]);                /* 关闭读描述符 */
        write(fd[1], "\nhello world\n", 14);
    }
    else{                            /* 子进程读 */
        close(fd[1]);                /* 关闭写端 */
        n = read(fd[0], line, MAXLINE);
        write(STDOUT_FILENO, line, n);
    }

    exit(0);
}

命名管道(name PIPE)

由于基于fork机制,所以管道只能用于父进程和子进程之间,或者拥有相同祖先的两个子进程之间 (有亲缘关系的进程之间)。为了解决这一问题,Linux提供了FIFO方式连接进程。FIFO又叫做命名管道(named PIPE)。

FIFO (First in, First out)为一种特殊的文件类型,它在文件系统中有对应的路径。当一个进程以读(r)的方式打开该文件,而另一个进程以写(w)的方式打开该文件,那么内核就会在这两个进程之间建立管道,所以FIFO实际上也由内核管理,不与硬盘打交道。之所以叫FIFO,是因为管道本质上是一个先进先出的队列数据结构,最早放入的数据被最先读出来,从而保证信息交流的顺序。FIFO只是借用了文件系统(file system,命名管道是一种特殊类型的文件,因为Linux中所有事物都是文件,它在文件系统中以文件名的形式存在。)来为管道命名。写模式的进程向FIFO文件中写入,而读模式的进程从FIFO文件中读出。当删除FIFO文件时,管道连接也随之消失。

FIFO的好处在于我们可以通过文件的路径来识别管道,从而让没有亲缘关系的进程之间建立连接。

函数原型:

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

int mkfifo(const char *filename, mode_t mode);
int mknode(const char *filename, mode_t mode | S_IFIFO, (dev_t) 0 );

其中pathname是被创建的文件名称,mode表示将在该文件上设置的权限位和将被创建的文件类型(在此情况下为S_IFIFO),dev是当创建设备特殊文件时使用的一个值。因此,对于先进先出文件它的值为0。

代码实例:

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

int main()  
{  
    int res = mkfifo("/tmp/my_fifo", 0777);  
    if (res == 0)  
    {  
        printf("FIFO created/n");  
    }  
     exit(EXIT_SUCCESS);  
}

编译这个程序:

gcc –o fifo1.c fifo

运行这个程序:

$ ./fifo1

用ls命令查看所创建的管道

$ ls -lF /tmp/my_fifo

prwxr-xr-x 1 root root 0 05-08 20:10 /tmp/my_fifo|

注意:ls命令的输出结果中的第一个字符为p,表示这是一个管道。最后的|符号是由ls命令的-F选项添加的,它也表示是这是一个管道。

FIFO读写规则

  • 从FIFO中读取数据: 约定:如果一个进程为了从FIFO中读取数据而阻塞打开了FIFO,那么称该进程内的读操作为设置了阻塞标志的读操作

  • 从FIFO中写入数据: 约定:如果一个进程为了向FIFO中写入数据而阻塞打开FIFO,那么称该进程内的写操作为设置了阻塞标志的写操作。

FIFO详解

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

管道 的相关文章

  • Linux、ARM:为什么仅当启动时存在 I2C GPIO 扩展器时才创建 gpiochip

    在 imx6sx 硬件平台 NXP 嵌入式 ARM 上使用 Linux 3 14 52 问题是设备树中指定的 PCF8575 I2C GPIO 扩展器不会实例化为 sys class gpio 结构中的设备 除非它们在内核启动期间存在 这些
  • Linux命令列出所有可用命令和别名

    是否有一个 Linux 命令可以列出该终端会话的所有可用命令和别名 就好像您输入 a 并按下 Tab 键一样 但针对的是字母表中的每个字母 或者运行 别名 但也返回命令 为什么 我想运行以下命令并查看命令是否可用 ListAllComman
  • perf stat中的cycles注释是什么意思

    8 014196 task clock 0 004 CPUs utilized 204 context switches 0 025 M sec 32 cpu migrations 0 004 M sec 0 page faults 0 0
  • 如何从类似于 eclipse 的命令行创建可运行的 jar 文件

    我知道 eclipse 会生成一个可运行的 jar 文件 其中提取并包含在该 jar 文件中的所有库 jar 文件 从命令提示符手动创建 jar 文件时如何执行类似的操作 我需要将所有 lib jar 解压到类文件夹中吗 目前我正在使用 j
  • Scrapy FakeUserAgentError:获取浏览器时发生错误

    我使用 Scrapy FakeUserAgent 并在我的 Linux 服务器上不断收到此错误 Traceback most recent call last File usr local lib64 python2 7 site pack
  • 如何成功使用RDAP协议代替whois

    我对新的 RDAP 协议有点困惑 也不知道何时进一步追求它有意义 在我看来 每个人都同意它是 whois 的继承者 但他们的数据库似乎是空的 在 ubuntu 上我尝试了 rdapper nicinfo 甚至他们的 RESTful API
  • 操作系统什么时候清除进程的内存

    进程在某些操作系统上成功或异常终止 操作系统何时决定擦除分配给该进程的内存 数据 代码等 在退出时或当它想为新进程分配内存时 这个清除内存分配过程在所有操作系统 winXP Win7 linux Mac 上都相同吗 据我了解 页表具有该进程
  • 怎样才能使 Windows 成为一个开箱即用的 POSIX 兼容操作系统?

    这个问题的动机是我的一个牵强的梦想 即 nix 平台上可用的许多优秀软件可以轻松移植到 Windows 微软最近对开源和开放性采取了不同的方法 所以我真的很想知道如果微软有这样的倾向 这样的事情会有多可行 我很好奇的一些更具体的事情是 是否
  • gethostbyname() 或 getnameinfo() 如何在后台工作?

    How gethostbyname or getnameinfo 在后台工作 include
  • 如何让 clangd 转向 c++20

    当没有其他信息时 如何让 clangd 回退到 c 20 例如 在第一次构建之前 cmake 可以生成一个 这是在带有最新 LLVM 的 Arch Linux 上 这是通过 Emacs LSP 运行的 但这应该没有什么区别 你可以加 Com
  • 构建 makefile 依赖/继承树

    如果我解释得不好或者问了一些明显的问题 我很抱歉 但我是 Linux 内核的新手 而且有点深入 我们有一个嵌入式 Linux 系统 它附带一个 文档非常糟糕的 SDK 其中包含数百个文件夹stuff 大多数文件夹包含rules make m
  • git在Windows和Linux之间切换后强制刷新索引

    我有一个Windows和Linux共享的磁盘分区 格式 NTFS 它包含一个 git 存储库 约 6 7 GB 如果我只使用Windows or 只使用Linux操作 git 存储库一切正常 但是每次切换系统的时候git status命令将
  • 停止服务时单元陷入故障状态(状态=143)[关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 这是我的问题 我有 CentOS 和 java 进程在上面运行 Java进程是通过启动 停止脚本来操作的 它也创建了 java 实例的 p
  • 为 Qt 应用程序创建 Linux 安装

    我刚刚用 Qt Creator 制作了一个很棒的程序 我对自己很满意 如何将其从台式机移至笔记本电脑 那么 最好的方法是安装程序 对吗 对于 Ubuntu 这是一个 Debian 软件包 对吗 我怎么做 有人这样做过吗 他们可以分享 QT
  • 如何在Python中独立于语言安装(linux)获取用户桌面路径

    我找到了 如何找到用户桌面的路径 的几个问题和答案 但在我看来它们都已失效 至少我找到的那些 原因是 如果用户安装的 Linux 不是英语 他或她的桌面很可能位于除 Desktop 例如 对于瑞典语 我相信它是在 Skrivbord 谁知道
  • 在Linux中断上下文中运行用户线程

    我正在编写一些定制的应用程序 并允许更改 Linux 内核中的中断处理程序代码 我有一个用户线程正在等待中断发生 如果发生中断 那么我要做的第一件事就是执行该用户线程 有什么办法让它发挥作用吗 Thanks 创建一个字符设备 这就是内核所做
  • Linux shell 脚本:十六进制数字到二进制字符串

    我正在 shell 脚本中寻找一些简单的方法来将十六进制数字转换为 0 和 1 字符的序列 Example 5F gt 01011111 是否有任何命令或简单的方法来完成它 或者我应该为其编写一些开关 echo ibase 16 obase
  • 劫持系统调用

    我正在编写一个内核模块 我需要劫持 包装一些系统调用 我正在暴力破解 sys call table 地址 并使用 cr0 来禁用 启用页面保护 到目前为止一切顺利 一旦完成 我将公开整个代码 因此如果有人愿意 我可以更新这个问题 无论如何
  • PHP 致命错误:未找到“MongoClient”类

    我有一个使用 Apache 的网站 代码如下 当我尝试访问它时 我在 error log 中收到错误 PHP Fatal Error Class MongoClient not found 以下是可能错误的设置 但我认为没有错误 php i
  • 如何模拟ARM处理器运行环境并加载Linux内核模块?

    我尝试加载我的vmlinux into gdb并使用 ARM 内核模拟器 但我不明白为什么我会得到Undefined target command sim 这是外壳输出 arm eabi gdb vmlinux GNU gdb GDB 7

随机推荐

  • 【Antlr】ANTLR语法规则

    1 概述 ANTLR语法规则的主要工作是定义词法解析规则和语法解析规则 ANTLR约定词法解析规则以大写字母开头 语法解析规则以小写字母开头 下面简单介绍一下ANTLR的规则 首先需要定义Grammar类型及名称 名称必须和文件名一样 有L
  • 每日一题 337. 打家劫舍 III

    难度 中等 整体思路相当于是前两天的方法倒过来 毕竟二叉树最常用的解法就是递归倒推 对于每一颗子树 他必定有一种最大的盗取方法 但是只有它的 root 的盗取情况才会影响到 root 的父节点 即如果收益最大的盗取方法中不盗取 root 那
  • anaconda 2023.3 win10 安装,镜像配置,存储路径更改超详细教程

    现在 ai 很火 做 ai 开发 离不开 python anaconda 是 python 开发的重要工具 这里介绍一下 anaconda 最新版的 2023 3 在 win10 的安装 镜像配置 以及虚拟环境存储路径的更改 1 anaco
  • (48.4)【WAF绕过】SQL注入、文件上传、XSS

    目录 一 SQL注入绕过 二 文件上传绕过 三 xss绕过 一 SQL注入绕过 WAF绕过基础分析和原理 注入绕过WAF方法分析https blog csdn net qq 53079406 article details 12314769
  • 深度学习2015年文章整理(CVPR2015)

    国内外从事计算机视觉和图像处理相关领域的著名学者都以在三大顶级会议 ICCV CVPR和ECCV 上发表论文为荣 其影响力远胜于一般SCI期刊论文 这三大顶级学术会议论文也引领着未来的研究趋势 CVPR是主要的计算机视觉会议 可以把它看作是
  • 【JavaWeb】 练习二

    1 gt 实现登录 如果用户名密码错误 转入到登录页面 提示信息 用户名密码错误 2 gt 如果用户不登录 直接访问展示页面 也需要转入到登录页面 提示信息 请先登录在访问 3 gt 实现退出功能 转到登录页面 4 gt 统计查看展示页面的
  • 必杀VI、VIM编辑器命令

    题记 VI和VIM有非常多的命令 在此分部分展示一下 第一部分 一 移动光标类 一般模式 光标移动 复制粘贴 查找替换 上下移动 h lt 光标向左移动一个字符 j 向上箭头 光标向上移动一个字符 k 向下箭头 光标向下移动一个字符 l 向
  • Go_接口、多态、接口继承、空接口、类型断言

    接口 接口是把所有具有共性的方法定义在一起 是方法集 任何类型实现了接口中所有的方法 就是实现了这个接口 接口可以实现多态 接口传递的是地址值 接口定义及调用 定义格式 tepe 接口名 interface 方法名 参数 返回值 调用格式1
  • (超全面,超基础)Kriging插值推导理论笔记,算法,普通克里金

    最近老师让我再重新推导一下克里金插值的方法如何求出预测结果 于是我又无数次地打开了大神的推导原理过程链接 克里金 Kriging 插值的原理与公式推导 作者 xg1990 但是不可避免地在推导过程中 数学知识基础薄弱的我卡住在一些公式推导的
  • 【有奖调研】HarmonyOS新物种,鸿蒙流量新阵地——元服务邀你来答题!

    聊技术无话不谈 一起来吹吹元服务 畅聊你对元服务的想法 说不定 你就能撬动元服务的爆发增长 元服务 即原子化服务 是华为 轻量化 服务的新物种 可提供全新的服务和交互方式 让应用化繁为简 让服务触手可及 基于鸿蒙万能卡片 元服务可实现应用功
  • 数据同步算法研究

    数据同步算法研究 1 引言 基于LAN或WAN的网络应用之间进行数据传输或者同步非常普遍 比如远程数据镜像 备份 复制 同步 数据下载 上传 共享等等 最为简单的做法自然就是对数据进行完全复制 然而 数据在网络上来回被复制多次后就会存在大量
  • mysql connect 5.6_mysql5,mysql5.6,mysql5.7,mysql8设置autoReconnect=true解决连接数据库超时问m...

    1 问题现象 com mysql jdbc CommunicationsException The last packet successfully received from the server was58129 seconds ago
  • Windows下面Clang的调试方法

    虽然我们使用了Clang工具进行编译 但是我们依旧可以使用Visual Studio进行调试 方法如下 首先 我们需要将编译分为两个步骤 先在CMD使用clang进行obj的生成 也就是编译 clang cl c Z7 o helloeng
  • 8.8数据结构作业

    进制转换 int stack zhuanhuan Stackptr S datatype e if NULL S printf 转换失败 n return 0 datatype x 0 while e gt 0 x e 2 stack pu
  • 【星海出品】Django入门

    Django简述 blog blog init py 空文件 settings py 管理项目的配置信息 urls py 路由 声明请求url的映射关系 wsgi py python程序和web服务器的通信协议 manage py 一个命令
  • Github官网进不去怎么办?Github无法访问怎么办?

    GitHub是一个面向开源及私有软件项目的托管平台 超5千万程序员正在使用 也被戏称为全球最大同性交友网站 是程序员必备网站之一 GitHub能干什么 1 托管代码 历史版本管理 当然不仅仅是代码 任何文件都支持 不少人用GitHub来写博
  • Java while循环学习小结

    while循环先判断循环条件是否满足 再执行循环语句 while循环可能一次都不执行 编写循环时要注意循环条件 并避免死循环 具体内容扫描关注公众号
  • 企业微信内部跳转小程序

    企业微信中跳转小程序 企业微信官方文档 在企业微信内快速跳转到指定的小程序页面之前需要先调用wx agentConfig wx agentConfig corpid 必填 企业微信的corpid 必须与当前登录的企业一致 agentid 必
  • Unity打包WebGL的优化常用操作?

    1 贴图部分优化 如果贴图格式时2048 在不影响画面效果的情况下 改成1024或者5 12 还可以缩小包体 2 压缩和解压缩问题 WebGL打包的时候分三种压缩情况 gzip 比Brotli文件打 但打包快 http和https都支持 B
  • 管道

    管道 pipe 管道可用于具有亲缘关系进程间的通信 有名管道克服了管道没有名字的限制 因此 除具有管道所具有的功能外 它还允许无亲缘关系的进程间通信 实现机制 管道是由内核管理的一个缓冲区 相当于放入内存的一个纸条 管道的一端连接一个进程的