进程间通信之System V 共享内存

news2024/10/7 12:29:01

文章目录

  • 一、共享内存

进程具有独立性,因此进程间通信的前提是两个进程能看到同一份资源

一、共享内存

在内存中申请一块空间,并将起始地址分别映射到两个进程的虚拟地址空间上,便可以让两个进程看到同一份资源

在这里插入图片描述

操作系统为了管理共享内存,除了创建共享内存外,还会创建包含共享内存信息的结构体

系统调用 shmget,头文件 sys/ipc.h、sys/shm.h

// 创建或获取共享内存
int shmget(key_t key, size_t size, int shmflg)

返回值:返回共享内存标识符 (供用户使用),出错返回 -1,并且 errno 被设置为相应的出错信息

参数:

  • key 用于创建或获取共享内存
    由于进程具有独立性,为了让两个进程可以找到同一个共享内存,通信双方的进程约定,创建共享内存的进程将 key 值设置到共享内存对应的结构体中,获取共享内存的进程通过相同的 key 来查找共享内存

  • size 表示共享内存的大小
    操作系统实际申请的共享内存大小为 size 向上调整到 PAGE_SIZE(4KB) 的整数倍

  • shmflag 位图,传递多个标志位时,标志位之间用 | 连接

常用的标志位:

  • IPC_CREAT 表示如果 key 值对应的共享内存不存在则创建,存在则获取
  • IPC_EXCL 不能单独使用,需要和 IPC_CREAT 一起传,表示如果 key 值对应的共享内存不存在则创建,存在则出错,即保证获取的共享内存一定是最新的
  • 0ddd 八进制数,表示共享内存的拥有者、所属组及 other 的操作权限

系统调用 ftok,头文件 sys/types.h、sys/ipc.h,生成创建共享内存时所需要的 key 值

// key_t 类型就是 int 的 typedef
key_t ftok(const char *pathname, int proj_id)

系统调用 shmat / shmdt,头文件 sys/types.h、sys/shm.h

// 将标识符为 shmid 的共享内存与进程虚拟地址 shmaddr 关联,如果 shmaddr 指定 NULL,表示由操作系统自主决定与进程关联的虚拟地址
// shmflg 指定 0 表示以读写方式关联共享内存,指定 SHM_RDONLY 表示以读方式关联共享内存,不存在以写方式关联共享内存,此时进程必须具有共享内存相应的操作权限
// 成功返回与共享内存关联的虚拟地址,失败返回 (void*) -1
void *shmat(int shmid, const void *shmaddr, int shmflg)

// 解除进程虚拟地址 shmaddr 和共享内存的关联
// 成功返回 0,失败返回 -1
int shmdt(const void *shmaddr)

共享内存的生命周期随操作系统,因此创建共享内存的进程退出后,并不会删除共享内存,需要通过系统调用或者指令删除共享内存

系统调用 shmctl,头文件 sys/ipc.h、sys/shm.h

// 对标识符为 shmid 的共享内存执行 cmd 指定的操作,buf 用来设置或者获取共享内存的属性,不需要则指定为 NULL
// cmd 指定 IPC_RMID 表示将共享内存标记为销毁,当共享内存的关联数为 0 时,共享内存会自动销毁
// 成功返回 0,失败返回 -1
int shmctl(int shmid, int cmd, struct shmid_ds *buf)

server 从共享内存中读取,client 向共享内存中写入

// shm.hpp
#ifndef __SHARED_MEMORY__
#define __SHARED_MEMORY__

#include <iostream>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/stat.h>

#define PATHNAME "."
#define PROID 0x666
#define CREATSHM (IPC_CREAT | IPC_EXCL)
#define GETSHM IPC_CREAT
#define CREAT 0 // 创建者
#define GET 1 // 获取者

const size_t gsize = 4096;

namespace starrycat
{
    class SharedMemory
    {
    public:
        SharedMemory(int flag, mode_t mode, size_t size = gsize, const char *pathname = PATHNAME, int proid = PROID)
            : _type((flag == CREATSHM) ? CREAT : GET)
        {
            umask(0);

            // 生成唯一的 key
            key_t key = ftok(pathname, proid);
            if (key < 0)
            {
                std::cout << "key 生成失败: " << errno << " " << strerror(errno) << std::endl;
                exit(1);
            }

            // CREATSHM 表示用 key 值创建共享内存,GETSHM 表示通过 key 值获取共享内存
            _shmid = shmget(key, size, flag | mode);
            if (_shmid < 0)
            {
                std::cout << "共享内存创建失败: " << errno << " " << strerror(errno) << std::endl;
                exit(2);
            }

            // 关联
            _shmaddr = static_cast<char*>(shmat(_shmid, nullptr, 0));
            if (_shmaddr == (char*)-1)
            {
                std::cout << "共享内存关联失败: " << errno << " " << strerror(errno) << std::endl;
                exit(3);
            }
        }

        SharedMemory(const SharedMemory& shm) = delete;
        SharedMemory& operator=(const SharedMemory& shm) = delete;

        ~SharedMemory()
        {
            // 去关联
            shmdt(_shmaddr);

            // 销毁
            if (_type == CREAT) shmctl(_shmid, IPC_RMID, nullptr);
        }

        char* getAddr() { return _shmaddr; }

    private:
        int _type;
        int _shmid;
        char* _shmaddr;
    };
}

#endif

// server.cc
#include "shm.hpp"
#include <iostream>
#include <unistd.h>

using namespace std;

int main()
{
    starrycat::SharedMemory shm(CREATSHM, 0666);
    char* start = shm.getAddr();

    // 通信
    for (int i = 0; i < 5; ++i)
    {
        cout << "从共享内存中读取: " << start << endl;
        sleep(1);
    }

    return 0;
}

// client.cc
#include "shm.hpp"
#include <iostream>
#include <unistd.h>


using namespace std;

int main()
{
    starrycat::SharedMemory shm(GETSHM, 0666);
    char* start = shm.getAddr();

    // 通信
    for (int i = 0; i < 3; ++i)
    {
        start[i] = 'A' + i;
        sleep(1);
    }

    return 0;
}

先启动 myserver,在启动 myclient

在这里插入图片描述

  • ipcs -m
    功能:查看共享内存
  • ipcmk -M 大小 -p 权限
    功能:创建共享内存
  • ipcrm -M/-m key/shmid
    功能:通过 key / shmid 删除共享内存

在这里插入图片描述

共享内存映射到进程的虚拟地址后,进程不需要通过系统调用来读取或写入数据,因此共享内存是所有进程间通信中速度最快的

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

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

相关文章

DP读书:不知道干什么就和我一起读书吧——以《鲲鹏处理器 架构与编程》中鲲鹏软件的构成为例

DP读书&#xff1a;不知道干什么就和我一起读书吧 为啥写博客&#xff1a;好处一&#xff1a;记录自己的学习过程优点二&#xff1a;让自己在各大社群里不那么尴尬推荐三&#xff1a;坚持下去&#xff0c;找到一个能支持自己的伙伴模版&#xff1a;鲲鹏软件构成硬件特定软件1. …

五、工厂方法模式

一、什么是工厂方法模式 工厂方法模式是对简单工厂模式的进一步抽象化&#xff0c;其好处是可以使系统在不修改原来代码的情况下引进新的产品&#xff0c;即满足开闭原则。   工厂方法模式的主要角色如下。 抽象工厂&#xff08;Abstract Factory&#xff09;&#xff1a;提…

USB集线器设计

参考电路 参考1 基于FE1.1S四路USB集线器设计 基于FE1.1S四路USB集线器设计 - 嘉立创EDA开源硬件平台 (oshwhub.com)https://oshwhub.com/beibu/ji-yufe1-1s-si-luusb-ji-xian-qi-she-ji 参考2 C479658_LCYZB-SL2.1A USB集线器验证板-V1 C479658_LCYZB-SL2.1A USB集线器验…

视频导出文件太大如何变小?缩小视频这样做

作为一名视频制作爱好者&#xff0c;我们经常需要导出视频文件&#xff0c;但是&#xff0c;有时候我们会发现导出的视频文件太大&#xff0c;给上传和分享带来很大的不便。那么&#xff0c;如何将视频文件变小呢&#xff1f;下面将为你介绍三个方法&#xff0c;让你轻松解决视…

【python】之time库,创建数字时钟!

今天我们来看一下time库的一个使用&#xff0c;提到time库&#xff0c;大家应该很熟悉吧&#xff0c;作为python的内置库&#xff0c;无需安装&#xff0c;直接导入使用即可。 一.time库 常用函数介绍 1.time.time( )&#xff1a;返回当前时间的时间戳 import timetime.time(…

5分钟快速搭建!这款颜值爆表的数据可视化工具,你值得拥有!

最好的数据可视化工具是什么&#xff1f; 没有最好&#xff0c;只有最适合的。不过&#xff0c;想要找一个优秀的数据可视化工具&#xff0c;可以从下面几点进行评估&#xff1a; &#xff08;1&#xff09;易用性&#xff1a; 直观的界面可以帮助新手快速上手&#xff0c;并…

知了汇智2023夏令营集训活动圆满收官,产教融合助力高素质人才培养

当前&#xff0c;新一轮科技革命和产业变革深入发展&#xff0c;新产业、新业态、新技术的涌现不断带动了对互联网人才、IT技术人才、信息安全人才需求的快速增长&#xff0c;在这一背景下&#xff0c;大学生需要提升专业应用技能和职场竞争力&#xff0c;以满足企业对人才日益…

Blender界面学习03 原点、鼠标所在位置的缩放与旋转

物体的坐标原点可以移动 放大缩小时默认是屏幕中央&#xff0c;修改为鼠标在哪儿就缩放哪儿 默认旋转时围绕屏幕的中心 可以修改为指定对象旋转

Matlab论文插图绘制模板第110期—水平双向柱状图

在之前的文章中&#xff0c;分享了很多Matlab柱状图的绘制模板&#xff1a; 进一步&#xff0c;再来看一种特殊的柱状图&#xff1a;水平双向柱状图。 先来看一下成品效果&#xff1a; 特别提示&#xff1a;本期内容『数据代码』已上传资源群中&#xff0c;加群的朋友请自行下…

vscode搭建springboot开发环境

前言 idea好用到但是收money&#xff0c;eclipse免费但是界面有点丑&#xff0c;所以尝试使用vscode开发springboot 提前准备 安装jdk&#xff0c;jdk需要大于11 安装vscode 安装maven 安装插件 主要是下面的插件 Extension Pack for JavaSpring Boot Extension PackDepe…

一个可以使用的聚合登录系统源码,可以实现一站式社会化账号登录。

简单测试了一下&#xff0c;可以跑起来&#xff0c;这个代码可以使用&#xff0c;但关键代码都加密了&#xff0c;所以使用时需要慎重。本来这种加密的垃圾代码我是不分享的&#xff0c;但有些同学还是需要参考一下程序设计思路&#xff0c;所以免费分享给大家研究学习使用。 …

走进低代码平台| iVX-困境之中如何突破传统

前言&#xff1a; “工欲善其事,必先利其器”&#xff0c;找到和使用一个优质的工具平台&#xff0c;往往会事半功倍。 文章目录 1️⃣认识走近低代码2️⃣传统的低代码开发3️⃣无代码编辑平台一个代码生成式低代码产品iVX受面性广支持代码复用如何使用&#xff1f; 4️⃣总结…

hive下库里有表数据,删不了库的解决办法

hive下库里有表数据&#xff0c;删不了库的解决办法 报错&#xff1a;FAILED: Execution Error, return code 1 from org.apache.hadoop.hive.ql.exec.DDLTask. InvalidOperationException(message:Database db_hive2 is not empty. One or more tables exist.) 使用CASCADE &…

中欧财富:分布式数据库的应用历程和 TiDB 7.1 新特性探索

作者&#xff1a;张政俊 中欧财富数据库负责人 中欧财富是中欧基金控股的销售子公司&#xff0c;旗下 APP 实现业内基金品种全覆盖&#xff0c;提供基金交易、大数据选基、智慧定投、理财师咨询等投资工具及服务。中欧财富致力为投资者及合作伙伴提供一站式互联网财富管理解决方…

【Linux】JumpServer 堡垒机远程访问

文章目录 前言1. 安装Jump server2. 本地访问jump server3. 安装 cpolar内网穿透软件4. 配置Jump server公网访问地址5. 公网远程访问Jump server6. 固定Jump server公网地址 前言 JumpServer 是广受欢迎的开源堡垒机&#xff0c;是符合 4A 规范的专业运维安全审计系统。JumpS…

1761. 一个图中连通三元组的最小度数

每日一题 1761. 一个图中连通三元组的最小度数 难度&#xff1a;困难 只会强行枚举 class Solution:def minTrioDegree(self, n: int, edges: List[List[int]]) -> int:fromTo {}for edge in edges:a fromTo.get(edge[0], [])a.append(edge[1])fromTo[edge[0]] aa fro…

一百六十八、Kettle——用海豚调度器定时调度从Kafka到HDFS的任务脚本(持续更新追踪、持续完善)

一、目的 在实际项目中&#xff0c;从Kafka到HDFS的数据是每天自动生成一个文件&#xff0c;按日期区分。而且Kafka在不断生产数据&#xff0c;因此看看kettle是不是需要时刻运行&#xff1f;能不能按照每日自动生成数据文件&#xff1f; 为了测试实际项目中的海豚定时调度从…

从零学算法(剑指 Offer 36)

123.输入一棵二叉搜索树&#xff0c;将该二叉搜索树转换成一个排序的循环双向链表。要求不能创建任何新的节点&#xff0c;只能调整树中节点指针的指向。 为了让您更好地理解问题&#xff0c;以下面的二叉搜索树为例&#xff1a; 我们希望将这个二叉搜索树转化为双向循环链表。…

智能界面组件DevExpress BI Dashboard — 新的导出功能(v23.1)

在DevExpress v23.1发布周期中&#xff0c;官方扩展了DevExpress BI Dashboard项的导出功能&#xff0c;这些增强功能适用于WinForms、WPF和Web平台&#xff0c;包括以下选项&#xff1a; 将自定义Dashboard项导出到Excel并自定义导出的文档本身在导出期间自定义Pivot Grid Da…

数组——二分查找

二分查找前提条件 数组是有序的数组中没有重复的元素 二分方法一般有两种写法 一 左闭右闭 当左大于右时&#xff0c;整体结束。 如果左小于等于右则继续查找。 每次mid 左加&#xff08;(右减左)/2&#xff09;除2操作也可以看做时右移一位 如果mid 值为目标则返回 否则mi…