_Linux(共享内存)

news2024/10/3 0:34:57

文章目录

  • 0. 共享内存
  • 1. 共享内存示意图
  • 2. 共享内存函数
    • 2.1 shmget函数
    • 2.2 shmat函数
    • 2.3 shmdt函数
    • 2.4 shmctl函数
    • 2.5 查看共享内存指令
    • 2.6 删除共享方法
      • 2.6.1 指令删除
      • 2.6.2 代码删除
  • 3. 实例代码
    • 3.0 log.hpp打印日志信息
    • 3.1 comm.hpp(shmServer.cc和shmClicent.cc共有文件)
    • 3.2 shmServer.cc
      • 3.2.1 创建公共的key值
      • 3.2.2 创建共享内存
      • 3.2.3 挂接
      • 3.2.4 通信
      • 3.2.5 去关联
      • 3.3.6 删除共享内存
      • 3.3.7 总代码块
    • 3.3 shmClicent.cc
      • 3.3.1 总代码
    • 3.4 结果展示
  • 4. 总结

0. 共享内存

  • 共享内存区是最快的IPC形式。一旦这样的内存映射到共享它的进程的地址空间,这些进程间数据传递不再涉及到内核,换句话说是进程不再通过执行进入内核的系统调用来传递彼此的数据。

    共享内存=共享内存块+对应的共享内存的内核数据结构!
    

1. 共享内存示意图

在这里插入图片描述

2. 共享内存函数

2.1 shmget函数

  • 功能:用来创建共享内存
  • 原型
    • int shmget(key_t key, size_t size, int shmflg);
  • 参数
    • key:这个共享内存段名字
    • size:共享内存大小
    • shmflg:由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的。
  • 返回值:成功返回一个非负整数,即该共享内存段的标识码;失败返回-1。

2.2 shmat函数

  • 功能:将共享内存段连接到进程地址空间
  • 原型
    • void *shmat(int shmid, const void *shmaddr, int shmflg);
  • 参数
    • shmid: 共享内存标识
    • shmaddr:指定连接的地址
    • shmflg:它的两个可能取值是SHM_RND和SHM_RDONLY
  • 返回值:成功返回一个指针,指向共享内存第一个节;失败返回-1。

说明:

shmaddr为NULL,核心自动选择一个地址
shmaddr不为NULL且shmflg无SHM_RND标记,则以	shmaddr为连接地址。
shmaddr不为NULL且shmflg设置了SHM_RND标记,则连接的地址会自动向下调整为SHMLBA的整数倍。公式:	shmaddr - (shmaddr % SHMLBA)
shmflg=SHM_RDONLY,表示连接操作用来只读共享内存

2.3 shmdt函数

  • 功能:将共享内存段与当前进程脱离
  • 原型
    • int shmdt(const void *shmaddr);
  • 参数
    • shmaddr: 由shmat所返回的指针
  • 返回值:成功返回0;失败返回-1
  • 注意:将共享内存段与当前进程脱离不等于删除共享内存段。

2.4 shmctl函数

  • 功能:用于控制共享内存
  • 原型
    • int shmctl(int shmid, int cmd, struct shmid_ds *buf);
  • 参数
    • shmid:由shmget返回的共享内存标识码
    • cmd:将要采取的动作(有三个可取值)
    • buf:指向一个保存着共享内存的模式状态和访问权限的数据结构
  • 返回值:成功返回0;失败返回-1。
命令说明
IPC_STAT把shmid_ds结构中的数据设置为共享内存的当前关联值
IPC_SET在进程有足够的权限的前提下,把共享内存的当前关联值设置为shmid_ds数据结构中给出的值
IPC_RMID删除共享内存段

2.5 查看共享内存指令

ipcs -m

在这里插入图片描述

  • key 唯一值(共享内存名字)
  • shmid 共享内存标识
  • owner 共享内存拥有者名字
  • perms 拥有者对共享内存权限
  • bytes 共享内存空间大小
  • nattch 多少个进程关联共享内存
  • status 状态

2.6 删除共享方法

2.6.1 指令删除

ipcrm -m shmid

shmid就是上图的22

2.6.2 代码删除

int n = shmctl(shmid, IPC_RMID, nullptr);

3. 实例代码

3.0 log.hpp打印日志信息

#pragma once

#include <iostream>
#include <ctime>

enum e
{
    Debug,
    Notice,
    Warning,
    Error
};

const std::string msg[]{
    "Debug",
    "Notice",
    "Warning",
    "Error"};

std::ostream &log(std::string message, int level)
{
    std::cout << " | " << (unsigned)time(nullptr) << " | " << msg[level] << " | " << message;
    return std::cout;
}

3.1 comm.hpp(shmServer.cc和shmClicent.cc共有文件)

#pragma once

#include <iostream>
#include <cstring>
#include <string>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <cassert>
#include "log.hpp"

using namespace std;

#define PATH_NAME "/home/Ding"
#define PROJ_ID 0X66
#define SHM_SIZE 4096 //共享内存的大小(512字节的整数倍)

#define FIFO_NAME "./fifo"

//命名管道---控制不让通信的时候它疯狂的读取数据;解决时序性问题
class Init
{
public:
    Init()
    {
        umask(0);
        int n = mkfifo(FIFO_NAME, 0666);
        assert(n == 0);
        (void)n;
        log("创建管道文件成功", Notice) << "\n";
    }

    ~Init()
    {
        unlink(FIFO_NAME);
        log("删除管道文件成功", Notice) << "\n";
    }
};

#define READ O_RDONLY
#define WRITE O_WRONLY

int OpenFIFO(string pathname, int flags)
{
    int fd = open(pathname.c_str(), flags);
    assert(fd >= 0);
    (void)fd;
}

void Wait(int fd)
{
    log("等待中...", Notice) << "\n";
    uint32_t temp = 0;
    ssize_t s = read(fd, &temp, sizeof(uint32_t));
    assert(s == sizeof(uint32_t));
    (void)s;
}

void Signl(int fd)
{
    uint32_t temp = 1;
    ssize_t s = write(fd, &temp, sizeof(uint32_t));
    assert(s == sizeof(uint32_t));
    (void)s;
    log("唤醒中...", Notice) << "\n";
}

void CloseFIfo(int fd)
{
    close(fd);
}
PATH_NAME 路径只有你有权限进入的目录都可以

3.2 shmServer.cc

3.2.1 创建公共的key值

    key_t k = ftok(PATH_NAME, PROJ_ID);
    assert(k != -1);
    log("创建公共key值", Debug) << "server key: " << k << endl;
  • ftok 作用是生成一个唯一值(相同的算法)
  • 这样两个进程就能找到同一块共享内存;就像试锁开门一样。

3.2.2 创建共享内存

// 2. 创建一个全新的共享内存---通信的发起者
    int shmid = shmget(k, SHM_SIZE, IPC_CREAT | IPC_EXCL | 0666);
    if (shmid == -1)
    {
        perror("shmget");
        exit(1);
    }
    log("创建共享内存", Debug) << "shmid: " << shmid << endl;
  • PATH_NAME 单独使用的时候无论有没有共享内存都创建
  • PROJ_ID 单独使用无异于
  • 上面两个同时使用时,没有共享内存创建;有就报错

3.2.3 挂接

// 3. 将指定的共享内存,挂接到自己的地址空间
    char *shmaddr = (char *)shmat(shmid, nullptr, 0);
    log("挂接共享内存成功", Debug) << endl;
    sleep(10);

3.2.4 通信

// 这里就是通信的逻辑了
    int fd = OpenFIFO(FIFO_NAME, READ);
    for (;;)
    {
        Wait(fd);

        printf("%s\n", shmaddr);
        if (strcmp(shmaddr, "quit") == 0)
            break;
        // sleep(1);
    }

3.2.5 去关联

// 4. 将指定的共享内存,从自己的地址空间中去关联
    int n = shmdt(shmaddr);
    assert(n != -1);
    (void)n;
    log("去挂接共享内存成功", Debug) << "shmdt: " << n << endl;

3.3.6 删除共享内存

// 5. 删除共享内存, IPC_RMID即便是当前进程与shm挂接,仍删除。
    n = shmctl(shmid, IPC_RMID, nullptr);
    assert(n != -1);
    (void)n;
    log("删除共享内存成功", Debug) << "shmctl: " << n << endl;

3.3.7 总代码块

#include "comm.hpp"

//在加载的时候,会自动构建全局变量,就要调用该类的构造函数 -- 创建管道文件
// 程序退出的时候,全局变量会被析构,自动调用析构函数,会自动删除管道文件
Init init;
string TransToHex(key_t k)
{
    char buffer[32];
    snprintf(buffer, sizeof buffer, "0x%x", k);
    return buffer;
}

int main()
{
    // 1.创建公共的key值
    key_t k = ftok(PATH_NAME, PROJ_ID);
    assert(k != -1);
    log("创建公共key值", Debug) << "server key: " << TransToHex(k) << endl;

    // 2. 创建一个全新的共享内存---通信的发起者
    int shmid = shmget(k, SHM_SIZE, IPC_CREAT | IPC_EXCL | 0666);
    if (shmid == -1)
    {
        perror("shmget");
        exit(1);
    }
    log("创建共享内存", Debug) << "shmid: " << shmid << endl;

    // sleep(10);
    //  3. 将指定的共享内存,挂接到自己的地址空间
    char *shmaddr = (char *)shmat(shmid, nullptr, 0);
    log("挂接共享内存成功", Debug) << endl;
    // sleep(10);

    // 这里就是通信的逻辑了
    int fd = OpenFIFO(FIFO_NAME, READ);
    for (;;)
    {
        Wait(fd);

        printf("%s\n", shmaddr);
        if (strcmp(shmaddr, "quit") == 0)
            break;
        // sleep(1);
    }
    // 4. 将指定的共享内存,从自己的地址空间中去关联
    int n = shmdt(shmaddr);
    assert(n != -1);
    (void)n;
    log("去挂接共享内存成功", Debug) << "shmdt: " << n << endl;
    // sleep(10);

    // 5. 删除共享内存, IPC_RMID即便是当前进程与shm挂接,仍删除。
    n = shmctl(shmid, IPC_RMID, nullptr);
    assert(n != -1);
    (void)n;
    log("删除共享内存成功", Debug) << "shmctl: " << n << endl;

    CloseFIfo(fd);
    return 0;
}

3.3 shmClicent.cc

3.3.1 总代码

#include "comm.hpp"

int main()
{
    // 1.创建公共的key值
    key_t k = ftok(PATH_NAME, PROJ_ID);
    if (k < 0)
    {
        log("创建公共key值失败", Debug) << "server key: " << k << endl;
        exit(1);
    }
    log("创建公共key值", Debug) << "clicent key" << k << endl;

    // 2. 获取共享内存
    int shmid = shmget(k, SHM_SIZE, 0);
    if (shmid < 0)
    {
        log("获取共享内存失败", Debug) << "shmid: " << shmid << endl;
        exit(2);
    }
    log("获取共享内存", Debug) << "shmid: " << shmid << endl;

    // sleep(10);

    // 3. 挂接共享内存
    char *shmaddr = (char *)shmat(shmid, nullptr, 0);
    if (shmaddr == nullptr)
    {
        log("挂接共享内存失败", Debug) << endl;
        exit(3);
    }
    log("挂接共享内存成功", Debug) << endl;
    // sleep(10);

    //使用
    int fd = OpenFIFO(FIFO_NAME, WRITE);
    while (true)
    {
        ssize_t s = read(0, shmaddr, SHM_SIZE - 1);
        if (s > 0)
        {
            shmaddr[s - 1] = 0;
            Signl(fd);
            if (strcmp(shmaddr, "quit") == 0)
                break;
        }
    }
    // 4. 去关联
    int n = shmdt(shmaddr);
    assert(n != -1);
    (void)n;
    log("去挂接共享内存成功", Debug) << "shmdt: " << n << endl;

    //不需要chmctl删除

    return 0;
}

3.4 结果展示

在这里插入图片描述

4. 总结

将共享内存当成一个大字符串,通信的时候可以像malloc一样使用 char buffer[SHM_SIZE];

  • 结论1: 只要是通信双方使用shm,一方直接向共享内存中写入数据,另一方,就可以立马看到对方写入的数据。共享内存是所有进程间通信(IPC),速度最快的!不需要过多的拷贝!!(不需要将数据给操作系统)
  • 结论2: 共享内存缺乏访问控制!会带来并发问题 【如果我想一定程度的访问控制呢? 能】利用命名管道加以控制。

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

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

相关文章

Java 17的这些新特性,Java迈入新时代

前言 2021年9月14日Java 17发布&#xff0c;作为新时代的农民工&#xff0c;有必要了解一下都有哪些新东西。 Java 17是Java 11以来又一个LTS&#xff08;长期支持&#xff09;版本&#xff0c;Java 11 和Java 17之间发生了那些变化可以在OpenJDK官网找到JEP&#xff08;Java…

Unity中用Natrue Renderer做自己的地形Terrain.

效果图 一、下载与导入Nature Renderer Nature Renrderer是个强大的插件&#xff0c;它本身就可以作为地形编辑的工具取代Unity的地形细节和树木的渲染系统。 nature-renderer官网 1.下载链接 推荐&#xff08;已经购买的许可证&#xff0c;可直接使用&#xff09;&#xf…

设计原则和设计模式01

一&#xff1a;软件设计原则 1.单一职责原则&#xff1a; 有且只有一个原因引起类的变化(类或者接口的职责单一化) 2.里氏替换原则&#xff1a; 子类可以扩展父类的功能,但不能改变父类原有的功能 3.依赖倒置原则&#xff1a; 1.高层模块不应该依赖于底层模块&#xff0c…

Java注解

Java注解&#xff08;Annotation&#xff09; Java 注解&#xff08;Annotation&#xff09;又称 Java 标注&#xff0c;是 JDK5.0 引入的一种注释机制。 注解也叫元数据&#xff0c;一种代码级别的说明&#xff0c;说明程序的是给计算机看的&#xff0c;与类&#xff0c;接口…

事件绑定(onsubmit)表单提交

事件绑定(onsubmit)表单提交 学习路线&#xff1a;JavaScript基础语法&#xff08;输出语句&#xff09;->JavaScript基础语法&#xff08;变量&#xff09;->JavaScript基础语法&#xff08;数据类型&#xff09;->JavaScript基础语法&#xff08;运算符&#xff09…

Python笔记 · Python语言的“动态性”

尽管对于Python程序员来说已经司空见惯&#xff0c;但是当那些从非动态语言转过来的程序员初次看到形如self.xxxxxx的语句就是在定义对象属性时往往会感到“离奇”&#xff1a;一个未经声明的&#xff08;类似private int a;那样&#xff09;变量&#xff0c;直接从self中“点”…

java知识回顾笔记(对象、反射、内省、实例、父类、构造方法、封装、泛型、super())

类&对象 在创建了一个类时&#xff0c;只声明但不赋值&#xff0c;其默认值为&#xff1a; 理解下图含义&#xff0c;即可理解对象和类&#xff1a; 实例 对象又被称为实例&#xff0c;实例变量被创建时&#xff0c;系统默认会赋值&#xff0c;例如&#xff1a; Studen…

什么知识库工具适合小团队?看看文档管理系统+NAS的最新解决方案

编者按&#xff1a;还在为团队选那款网盘而发愁吗?试试文档管理系统和NAS结合吧&#xff0c;高效率低成本&#xff0c;适合小团队。 关键词&#xff1a;免维护&#xff0c;免安装&#xff0c;大容量&#xff0c;在线编辑&#xff0c;文档共享&#xff0c;数据安全 对于企业或…

LeetCode-66-加一

1、从后向前遍历 我们可以从后向前遍历数组&#xff0c;针对不同的情况进行操作&#xff1a;1、若当前数字不为9&#xff0c;则我们直接将数字的值加一并返回即可&#xff1b;2、若当前数字为9&#xff0c;我们将当前数字置为0并对前一位执行加一操作&#xff1b;3、若所有数字…

后端接口时通时不通,团队全链路排查实战

背景&#xff1a; 1 最近团队做了一套系统&#xff0c;已经临近上线了&#xff1b; 2 后端的服务和前端的代码都是新写的&#xff0c;两边的服务器&#xff0c;数据库也都是新申请的&#xff1b; 3 本来测试的时候用的测试服务器&#xff0c;一切都挺好的&#xff0c;但部署到线…

基于分发与计算的GRTN全球实时传输网络

一张能同时满足「分发」与「计算」需求的网。 从直播趋势看「分发」与「计算」 阿里云直播产品架构图中&#xff0c;主要分为端和云两个部分&#xff1a;在端侧&#xff0c;主要包含推流端和播放端&#xff1b;在云侧&#xff0c;一是基于分布式节点构建的传输网&#xff0c;二…

mosquitto部署mqtt broker 并测试订阅与发布

mosquitto部署mqtt broker 并测试订阅与发布 1&#xff0c;MQTT协议介绍 MQTT(消息队列遥测传输)是ISO 标准(ISO/IEC PRF 20922)下基于发布/订阅范式的消息协议。它工作在 TCP/IP协议族上&#xff0c;是为硬件性能低下的远程设备以及网络状况糟糕的情况下而设计的发布/订阅型…

如何使用phpstudy在服务器上发布Discuz_X3.4_SC_UTF8_20220811和zzcms2023

Web-ZZCMS&#xff1a; 源码下载&#xff1a;http://www.zzcms.net/about/6.html Web-Discuz&#xff1a; 源码下载&#xff1a;https://www.discuz.net/ 首先安装phpstudy&#xff0c;傻瓜式安装教程&#xff0c;如果中途 遇到报错比如我遇到的是Apache未启动&#xff0c;80端…

Ansible自动化部署安装openGauss3.1企业版单机

文章目录一、背景二、环境准备三、具体实施步骤3.1、安装ansible3.2、配置主机清单3.3、测试主机连通性3.4、创建相关目录3.5、下载openGauss软件包到files目录3.6、创建变量文件3.7、创建安装时需要的xml模板3.8、创建任务文件四、执行自动化安装4.1、校验语法4.2、自动化安装…

CAD新手必练图形三

这次说的这个CAD新手必练图形&#xff0c;用到了CAD直线、圆、多边形、修剪和旋转等多个CAD功能命令&#xff0c;一起跟着操作一下吧 目标图形 操作步骤 1.使用CAD直线命令绘制一条垂直的直线和两条水平的直线&#xff0c;两条水平的直线之间的距离为15&#xff0c;并将他们修…

【CSDN竞赛第11期】编程竞赛总结

文章目录前言/背景题目与解题代码1. 圆小艺2. K皇把妹3. 筛选宝物4. 圆桌CSDN编程竞赛报名地址&#xff1a;https://edu.csdn.net/contest/detail/16 前言/背景 目前已经连续参加10次CSDN的编程竞赛了&#xff0c;这种比赛挺有意义&#xff0c;希望一直举办下去&#xff01; 题…

JDBC 访问数据库

文章目录一、实验目的二、实验要求三、实验内容1、设计数据库表2、定义实体类3、定义数据库连接类4、实现数据库的增删改查5、测试用例一、实验目的 理解 JDBC 的工作原理&#xff0c;掌握 JDBC 访问数据库&#xff1b; 掌握常见数据库 MYSQL &#xff1b; 二、实验要求 理解…

【华为上机真题 2022】字符串序列判定

&#x1f388; 作者&#xff1a;Linux猿 &#x1f388; 简介&#xff1a;CSDN博客专家&#x1f3c6;&#xff0c;华为云享专家&#x1f3c6;&#xff0c;Linux、C/C、云计算、物联网、面试、刷题、算法尽管咨询我&#xff0c;关注我&#xff0c;有问题私聊&#xff01; &…

中断系统中的设备树__中断号的演变与irq_domain

1 中断号与硬件相关_号码固定 当我们的系统中只有一两个中断控制器时&#xff0c;上面的方法很有用&#xff0c;可以给每一个中断预先确定好他的虚拟中断号&#xff0c;但是当中断控制器越来越多、当中断越来越多&#xff0c;上述方法(virq和hwirq固定绑定)有缺陷: a. 增加工作…

马克思的手稿-第11届蓝桥杯Scratch选拔赛真题精选

[导读]&#xff1a;超平老师计划推出Scratch蓝桥杯真题解析100讲&#xff0c;这是超平老师解读Scratch蓝桥真题系列的第92讲。 蓝桥杯选拔赛每一届都要举行4~5次&#xff0c;和省赛、国赛相比&#xff0c;题目要简单不少&#xff0c;再加上篇幅有限&#xff0c;因此我精挑细选…