【Linux】IPC进程间通信:并发编程实战指南(一)

🌈 个人主页:Zfox_
🔥 系列专栏:Linux

目录

  • 一:🔥 进程间通信介绍
    • 🦋 1.进程通信的目的
    • 🦋 2.进程通信的方式
  • 二:🔥 「匿名管道」通信
    • 🦋 匿名管道通信代码
    • 🦋 用fork来共享管道原理
    • 🦋 站在文件描述符角度-深度理解管道
    • 🦋 站在内核角度-管道本质
    • 🦋 管道读写规则
    • 🦋 管道特点
  • 三:🔥 进程池
  • 四:🔥 命名管道通信
    • 🎀 创建一个命名管道
    • 🎀 匿名管道与命名管道的区别
    • 🎀 命名管道的打开规则
    • 🎀 命名管道通信代码
  • 五:🔥 共勉

一:🔥 进程间通信介绍

🦋 1.进程通信的目的

  • 数据传输一个进程需要将它的数据发送给另一个进程
  • 资源共享多个进程之间共享同样的资源
  • 通知事件一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止 时要通知父进程)
  • 进程控制有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变。

🦋 2.进程通信的方式

进程通信的前提是能让不同的进程看到操作系统中的同一份资源,但是为了保证进程之间的独立性(各进程不能访问其他进程的地址空间),由操作系统创建一份共享资源。

进程需要通信,就需要让操作系统创建共享资源,所以操作系统必须提供给进程不同的调用接口,用于创建不同的共享资源,实现不同种类的进程通信。

  • 进程通信有两种通信标准:System V 标准和 POSIX 标准(都是本地通信)

System V标准中有三种通信方式:消息队列共享内存信号量

\qquad 🦁 但是 System V 标准需要重新构建操作系统代码来实现进程通信,比较繁琐。在 System V 标准出现之前,还有一种通信方式是 「管道通信」「管道通信」 是直接复用现有操作系统的代码。

(现在本地通信已经被网络通信取代,所以本文只重点介绍管道通信和共享内存通信)

二:🔥 「匿名管道」通信

  • \qquad 进程可以通过 读/写 的方式打开同一个文件,操作系统会创建两个不同的文件对象 file,但是文件对象 file 中的内核级缓冲区、操作方法集合等并不会额外创建,而是一个文件的文件对象的内核级缓冲区、操作方法集合等通过指针直接指向另一个文件的内核级缓冲区、操作方法集合等。这样以读方式打开的文件和以写方式打开的文件共用一个内核级缓冲区。
    在这里插入图片描述

  • \qquad 进程通信的前提是不同进程看到同一份共享资源,所以根据上述原理,父子进程可以看到同一份共享资源:被打开文件的内核级缓冲区。父进程向被打开文件的内核级缓冲区写入,子进程从被打开文件的内核级缓冲区读取,这样就实现了进程通信!这里也将被打开文件的内核级缓冲区称为 「 管道文件」。

🐮 此外,管道通信只支持单向通信,即只允许父进程传输数据给子进程,或者子进程传输数据给父进程。当父进程要传输数据给子进程时,就可以只使用以写方式打开的文件的管道文件,关闭以读方式打开的文件,同样的,子进程只是用以读方式打开的文件的管道文件,关闭掉以写方式打开的文件。父进程向以写方式打开的文件的管道文件写入,子进程再从以读方式打开的文件的管道文件读取,从而实现管道通信。如果是要子进程向父进程传输数据,同理即可。

🦋 匿名管道通信代码

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

在这里插入图片描述

\qquad 🦁 匿名管道通信(此处为子进程向父进程单向通信)就是使用pipe系统调用接口打开文件并创建子进程,子进程向管道文件中写入,父进程从管道文件中读取。

#include <iostream>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

// Father process -> read
// Child process -> write

int main()
{
    // 1.创建管道
    int fds[2] = {0};

    int n = pipe(fds);  // fds:输出型参数
    if(n != 0)
    {
        std::cerr << "pipe error" << std::endl;
        return 1;
    }


    // 2. 创建子进程
    pid_t id = ::fork();
    if(id < 0)
    {
        std::cerr << "fork error" << std::endl;
        return 2;
    }
    else if(id == 0)
    {
        // 子进程
        // 3. 关闭不需要的fd

        ::close(fds[0]);       // 关闭0端 读端

        int cnt = 0;
        while(true)
        {
            std::string message = "hello bit, hello ";
            message += std::to_string(::getpid());
            message += ", ";
            message += std::to_string(cnt);

            ::write(fds[1], message.c_str(), message.size());
            cnt++;
            ::sleep(1);
            break;
        }

        ::close(fds[1]);
        ::exit(0);
    }
    else 
    {
        // 父进程
        // 3. 关闭不需要的fd
        ::close(fds[1]);        // 关闭1端写端

        char buffer[1024];
        while(true)
        {
            ssize_t n = ::read(fds[0], buffer, 1024);
            if(n > 0)
            {
                buffer[n] = 0;
                std::cout << "child->father, message: " << buffer << std::endl;
            }
            else if(n == 0)
            {
                // 如果写端关闭
                // 读端读完管道内部的数据, 在读取的时候,
                // 就会读取到返回值0, 表示对端关闭, 也表示读到文件结尾
                std::cout << "n: " << n << std::endl;
                std::cout << "child quit ??? me too" << std::endl;
                break;
            }
        }

        ::close(fds[0]);
        pid_t rid = waitpid(id, nullptr, 0);
        std::cout << "father wait child success: " << rid << std::endl;
    }

    return 0;
}

🦋 用fork来共享管道原理

在这里插入图片描述

🦋 站在文件描述符角度-深度理解管道

在这里插入图片描述

🦋 站在内核角度-管道本质

在这里插入图片描述
🎯 所以,看待管道,就如同看待文件一样!管道的使用和文件一致,迎合了“Linux一切皆文件思想“

🦋 管道读写规则

  • 当没有数据可读时
    • read 调用阻塞,即进程暂停执行,一直阻塞等待
    • read 调用返回-1,errno值为EAGAIN。
  • 当管道满的时候
    • write 调用阻塞,直到有进程读走数据
    • 调用返回-1,errno值为 EAGAIN
  • 如果所有管道写端对应的文件描述符被关闭,则read返回0
  • 如果所有管道读端对应的文件描述符被关闭,则write操作会产生信号

🦋 管道特点

  • 只能用于具有共同祖先的进程(具有亲缘关系的进程)之间进行通信;通常,一个管道由一个进程创建,然后该进程调用fork,此后父、子进程之间就可应用该管道。
  • 管道提供流式服务。
  • 一般而言,进程退出,管道释放,所以管道的生命周期随进程。
  • 一般而言,内核会对管道操作进行同步与互斥。
  • 管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道。

三:🔥 进程池

\qquad 父进程创建多个子进程,并为每个子进程创建一个管道文件,父进程为写端,子进程为读端。父进程给子进程通过管道传输任务,这就是进程池。

\qquad 如果父进程没有给子进程传输任务,即管道文件中没有数据,根据进程通信情况1,读端即子进程会阻塞等待父进程传输任务。

\qquad 此外父进程还要给子进程平衡任务,不能让某个进程特别繁忙,其他进程没有任务可做。这就是负载均衡。

🎁 Gitee仓库源码实现

四:🔥 命名管道通信

  • 管道应用的一个限制就是只能在具有共同祖先(具有亲缘关系)的进程间通信。
  • 如果我们想在不相关的进程之间交换数据,可以使用FIFO文件来做这项工作,它经常被称为命名管道。
  • 命名管道是一种特殊类型的文件

🎀 创建一个命名管道

  • Linux系统中,使用 mkfifo 命令创建有名管道文件,再使用两个进程打开即可
$ mkfifo filename
  • Linux 系统编程中使用 mkfifo 函数创建一个管道文件,再让两个不相关的进程打开:
int mkfifo(const char *pathname, mode_t mode);

mkfifo 函数原型:pathname 是管道文件的路径名,mode 是设置管道的权限。函数成功时返回 0,失败时返回 -1,并设置errno以指示错误原因。

  • unlink 函数用于删除文件系统中的一个文件
int unlink(const char *pathname);

unlink 函数原型:pathname 是管道文件的路径名。函数成功时返回 0,失败时返回 -1,并设置 errno 以指示错误原因。

🎀 匿名管道与命名管道的区别

  • 匿名管道由pipe函数创建并打开。
  • 命名管道由mkfifo函数创建,打开用open。
  • FIFO(命名管道)与pipe(匿名管道)之间唯一的区别在它们创建与打开的方式不同,一但这些工作完成之后,它们具有相同的语义。

🎀 命名管道的打开规则

  • 如果当前打开操作是为读而打开FIFO时
    • O_NONBLOCK disable:阻塞直到有相应进程为写而打开该FIFO
    • O_NONBLOCK enable:立刻返回成功
  • 如果当前打开操作是为写而打开FIFO时
    • O_NONBLOCK disable:阻塞直到有相应进程为读而打开该FIFO
    • O_NONBLOCK enable:立刻返回失败,错误码为ENXIO

🎀 命名管道通信代码

🦁 一共有五个文件:Client.hpp、Common.hpp、Client.cc、Server.cc、Server.hpp

  • Common.hpp 文件中是全局公共函数和方法
  • Client.cc 文件是客户端,用作写端进程
  • Client.hpp 文件是客户端的封装
  • Server.cc 文件是服务端,用作读端进程
  • Server.hpp 文件是服务端的封装和初始化方法

Common.hpp

#pragma once

#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>

const std::string gpipeFile="./fifo";
const mode_t gmode = 0600;
const int gdefultfd = -1;
const int gsize = 1024;
const int gForRead = O_RDONLY;
const int gForWrite = O_WRONLY;

int OpenPipe(int flag)
{
    int fd = ::open(gpipeFile.c_str(), flag);
    if(fd < 0)
    {
        std::cerr << "open error" << std::endl;
        return fd;
    }
    return fd;
}

void ClosePipeHelper(int fd)
{
    if(fd >= 0)
        ::close(fd);
}

Server.hpp

#pragma once

#include <iostream>
#include "Comm.hpp"

class Server
{
public:
    Server()
        :_fd(gdefultfd)
    {}

    bool OpenPipeForRead()
    {
        _fd = OpenPipe(gForRead);
        if(_fd < 0) return false;
        return true;
    }

    // std::string *: 输出型参数
    // const std::string &: 输入型参数
    // std::string &: 输入输出型参数
    int RecvPipe(std::string *out)
    {
        char buffer[gsize];
        ssize_t n = ::read(_fd, buffer, sizeof(buffer) - 1);
        if(n > 0)
        {
            buffer[n] = 0;
            *out = buffer;
        }
        return n;
    }

    void ClosePipe()
    {
        ClosePipeHelper(_fd);
    }

    ~Server()
    {}
private:
    int _fd;
};


class Init
{
public:
    Init()
    {
        ::umask(0);
        int n = ::mkfifo(gpipeFile.c_str(), gmode);  // 命名管道
        if(n == -1)
        {
            std::cerr << "mkfifo error" << std::endl;
            return ;
        }
        std::cout << "mkfifo success" << std::endl;
    }

    ~Init()
    {
        int n = ::unlink(gpipeFile.c_str());
        if(n == -1)
        {
            std::cerr << "unlink error" << std::endl;
            return ;
        }
        std::cout << "unlink success" << std::endl;
    }
};

Init init;

Server.cc

#include "Server.hpp"
#include <iostream>

int main()
{
    Server server;
    server.OpenPipeForRead();

    std::string message;
    while(true)
    {
        if(server.RecvPipe(&message) > 0)
        {
             std::cout << "client Say# " << message << std::endl;
        }
        else 
        {
            break;
        }
    }

    std::cout << "client quit, me too!" << std::endl;
    server.ClosePipe();
    return 0;
}```

**Client.hpp**

```cpp
#pragma once

#include <iostream>
#include "Comm.hpp"

class Client
{
public:
    Client()
        :_fd(gdefultfd)
    {}

    bool OpenPipeForWrite()
    {
        _fd = OpenPipe(gForWrite);
        if(_fd < 0) return false;
        return true;
    }

    // std::string *: 输出型参数
    // const std::string &: 输入型参数
    // std::string &: 输入输出型参数
    int SendPipe(const std::string &in)
    {
        return ::write(_fd, in.c_str(), in.size());
    }

    void ClosePipe()
    {
        ClosePipeHelper(_fd);
    }

    ~Client()
    {}
private:
    int _fd;
};

Client.cc

#include "Client.hpp"
#include <iostream>

int main()
{
    Client client;
    client.OpenPipeForWrite();

    std::string message;
    while(true)
    {
        std::cout << "Please Enter# ";
        std::getline(std::cin, message);
        client.SendPipe(message);
    }

    client.ClosePipe();
    return 0;
}

五:🔥 共勉

以上就是我对 【Linux】动静态库:构建强大软件生态的基石 的理解,觉得这篇博客对你有帮助的,可以点赞收藏关注支持一波~😉
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

数据采集-Kepware OPCUA 服务器实现

KepserverEX OPC UA server设置 系列文章目录 数据采集-Kepware 安装证书异常处理 目录 KepserverEX OPC UA server设置系列文章目录一、OPC UA(OPC Unified Architecture)二、防火墙的配置三、配置KepserverEX的OPC UA3.1 启用远程连接3.2 启动OPCUA服务器接口 四、管理OPCU…

spring-boot(mybatisplus条件构造、接口、生成器)

条件构造器 除了新增以外&#xff0c;修改、删除、查询的SQL语句都需要指定where条件。因此BaseMapper中提供的相关方法除了以id作为where条件以外&#xff0c;还支持更加复杂的where条件。 使用&#xff1a; 第一步创建条件查询&#xff1a; //条件的构造查询 QueryWrapper…

单个相机矫正畸变

1、通过标定助手获取到内参外参&#xff0c;外参在此无效&#xff0c;只用到了内参 2、然后通过halcon算子进行矫正 参考&#xff1a;超人视觉

金华迪加网络科技有限公司现场大屏互动系统存在文件上传漏洞

漏洞描述 金华迪加网络科技有限公司专注于开发和优化现场互动系统平台,其主要产品是现场活动大屏幕系统。这个系统被设计用于增强活动现场的互动性&#xff0c;提供技术支持给合作企业。某公司现场大屏互动系统存在文件上传漏洞&#xff0c;未经身份验证的攻击者通过漏洞上传恶…

构 造 器

我们创建了一个对象&#xff0c;在其中定义了属性&#xff0c;new一个对象&#xff0c;然后设置对应的属性&#xff0c;但是我们可以在new对象的时候&#xff0c;同时传入我们要设置的属性&#xff0c;这个时候就需要构造器。 特点 构造方法是一个特殊的成员方法&#xff0c;…

Android平台RTSP|RTMP播放器高效率如何回调YUV或RGB数据?

技术背景 我们在做Android平台RTSP、RTMP播放器的时候&#xff0c;经常遇到这样的技术诉求&#xff0c;开发者希望拿到播放器解码后的YUV或RGB数据&#xff0c;投递给视觉算法&#xff0c;做AI分析&#xff0c;本文以ffmpeg和大牛直播SDK的SmartPlayer为例&#xff0c;介绍下相…

个体营业执照经营异常多久解除

经营异常是怎么回事&#xff1f;是什么意思&#xff1f;首先&#xff0c;我们要明确什么是公司经营异常。简单来说&#xff0c;就是公司在经营过程中出现了一些问题&#xff0c;导致公司无法正常运营。这些问题可能包括未按规定报送年度报告、未按规定公示有关信息等。那么&…

Air780E如何发送SMS?一文详解!

今天一起来学习使用合宙低功耗4G模组Air780E发送SMS短消息&#xff1a; 一、SMS简介 SMS&#xff08;短消息服务&#xff0c;ShortMessageService&#xff09;功能主要用于在蜂窝网络中传输短消息。 在4G网络中&#xff0c;短信可以在数据传输的同时进行&#xff0c;不会因数…

金华迪加 现场大屏互动系统 mobile.do.php 任意文件上传漏洞复现

0x01 产品简介 金华迪加现场大屏互动系统是一种集成了先进技术和创意设计的互动展示解决方案,旨在通过大屏幕和多种交互方式,为观众提供沉浸式的互动体验。该系统广泛应用于各类活动、展览、会议等场合,能够显著提升现场氛围和参与者的体验感。 0x02 漏洞概述 金华迪加 现…

基于Java+SpringBoot+Vue的视频网站系统的设计与实现

基于JavaSpringBootVue的视频网站系统的设计与实现 前言 ✌全网粉丝20W,csdn特邀作者、博客专家、CSDN[新星计划]导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末附源码下载链接&#x1…

ComfyUI - ComfyUI 工作流中集成 SAM2 + GroundingDINO 处理图像与视频 教程

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://spike.blog.csdn.net/article/details/143359538 免责声明&#xff1a;本文来源于个人知识与公开资料&#xff0c;仅用于学术交流&#xff0c;欢迎讨论&#xff0c;不支持转载。 SAM2 与…

【华硕天选5开机黑屏只有鼠标,调用资源管理器也无法黑屏状态的一种解决方式】

华硕天选5开机黑屏只有鼠标&#xff0c;调用资源管理器也无法黑屏状态的一种解决方式 1.问题描述2.解决方法3.重启如下图 1.问题描述 华硕天选5开机黑屏只有鼠标&#xff0c;调用资源管理器&#xff08;ctrlalt.&#xff09;也无法黑屏状态。 2.解决方法 ctrl shitf10 就能正…

CAN通讯中常见的接口异常问题及其解决方案

控制器局域网(CAN)是一种广泛应用于汽车、工业自动化等领域的串行通信网络。由德国博世公司(Bosch)和英特尔(Intel)共同开发的CAN协议因其高效的实时性、良好的抗干扰能力和可靠的多主结构,成为了现代汽车电子系统中不可或缺的一部分。随着CAN控制器价格的逐渐降低,越来…

Jmeter基础篇(19)JSR223预处理器

前言 JSR223预处理器是Apache JMeter中的一个组件&#xff0c;它允许用户使用任何支持Java Scripting API (JSR 223) 的脚本语言来执行预处理任务。这个功能非常强大&#xff0c;因为它让测试人员能够利用如Groovy、JavaScript&#xff08;Nashorn引擎&#xff09;、BeanShell…

Unity简易版成就系统

转自&#xff1a; https://learn.u3d.cn/tutorial/achievements-onecredit?chapterId63562b28edca72001f21d129# 脚本AchievementSystem using System.Collections; using UnityEngine; using UnityEngine.UI;public class AchievementSystem : MonoBehaviour {public Achi…

shell脚本案例:RAC配置多路径时获取磁盘设备WWID和磁盘大小

使用场景 在RAC配置多路径时&#xff0c;需要获取到磁盘设备的wwid。因为RAC的磁盘配置是提前规划好的&#xff0c;只知道wwid&#xff0c;不知道磁盘对应大小&#xff0c;是不知道应该如何配置多路径的mutipath.conf文件的&#xff1b;而凭借肉眼手工去对应磁盘设备的wwid和大…

数据建模圣经|数据模型资源手册卷3,数据建模最佳实践

简介 本书采用了类设计模式的方式对数据模型进行高度抽象总结&#xff0c;展现了常见的数据模型构建模型等模型的作用、层次、分类、地位、沟通方式&#xff0c;和业务规则。使用一个强大的数据模型模式的数据建模&#xff0c;评估特定与广义模型的优缺点&#xff0c;有助于你改…

Diving into the STM32 HAL-----Interrupts

硬件管理就是处理异步事件。其中大部分来自硬件外围设备。例如&#xff0c;计时器达到配置的 period 值&#xff0c;或者 UART 在数据到达时发出警告。 中断是一个异步事件&#xff0c;它会导致按优先级停止执行当前代码&#xff08;中断越重要&#xff0c;其优先级越高;这将导…

clickhouse运维篇(三):生产环境一键生成配置并快速部署ck集群

前提条件&#xff1a;先了解集群搭建流程是什么样&#xff0c;需要改哪些配置&#xff0c;有哪些环境&#xff0c;这个文章目的是简化部署。 clickhouse运维篇&#xff08;一&#xff09;&#xff1a;docker-compose 快速部署clickhouse集群 clickhouse运维篇&#xff08;二&am…

「C/C++」C++11 之<thread>多线程编程

✨博客主页何曾参静谧的博客📌文章专栏「C/C++」C/C++程序设计📚全部专栏「VS」Visual Studio「C/C++」C/C++程序设计「UG/NX」BlockUI集合「Win」Windows程序设计「DSA」数据结构与算法「UG/NX」NX二次开发「QT」QT5程序设计「File」数据文件格式「PK」Parasolid函数说明目…