Linux——进程间通信——命名管道

news2024/12/26 23:11:31

       

目录

一.命名管道

        1.1定义与区别

        1.2命名管道的原理

1.3命名管道的创建

1.4命名管道的实现

Comm.hpp 头文件:

Sever.cc代码(该进程是读取数据的):

Client.cc(该进程是写数据的):

1.4.2案例测试:

重新执行Sever:

1.4.3案例执行:

这里需要注意:

二.总结:


         在上一篇博客中,我讲述了有关进程间通信的三种方式,有System V、POSIX和管道。重点讲述了管道的来源,匿名管道的代码实现,以及在匿名管道中出现的多种情况案例分析。

(132条消息) Linux——进程通信_橙予清的zzz~的博客-CSDN博客icon-default.png?t=N5K3https://blog.csdn.net/weixin_69283129/article/details/131434925?spm=1001.2014.3001.5501

        匿名管道大多应用于父子进程间的数据通信,接下来我将介绍一下管道的另一种形式——命名管道。

一.命名管道

        1.1定义与区别

        命名管道,物如其名,那么这个管道是有名字的,它的作用是通过调用mkfifo系统调用函数的方式创建一个管道文件,在该管道文件内让两个毫无血缘关系的进程之间进行数据通信;而匿名管道的作用是通过使用pipe函数创建一个公共的没有名字的管道,然后通过使用fork函数创建出有血缘关系的子进程,让父子进程进行数据间的通信。

        命名管道(FIFO)区别于匿名管道的,它可以用于任意两个进程之间的通信,原因在于它提供了一个路径与特定文件相关联,以FIFO的文件形式存在于文件系统之中,这样即使是任意进程,只要访问这个路径,就能通过FIFO文件相互通信。

        FIFO即First In First Out ,意为先进先出,在计算机科学,它通常指一种特殊的数据结构——队列。在命名管道中,数据的读取顺序和写入顺序是一样的,即先写入的数据会被先读取。

        

        1.2命名管道的原理

        命名管道的原理还是像本质说的那样:想让不同进程之间实现通信,就必须让不同的进程看到同一份公共的资源。

        首先,不同的进程打开同一个文件,一定是指向同一个struct file结构体(进程属性:文件描述符表,它也是一个结构体),而不是各自拷贝一份struct file,这个容易理解,否则当第二个进程按照同一个路径打开这个文件时,文件就已经被打开过了。当然也不能让文件为了每个进程都加载到内存中,否则会出现很多重复的文件,效率也会因此而降低。

        而操作系统对于通信也是追求效率的,让磁盘和内存进行IO,速度太慢,所以科学家们在磁盘中定义了一种叫做命名管道文件,它是一种特殊文件,被多个进程打开的同时,还能保证不会将内存的数据刷新到磁盘,所以它不会被保存数据,大小永远为0。说白了它就像是一个临时的文件一样。该文件在系统路径中,因此这个路径具有唯一性,不同的进程通过同一条路径看到同一个管道文件从而共享同一份公共资源。

        

 

命名管道就是让两个不同的独立进程,看到同一份资源!那它是怎么做到的?

答:命名管道可以让两个进程打开指定名称的(路径+文件名称)同一个文件

        这也是OS采用相对路径和绝对路径不冲突的原因,而且使用绝对路径来标定某些文件的位置,这是绝对路径的唯一性决定的。实际上,文件路径展开后就是一颗树,从根结点到任意节点是绝对路径,它不会和其他任意路径相交,因此它具有唯一性。


        结合匿名管道的特性,不同进程通过同一条路径找到的文件是同一个文件,这个文件是管道文件。在本质上命名管道和匿名管道都是文件,而且都是内存文件,但前者有磁盘实例,后者无磁盘实例。


        井且还有一个重要的特点:当我们向命名管道中写入数据时候,该数据并不会刷新到磁盘中的命名管道文件,当我们从命名管道中读取数据时候,也不会是从磁盘文件的命名管道读取,而是从打开的命名管道读取数据;这么做就是为了效率!所以往磁盘文件中写入内容的效率远不如通过管道向文件写入内容。


1.3命名管道的创建

        命名管道被创建的方式有两种:一是在程序内通过调用mkfifo函数生成、另一种是使用mkfifo指令生成。

方式1:

         通过mkfifo函数的两个参数可知:第一个参数是需要填文件的路径的,第二个参数表示文件的权限,这个和umask掩码权限关联,在Linux中,umask默认是002,若不想用系统默认的umask,可以重新设置。

        mkfifo函数,成功则在磁盘中创建出一个管道文件,返回0;创建失败则返回-1

方式2:

 

1.4命名管道的实现

        案例说明:该案例需要用到两个进程,一个进程进行管道文件的创建和删除,且采用只读的方式打开管道文件——即从管道文件中读取数据;另一个进程只管采用write的方式去open管道文件——既向管道文件中写入数据。

        这就达到了一个进程写,一个进程读的目的,实现了数据通信。

Comm.hpp 头文件:

        在显示代码前,我来介绍一个系统调用函数:

       unlink()会删除参数pathname 指定的文件. 如果该文件名为最后连接点, 但有其他进程打开了此文件, 则在所有关于此文件的文件描述词皆关闭后才会删除. 如果参数pathname 为一符号连接, 则此连接会被删除。

返回值:成功则返回0, 失败返回-1, 错误原因存于errno

#pragma once
#include<iostream>
#include<unistd.h>
#include<cassert>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<string>
#include<cstring>
#include<cerrno>
#include<cstdlib>
using namespace std;
#define NAME_ "./tmp/pipe.106"    //采用宏定义的方式确定管道的路径
#define NUM 5

//创建管道函数
bool Create_(const std::string& path){
    umask(0);    //设置文件权限的掩码为0

    int n=mkfifo(path.c_str(),0666);
    if(n<0){
        cerr<<"error: "<<strerror(errno)<<endl;
        return false;
    }

    else{   
            cout<<"命名管道开辟成功!欢迎使用! "<<endl;
            return true;
    }
}

 //删除管道文件函数
void Remove(const std::string& path){
    int n=unlink(path.c_str());     //等到该文件的文件描述符都被关闭后才会被删除
    assert(n==0);                    //确保unlink调用成功
    (void)n;
}

Sever.cc代码(该进程是读取数据的):

#include "Comm.hpp"


int main(){
    //创建管道文件
    Create_(NAME_);

    //用读方式访问管道文件
    int rfd=open(NAME_,O_RDONLY);
    if(rfd<0){
        cerr<<"errno:"<<strerror(errno)<<endl;  //访问失败返回错误
        exit(-1);
    }

    //访问成功
    else{

        char buffer[1024];
        int cnt=0;
        //采用循环的方式不断的从管道中读取数据,并打印数据
        while(true){
            ssize_t r=read(rfd,buffer,sizeof(buffer)-1);

            //判断read的返回值,若大于0,表明读取成功
            if(r>0){
                buffer[r]=0;
                buffer[strlen(buffer)-1]=0;
                cout<<" Had received:#  "<<buffer<<endl;
            }

            //若等于0,表明管道中已经没有数据可读了,上一次已经读完了,直接退出循环
            else if(r==0){
                cout<<"Client had exited! ,me too!"<<endl;
                break;
            }
            //若小于0,表明管道中,数据读取错误,也直接跳出循环
            else{
                cout<<"Client exception, I quit right now! "<<endl;
                break;
            }
        }

        cout<<"Sever 正常退出!"<<endl;
    }

    //删除管道文件
    Remove(NAME_);
}

Sever.cc文件就是负责管道文件的创建和销毁的。 

Client.cc(该进程是写数据的):

#include "Comm.hpp"

int main(){
    //以写的方式打开管道文件
    int wfd=open(NAME_,O_WRONLY);
    if(wfd<0){
        cerr<<"error: "<<strerror(errno)<<endl;
        exit(-1);
    }

    //打开成功
    else{
        char buffer[1024];
        int cnt=0;
        while(true){
            cout<<"请输入您想输入的内容:"<<endl;
            fgets(buffer,sizeof(buffer),stdin);     //使用fgets函数可以将键盘中输入的内容输入C缓冲区
            write(wfd,buffer,strlen(buffer));       //将缓冲区内容写入管道中
        }
    }
    return 0;
}

1.4.2案例测试:

        首先先来运行一下Sever的可执行文件,先确保一下管道文件是否能被成功创建! 

 

 运行后Sever.cc的可执行文件后发现,管道文件被创建成功!

 

 但是第二次运行该可执行文件时,发现运行失败,原因是管道文件已存在!

        这是mkfifo函数的弊端,mkfifo是创建一个管道文件,成功则返回0,且会在指定路径下创建出一个管道文件。而第二次运行server可执行文件时,第一次的server运行已经在指定路径中创建出了管道文件,而第二次server又进行了一次管道文件的创建,同名文件不能在一个路径下创建两次,所以第二次的server运行失败,就是因为该文件在同一路径下创建两次导致的,若还想运行server,就需要先删除管道文件。

重新执行Sever:

 

 

1.4.3案例执行:

        我使用了两个端口进行进程通信的演示,左端口为读取进程,右端口为写进程:

1.首先运行Sever可执行文件,因为需要先创建出管道文件;

2.其次再运行Client可执行文件 ;

3.然后在Client那一端口就输入想要输入的内容到管道,回车确定;

4.紧接着Sever端口就会从管道中读取数据到显示屏;

       

这里需要注意:

        1.当Client不输入内容时,Server会处于阻塞状态,它将一直等待Client输入内容,Server才会继续进行读取。

        2.当Client写进程停止写入一一即关闭写端时,读端Server在读取完管道的所有内容后,系统也会随着写端关闭而正常结束while循环,进而正常关闭Server进程,这就是rfd返回值为0的情况。

        3.当Client写进程在写入的过程中,我们强制关闭了Server读进程,那么写端就会被强制关闭——异常终止Client写进程,读端被提前关闭,写端再写入就是非法的,所以会报异常退出!如下:

 


二.总结:

        命名管道(FIFO)最大的特性就是每个FIFO都有一个路径名与之相关联,从而允许无亲缘关系的任意两个进程间通过FIFO进行通信。所以FIFO有以下特性:

1.和管道一样,FIFO仅提供半双工的数据通信,即只支持单向的数据流;
2.和管道不同的是,FIFO可以支持任意两个进程间的通信;
3.FIFO 在文件系统中作为一个特殊的文件而存在,但 FIFO 中的内容却存放在内存中;

4.当使用 FIFO 的进程退出后,FIFO 文件将继续保存在文件系统中以便以后使用FIFO 有名字,不相关的进程可以通过打开命名管道进行通信。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/697347.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

如何学习PHP常用函数和内置库? - 易智编译EaseEditing

要学习PHP的常用函数和内置库&#xff0c;可以按照以下步骤进行&#xff1a; 学习PHP基础知识&#xff1a; 首先&#xff0c;掌握PHP的基础语法和核心概念。了解变量、数据类型、操作符、控制流程、函数等基本概念和语法规则。 查阅官方文档&#xff1a; PHP官方文档是学习P…

el-date-picker类型是datetimerange然后时间选择器的时分错位和el-time-picker 组件时分秒样式错位

第一种&#xff1a; 现象&#xff1a; 原因&#xff1a; 当分辨率过小且使用了px转换成vw的插件的话&#xff0c;存在vw的精确度不一致导致出现错位&#xff0c;【不知道不用vw转换插件会不会这样&#xff0c;但是解决方法应该是通用的】 解决方法&#xff1a; 在全局引入一…

高通 P-sensor 阈值调整

阈值效果debug 距离传感器调试基本没有太多工作量&#xff0c;主要根据整机来调整阈值&#xff0c;选择最合适的 P-Sensor 感应效果。高通平台sensor代码主要在modem侧&#xff0c;阈值设置是在AP侧&#xff0c;对应配置文件一般是&#xff1a; vendor/qcom/proprietary/senso…

【博客679】LVS NAT模式与FULLNAT模式原理与配置差别

LVS NAT模式与FULLNAT模式原理与配置差别 注意&#xff1a; LVS NAT模式是LVS原生的一种工作方式&#xff0c;而FULLNAT是在NAT模式下通过配置SNAT来 实现FULLNAT的&#xff0c;而且配合SNAT这部分是靠我们自己来实现的 1、LVS NAT模式原理与特点 NAT模式的数据包请求流程&…

基于matlab使用校准相机拍摄的两张图像中恢复相机运动并重建场景的3D结构(附源码)

一、前言 运动结构 &#xff08;SfM&#xff09; 是从一组 3-D 图像估计场景的 2-D 结构的过程。此示例演示如何从两个图像估计校准相机的姿势&#xff0c;将场景的三维结构重建为未知比例因子&#xff0c;然后通过检测已知大小的对象来恢复实际比例因子。 此示例演示如何从使…

如何开一家真人手办店?

从目前的情况来看&#xff0c;在3D建模这一个行业里&#xff0c;真人手办算是一个不错的风口&#xff0c;之前在圈子里刮起了一阵浪潮。手办大家都接触过&#xff0c;真人手办简单来说就是把以前手办的角色变成了真人&#xff0c;作为礼物和纪念品再合适不过。 许多人对这个新生…

【软件安装】Linux系统中安装Nginx服务器(Ubuntu系统)

这篇文章&#xff0c;主要介绍Linux系统中安装Nginx服务器&#xff08;Ubuntu系统&#xff09;。 目录 一、Linux安装Nginx 1.1、下载nginx安装包 &#xff08;1&#xff09;第一种方式 &#xff08;2&#xff09;第二种方式 1.2、上传nginx压缩包到Linux服务器 1.3、解压…

新项目搞完啦!!!

大家好&#xff0c;我是鱼皮。 经过了 7 场直播&#xff0c;总时长近 20 小时&#xff0c;我在 自己的编程导航 的第 5 个 全程直播开发 的项目 —— 智能 BI 项目&#xff0c;完结啦&#xff01; 我在这里对该项目做一个简单的总结&#xff0c;希望让更多需要它的同学看到&am…

爬虫-爬取Bing词典单词相关数据

主要爬取的数据如图片中的红框所示: 爬取效果如下所示: 具体爬虫代码私我&#xff5e;

Seata 四种模式对比总结

一、前言 通过以下系列章节&#xff1a; docker-compose 实现Seata Server高可用部署 | Spring Cloud 51 Seata AT 模式理论学习、事务隔离及部分源码解析 | Spring Cloud 52 Spring Boot集成Seata利用AT模式分布式事务示例 | Spring Cloud 53 Seata XA 模式理论学习、使用…

uniapp中微信小程序不能编译style绑定方法的解决方案

uniapp中动态style问题 这是我的代码设置了根据传参显示不同字体颜色和不同背景色 这两个方法我都写在methods中 methods: {// // 添加不同背景颜色getBackColor(val) {let color "";switch (val[4]) {case 0:color background:${this.colors[0]};break;case 1:col…

Python中的for循环语句及其应用举例(等差数列求和、阶乘、寻找最大值)

Python中的for循环语句及其应用举例(等差数列求和、阶乘、寻找最大值) 在学习任何编程语言的时候&#xff0c;不熟悉判断选择结构和循环结构&#xff0c;就难以发挥计算机优秀的计算能力和提高学习工作效率。本文将重点讲解Python中的for循环语句&#xff0c;并举例等差数列求…

数据分析:2023 最值得投资的 AI 公司榜单

文章目录 1 AI 公司的火爆程度和发展趋势1.1 AI 市场的规模和增长趋势1.2 全球 AI 公司数量和分布情况1.3 我们如何把握 AI 风口&#xff1f; 2 方法一&#xff1a;网络数据采集 - 使用亮数据代理2.1 动态 ip 代理&#xff0c;获取数据2.2 浏览器代理 3 方法二&#xff1a;网络…

Linux:nginx虚拟主机

基于域名 cd /usr/local/nginx/html/ mkdir aaa mkdir bbb echo www.aaa.com > aaa/index.html echo www.bbb.com > bbb/index.html vim /usr/local/nginx/conf/nginx.conf 修改server中的配置 server {listen 80;server_name www.benet.com;charset utf-8;access_log l…

spring boot项目敏感配置信息如何加密?

一般情况下&#xff0c;spring boot项目为了能够实时刷新配置信息&#xff0c;都会把配置文件放在nacos之类的配置中心上。但是这样就会存在一个问题&#xff0c;一些比较敏感的配置信息&#xff0c;比如数据库密码&#xff0c;一旦被泄露&#xff0c;就会有严重的数据安全问题…

剩余电流式电气火灾监控系统在火力发电厂的应用

张心志 安科瑞电气股份有限公司 上海嘉定 201801 摘要&#xff1a;本文首先指出了在火力发电厂中应用电气火灾监控系统的重要性&#xff0c;接着研究分析了电气火灾监控系统的应用情况&#xff0c;主要从 系统简介、电气火灾原因等方面进行了分析。*后从配电箱、火灾监控设备…

如何画时序图

10年产品经理教你3步画好UML时序图&#xff0c;轻松掌握流程分析利器【建议收藏】 - 知乎 转自知乎 上次介绍了活动图&#xff0c;这次分享 UML 中&#xff0c;另一种流程分析利器——时序图。 以前每次要分析流程&#xff0c;我都会用活动图。直到有一次&#xff0c;我面对…

ChatGPT伪原创:智能AI助手助力文章创作

智能AI助手助力文章创作 随着人工智能技术的不断发展&#xff0c;智能AI助手正逐渐成为文章创作的得力工具。无论是在写作过程中提供灵感和创意&#xff0c;还是在文章编辑和校对中提供帮助&#xff0c;智能AI助手都能为作者节省时间和精力&#xff0c;提高文章质量。本文将从…

剑指 Offer 55 - I. 二叉树的深度 / LeetCode 104. 二叉树的最大深度(二叉树后序遍历,回溯传递值)

题目&#xff1a; 链接&#xff1a;剑指 Offer 55 - I. 二叉树的深度&#xff1b;LeetCode 104. 二叉树的最大深度 难度&#xff1a;简单 输入一棵二叉树的根节点&#xff0c;求该树的深度。从根节点到叶节点依次经过的节点&#xff08;含根、叶节点&#xff09;形成树的一条…

Java 针对Word模板内容替换输出

对应依赖 <repositories><!-- 使用 Free Spire.Doc for Java 的jar包实现替换word中的内容--><repository><id>com.e-iceblue</id><name>e-iceblue</name><url>https://repo.e-iceblue.cn/repository/maven-public/</url&…