Linux进程间通信(一)——管道通信

news2024/12/24 10:06:20

目录

前言

1.管道实现进程间通信

①管道的所属问题

②匿名管道通信

③命名管道通信

2.使用管道通信实现一个进程池

①进程池类图

②Channel类实现

③ProcessPoll类实现

④代码一览


前言

在学习Linux中的进程时,曾提到过进程的独立性。进程独立性的是进程与进程之间在出错时不会相互影响的重要保证。但是由此也引发的一个问题,如果进程之间想要相互传递信息怎么办,由于进程与进程之间独立,一个进程不能访问另一个进程中的资源与数据等信息。这个时候就需要在进程之间建立信道来实现进程间通信。本文介绍进程间通信中的一个方式——管道通信。


1.管道实现进程间通信

①管道的所属问题

在讲解管道通信之前,我们需要明确一下管道的所属问题。管道本质上是一个文件,进程通信的两方,一方向文件中写,另一方从文件中读就实现了一种进程间的通信。但是请注意,由于进程独立性的影响,如果将这个用于进程间通信的文件定义在进程通信两方的任何一方的进程中,都会导致另一方不能读取到数据,所以管道独立不归属于进程通信的两方,而是归属于操作系统。

②匿名管道通信

匿名管道顾名思义就是进程要通过一个没有名字的文件进行进程间通信。

我们需要使用pipe系统调用接口来创建一个匿名管道。而后使用对应的文件描述符进行读写操作。

int pipe(int fd[2]);
//其中fd数组是一个输出型参数,数组中的第一个元素表示写文件描述符,第二个元素表示读文件描述符
//该接口在正确执行时返回0,出错返回错误码
图2        匿名管道通信示例

 图2所示的代码,首先定义了一个fd数组作为pipe接口的参数传入,当函数执行结束后,fd数组中被填充了读写文件描述符,而后使用fork创建子进程,子进程可以继承父进程的所有数据包括fd数组和数组指向文件的关系,这样就父子进程就都可以对文件进行读写操作了,进而实现了进程通信的一种方式。

注意:

管道通信的特性:

Ⅰ.匿名管道只能用于有血缘关系的进程之间通信

Ⅱ.匿名管道的生命周期与进程一致(父子进程任意一端关闭都会导致管道关闭)

Ⅲ.单个管道是半双工的,即通信的进程间都可以读取或接收数据但是不能同时进行读和写。

Ⅳ.管道是面向字节流的

管道通信的特殊情况:

Ⅰ.管道没有数据,读端阻塞等待。

Ⅱ.管道中数据已满,读端不读写段阻塞等待。

Ⅲ.当写端不继续写入数据并且关闭了写端管道,读端将管道中的数据读完后关闭。

Ⅳ.当读端不继续读取数据并且关闭了读端管道,操作系统会立即终止写入进程。

③命名管道通信

如果你足够的细心你会发现匿名管道一个很大的问题,就是实现进程间通信的文件没有名字,这也就意味着,没有血缘关系的进程不能使用匿名管道进行通信。因为匿名管道没有名称,进程间又相互独立,没有血缘关系又不能进行数据的继承,所以没有血缘关系的进程不能使用匿名管道进行通信。命名管道就是为了应对匿名管道的缺陷而诞生的。

//命名管道有两种创建方式

//在命令行中
mkfifo filename

//在程序中
int mkfifo(const char *filename,mode_t mode) 

//说明:
//filename表示命名管道的名称,可自行确定
//mode表示文件的权限,注意该参数与文件掩码相减后才是文件的权限
图3        命名管道进程通信示例

注意:

命名管道除了在打开方式上与匿名管道不同,其它行为表现与匿名管道一致。 

2.使用管道通信实现一个进程池

①进程池类图

图4        管道与进程池类图

②Channel类实现

Channel类用来表示一对父子进程之间的关系。在管道通信中,对于管道的开闭,我们可以通过控制写端描述符来进行控制,对于一个管道当写端关闭时,读端读完管道中的内容后就会自动关闭;对于程序的执行结果,需要使用wait/waitpid来进行获取,但是在进程池中往往一次性创建多个进程,如果使用wait来获取进程返回值,那么很难确定是哪一个进程进行的返回,所以我们需要使用waitpid来进行对特定pid的进程获取返回值,这就要求在Channel中要存储子进程的pid;我们也可以为管道加一个名字,用来更明显地区分同一批进程(实际上可以直接使用pid进行区分)。

class Channel
{
private:
    int _wfd;
    int _childid;
    std::string _name;
public:
    Channel(int wfd,int childid,std::string& name):_wfd(wfd),_childid(childid),_name(name){}
    int getfd()
    {
        return _wfd;
    }
    int getid()
    {
        return _childid;
    }
    std::string& name()
    {
        return _name;
    }
    
    void CloseChannl()
    {
        close(_wfd);
    }

};

③ProcessPoll类实现

进程池的生成需要用户通过命令行参数来进行控制,当执行可以执行程序时,需要在可执行程序后加上要生成进程池中进程的数量;而后创建一个传入参数大小的进程池,而后等待创建的进程返回结果,最后将所有的Channl弹出Channels。

class ProcessPool
{
private:
    int _ProcessNum;
    std::vector<Channel> Channels;
  
public:
    ProcessPool(int ProcessNum=1):_ProcessNum(ProcessNum){}

    void CreatePool()
    {
        std::vector<int> fds;
       for(int i=0;i<_ProcessNum;i++)
       {
            int pip[2];
            pipe(pip);
            int task_index=NextTask();
            write(pip[1],&task_index,sizeof(int));
            std::string name = "process-"+std::to_string(i);
            
            fds.push_back(pip[1]);

            int id=fork();

            if(id<0)
            {
                std::cout<<PipFailed<<std::endl;
                break;
            }

            if(id==0)
            {
                std::cout<<getpid()<<std::endl;
                if(fds.size()!=0)   //处理不必要的管道
                {
                    for(auto&e:fds)
                    {
                        close(e);
                        std::cout<<e<<' ';
                    }
                }        
                AssignTask(pip[0]);
                exit(0);
            }

            close(pip[0]);
            Channels.push_back(Channel(pip[1],id,name));
       }
    }
    void KillAll()
    {
        while(Channels.size())
        {
            Channel& tmp=Channels.back();
            tmp.CloseChannl();
            std::cout<<tmp.name()<<" exit..."<<std::endl;
            Channels.pop_back();
        }

    }

    void Wait()
    {
        for(auto&e:Channels)
        {
            int status;
            if(e.getid()==waitpid(e.getid(),&status,0))
            {
                std::cout<<e.name()<<" pid:"<<e.getid()<<" exit_code:"<<status<<std::endl;
            }
        }
    }

④代码一览


#include "task.hpp"

//使用说明
void Usage(char *ExeFileName)
{
    std::cout<<"Usage:"<<std::endl;
    std::cout<<'\t'<<ExeFileName<<" : "<<"number (You should give a positive number)"<<std::endl;
}

//创建一个管道类
//该类用于描述一对父子进程之间的关系
//变量说明: 
//_wfd  用来控制管道,当写端关闭后,管道内容为空,写端自动关闭,也可以由父进程向子进程发送数据
//_childid  子进程id值,等待子进程退出,子进程退出时,唯一标识
//_name 用于调试,无实际意义
class Channel
{
private:
    int _wfd;
    int _childid;
    std::string _name;
public:
    Channel(int wfd,int childid,std::string& name):_wfd(wfd),_childid(childid),_name(name){}
    int getfd()
    {
        return _wfd;
    }
    int getid()
    {
        return _childid;
    }
    std::string& name()
    {
        return _name;
    }
    
    void CloseChannl()
    {
        close(_wfd);
    }

};

class ProcessPool
{
private:
    int _ProcessNum;
    std::vector<Channel> Channels;
  
public:
    ProcessPool(int ProcessNum=1):_ProcessNum(ProcessNum){}

    void CreatePool()
    {
        std::vector<int> fds;
       for(int i=0;i<_ProcessNum;i++)
       {
            int pip[2];
            pipe(pip);
            int task_index=NextTask();
            write(pip[1],&task_index,sizeof(int));
            std::string name = "process-"+std::to_string(i);
            
            fds.push_back(pip[1]);

            int id=fork();

            if(id<0)
            {
                std::cout<<PipFailed<<std::endl;
                break;
            }

            if(id==0)
            {
                std::cout<<getpid()<<std::endl;
                if(fds.size()!=0)   //处理不必要的管道
                {
                    for(auto&e:fds)
                    {
                        close(e);
                        std::cout<<e<<' ';
                    }
                }        
                AssignTask(pip[0]);
                exit(0);
            }

            close(pip[0]);
            Channels.push_back(Channel(pip[1],id,name));
       }
    }
    void KillAll()
    {
        while(Channels.size())
        {
            Channel& tmp=Channels.back();
            tmp.CloseChannl();
            std::cout<<tmp.name()<<" exit..."<<std::endl;
            Channels.pop_back();
        }

    }

    void Wait()
    {
        for(auto&e:Channels)
        {
            int status;
            if(e.getid()==waitpid(e.getid(),&status,0))
            {
                std::cout<<e.name()<<" pid:"<<e.getid()<<" exit_code:"<<status<<std::endl;
            }
        }
    }

    
    ~ProcessPool(){}
    ProcessPool& operator=(ProcessPool&) = delete;
};

int main(int argc,char * argv[])
{
    int process_num=1;
    if(argc!=2||atoi(argv[1])<=0)
    {
        Usage(argv[0]);
        exit(1);
    }
    process_num=atoi(argv[1]);

    ProcessPool *Pool=new ProcessPool(process_num);
    
    Pool->CreatePool();
    Pool->Wait();
    
    Pool->KillAll();
    
    return 0;
}

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

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

相关文章

SpringMVC后台控制端校验-表单验证深度分析与实战优化

前言 在实战开发中&#xff0c;数据校验也是十分重要的环节之一&#xff0c;数据校验大体分为三部分&#xff1a; 前端校验后端校验数据库校验 本文讲解如何在后端控制端进行表单校验的工作 案例实现 在进行项目开发的时候,前端(jquery-validate),后端,数据库都要进行相关的数据…

【数据结构】图的最短路径

快乐的流畅&#xff1a;个人主页 个人专栏&#xff1a;《C游记》《进击的C》《Linux迷航》 远方有一堆篝火&#xff0c;在为久候之人燃烧&#xff01; 文章目录 引言一、最短路径的概念二、Dijkstra算法2.1 思想2.2 实现 三、Bellman-Ford算法3.1 思想3.2 实现 四、Floyd-Warsh…

操作教程|基于DataEase用RFM分析法分析零售交易数据

DataEase开源BI工具可以在店铺运营的数据分析及可视化方面提供非常大的帮助。同样&#xff0c;在用于客户评估的RFM&#xff08;即Recency、Frequency和Monetary的简称&#xff09;分析中&#xff0c;DataEase也可以发挥出积极的价值&#xff0c;通过数据可视化大屏的方式实时展…

液态神经网络 LNN

神经网络 (NN) 是 机器学习 模仿人脑结构和运算能力以从训练数据中识别模式的算法。 通过处理和传输信息的互连人工神经元网络&#xff0c;神经网络可以执行复杂的任务&#xff0c;例如 人脸识别, 自然语言理解&#xff0c;以及无需人工协助的预测分析。 尽管神经网络是一种强…

Mac电脑SourceTree git账号密码更改提示再次输入密码

前言&#xff1a; 最近小编git账号密码修改了&#xff0c;之前在sourceTree的git仓库在拉代码提交代码就会报错&#xff0c;提示因为密码导致的仓库连接失败。 解决方案 1.在mac电脑应用程序中搜索“钥匙串” 点击钥匙串访问 在钥匙串中选登录&#xff0c;在在右侧列表中找…

key形式和key/value形式二叉树

首先模拟一下key形式类 使用的结构是搜索二叉树 结点中有左孩子和右孩子 还有一个存储的值 template <class K>struct BSTnode//搜索二叉树不支持修改 中序遍历是有序的{K _key;BSTnode<K>* _left;BSTnode<K>* _right;BSTnode(const K& key):_key(key…

【C++】12.string类的使用

文章目录 1. 为什么学习string类&#xff1f;1.1 C语言中的字符串1.2 两个面试题(暂不做讲解) 2. 标准库中的string类2.1 string类(了解)2.2 auto和范围for 3. 查看技术文档4. string的访问5. 如何读取每个字符呢&#xff1f;6. auto语法糖&#xff08;C11&#xff09;7. 范围f…

spring boot 2.7整合Elasticsearch Java client + ingest attachment实现文档解析

一、软件环境 软件版本号备注Spring boot2.7.23.x版本建议使用ElasticSearch8.xElasticSearch7.17.4ElasticSearch 7.x 可使用JDK 8 ElasticSearch 8.x 要求使用JDK 11 二、安装ElasticSearch 下载地址&#xff1a;https://artifacts.elastic.co/downloads/elasticsearch/el…

网站建设中,虚拟主机的各项指标和参数

虚拟主机的各项指标和参数主要包括空间大小、并发连接数、带宽限制、流量限制、CPU限制、内存以及IO速度等。以下是对这些指标和参数的详细介绍&#xff1a; 空间大小&#xff1a;空间大小通常以MB或GB为单位&#xff0c;表示虚拟主机可以容纳的数据量。例如&#xff0c;一个1…

地级市-城市创业活跃度(每百人新创企业数)(2000-2021年)

城市创业活跃度通常指一个城市在一定时期内新创企业的数量和质量&#xff0c;它反映了城市的创业环境、创业者的积极性和创造力。根据中的研究&#xff0c;创业活跃度&#xff08;Entre_Activation&#xff09;作为反映区域层面创业活动积极程度的核心指标&#xff0c;被广泛用…

【Vue】Vue扫盲(二)指令:v-for 、v-if、v-else-if、v-else、v-show

【Vue】Vue扫盲&#xff08;一&#xff09;事件标签、事件修饰符&#xff1a;click.prevent click.stop click.stop.prevent、按键修饰符、及常用指令 文章目录 一、v-for遍历数组数组角标遍历对象&#xff1a;Key作用介绍 二、v-if、v-show基本用法&#xff1a;案例&#xff1…

【unity框架开发12】从零手搓unity存档存储数据持久化系统,实现对存档的创建,获取,保存,加载,删除,缓存,加密,支持多存档

文章目录 前言一、Unity对Json数据的操作方法一、JsonUtility方法二、Newtonsoft 二、持久化的数据路径三、数据加密/解密加密方法解密方法 四、条件编译指令限制仅在编辑器模式下进行加密/解密四、数据持久化管理器1、存档工具类2、一个存档数据3、存档系统数据类4、数据存档存…

【Photoshop——肤色变白——曲线】

1. 三通道曲线原理 在使用RGB曲线调整肤色时&#xff0c;你可以通过调整红、绿、蓝三个通道的曲线来实现黄皮肤到白皮肤的转变。 黄皮肤通常含有较多的红色和黄色。通过减少这些颜色的量&#xff0c;可以使肤色看起来更白。 具体步骤如下&#xff1a; 打开图像并创建曲线调…

几何完备的3D分子生成/优化扩散模型 GCDM-SBDD - 评测

GCDM 是一个新的 3D 分子生成扩散模型&#xff0c;与之前的 EDM 相比&#xff0c;GCDM 优化了其中的图神神经网络部分&#xff0c;使用手性敏感的 SE3 等变神经网络 GCPNET 代替了 EDM 中的 EGNN&#xff0c;让节点间消息传递、聚合根据手性不同而进行。本文对 GCDM-SBDD&#…

DMN决策引擎入门知识点

本文主要讲解Camunda是如何使用Dmn决策引擎&#xff0c;体验地址:www.jeecgflow.com Dmn决策表定义 Dmn在线设计 命中策略(Hit Policy) 策略名称策略描述Unique只有一个或者没有规则可以满足。决策表的结果包含满足规则的输出条目。如果超过一个规则满足&#xff0c;那么就违…

电脑知识:适用于 Windows 10 的 PDF 编辑器列表

PDF 是一种流行的、多功能且安全的文件格式&#xff0c;用于在线共享文档。但是&#xff0c;如果没有合适的应用程序&#xff0c;查看和编辑 PDF 文件可能会变得复杂。 幸运的是&#xff0c;有很多 PDF 编辑器可以帮助您更正重要文档上的错误、填写表格、为合同添加签名、更改…

【个人成长】编程小白如何成为大神?

1. 选择适合自己的编程语言 作为新手&#xff0c;选择一门适合自己的编程语言至关重要。不同的编程语言有不同的应用领域和特点。以下是几种适合初学者的编程语言&#xff1a; Python&#xff1a;广泛应用于数据科学、人工智能、自动化脚本和Web开发等领域。它语法简洁易懂&a…