【Linux详解】——共享内存

news2024/11/24 8:37:05

📖 前言:本期介绍共享内存。


目录

  • 🕒 1. 共享内存的原理
  • 🕒 2. 共享内存的概念
    • 🕘 2.1 接口认识
    • 🕘 2.2 演示生成key的唯一性
    • 🕘 2.3 再谈key
  • 🕒 3. 共享内存相关命令
  • 🕒 4. 利用共享内存进行进程间通信
  • 🕒 5. 共享内存的优缺点

🕒 1. 共享内存的原理

在之前学过的进程地址空间的基础上,我们知道,进程之间具有独立性,因为每个进程的内核数据结构的数据以及页表的映射都是独立的。而对于共享内存,我们同样了解,这是为了让进程之间能够进行通信的公共空间,接下来就通过进程地址空间的结构去了解共享空间的位置及原理:
在这里插入图片描述

OS为了让两个毫不相关的进程之间进行通信,进行了三个工作:

  1. 在对应的内存当中让用户帮OS申请一块空间(通过指定的调用接口)
  2. 将创建好的内存映射进进程的地址空间(用户就可以通过访问起始地址的方式来进行对申请的这块内存空间的访问)
  3. 未来不想通信:
    • 取消进程和内存的映射关系
    • 释放内存

因此,我们把申请的这块空间称之为共享内存,将映射关系称之为进程和共享内存进行挂接。将取消进程和内存的映射关系称之为去关联,释放内存释放的就是共享内存。

理解

  • 进程间通信,是专门设计的,用来IPC的,和malloc/new不是一个东西。
  • 共享内存是一种通信方式,所有想通信的进程,都可以用。
  • OS中一定会存在着很多共享内存。

🕒 2. 共享内存的概念

通过让不同的进程,看到同一个内存块的方式,叫做共享内存。

🕘 2.1 接口认识

#include<sys/ipc.h>
#include<sys/shm.h>

int shmget(key_t key, size_t size, int shmflg);// size:共享内存的大小

对于shmflg,常见的有两种选择:

  • IPC_CREAT:如果不存在共享文件则创建,存在则获取
  • IPC_EXCL
    • 无法单独使用,单独使用没有意义,需要结合IPC_CREAT
    • IPC_CREAT|IPC_EXCL:如果不存在,就创建,如果已存在,就出错返回。即在用户的角度,如果创建成功,一定是一个新的shm

shmget返回值: 记住他是一个标识符就够用了,得到的是共享内存的标识符。(和文件fd没有任何关系)

key: 是什么不重要,最重要的是其具备的唯一性。

而获取key值,则通过一个新的接口:ftokftok通过指定的字符串数据*pathname以及char类型的proj_id数据进行一系列的算法整合返回了具有唯一性的Key:

key_t ftok(char *pathname, char proj_id);

由于创建的key值有可能已经被别人使用了,因此有失败的可能性。创建Key值如果失败,则返回-1。

🕘 2.2 演示生成key的唯一性

# Makefile
.PHONY:all
all:shm_client shm_server

shm_client:shm_client.cc
	g++ -o $@ $^ -std=c++11
shm_server:shm_server.cc
	g++ -o $@ $^ -std=c++11

.PHONY:clean
clean:
	rm -f shm_client shm_server
// comm.hpp
#ifndef _COMM_HPP_
#define _COMM_HPP_

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

#define PATHNAME "."//当前路径
#define PROJ_ID 0x66

key_t getKey()
{
    key_t k = ftok(PATHNAME, PROJ_ID);
    if(k < 0)
    {
        // cin, cout, cerr ->stdin, stdout, stderr->0, 1, 2;标准错误stderr向2打印。
        std::cerr << errno << ":" << strerror(errno) << std::endl;
        exit(1);//终止进程
    }
    return k;
}

#endif
// shm_server.cc
#include"comm.hpp"

int main()
{
    key_t k = getKey();

    printf("0x%x\n", k);
    return 0;
}
// shm_client.cc
#include"comm.hpp"

int main()
{
    key_t k = getKey();

    printf("0x%x\n", k);
    return 0;
}
[hins@VM-12-13-centos shm]$ ./shm_server
0x6601062a
[hins@VM-12-13-centos shm]$ ./shm_client
0x6601062a

通过make后执行发现,两个程序的k值是一样的,这就证明了ftok指定参数的返回值是唯一的。(k实际上就是32位的一个整数)

🕘 2.3 再谈key

OS中一定存在多个共享内存,因为彼此之间可能都需要通信,因此也就都需要申请一块空间。而OS申请的共享空间,也一定和进程一样需要被管理,既然需要管理,那么一定也是先描述再组织的方式,即共享内存 = 物理内存块+共享内存的相关属性 。

之前谈到过,key是什么不重要,能进行唯一性的标识最重要,因此创建共享内存的时候,是如何保证共享内存在系统中是唯一的呢?当然是通过key来确定的,只要一个进程也看到了同一个key,就能够访问这个共享内存。那么key在哪里,实际上这就和PCB一样,key就在内核中的属性集合里,即:

struct shm{
    key_t key;
    //...
}

即:key是通过shmget这样的系统调用,设置进入共享内存属性中,用来表示该共享内存在内核中的唯一性!

shmid和key就好比fd和inode。为什么有了key还需要shmid呢?通过key和shmid的区分,能够面向系统层面和用户层面,这样能够更好的进行解耦,以免内核中的变化影响到用户级。

🕒 3. 共享内存相关命令

通过让不同的进程,看到同一个内存块的方式,叫做共享内存。

查看共享内存ipcs -m/-q/-s (共享内存/消息队列/信号量数组)

删除共享内存ipcrm -m shmnid

将共享内存与虚拟内存进行关联:

#include <sys/types.h>
#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr, int shmflg);// 共享内存id,被映射的进程地址空间(给nullptr),给0默认可以读写。成功时,将返回共享内存的虚拟地址。失败返回-1(失败错误码errno被设置) 

将共享内存与虚拟内存去关联

#include <sys/types.h>
#include <sys/shm.h>
int shmdt(const void *shmaddr);//参数:shmat的返回值。成功时,将返回0。失败返回-1(失败错误码errno被设置)

控制(主要用移除)共享内存(shmctl)

#include <sys/ipc.h>
#include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_ds *buf);//shmid(类似fd),传入系统设定的宏,shmid_ds数据结构。传入IPC_RMID移除共享内存成功时,将返回0。失败返回-1(失败错误码errno被设置)

🕒 4. 利用共享内存进行进程间通信

// comm.hpp
#pragma once
#include <iostream>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <cstdlib>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
 
 
#define PATHNAME "."//当前路径(路径都行)
#define PROJ_ID 0X55//项目id也无要求
#define MAX_SIZE 4096
key_t getKey()
{
    key_t k=ftok(PATHNAME, PROJ_ID);
    if(k==-1)
    {
        std::cout<<"ftok"<<errno<<":"<<strerror(errno)<<std::endl;
        exit(1);
    }
    return k;
}
int getShmHelper(key_t key,int flags)
{
    int shmid=shmget(key,MAX_SIZE,flags);
    if(shmid==-1)//创建共享内存失败
    {
        std::cerr<<"shmget"<<errno<<":"<<strerror(errno)<<std::endl;
        exit(2);
    }
    return shmid;//返回共享内存标识符
}
int getShm(key_t key)//创建||获取共享内存
{
    return getShmHelper(key,IPC_CREAT);//传0也行
}
int createShm(key_t key)//必定创建共享内存
{
    return getShmHelper(key,IPC_CREAT|IPC_EXCL|0600);//生成一个全新的共享内存
}
void* attachShm(int shmid)//让共享内存与虚拟内存建立联系
{
    void* memstart=shmat(shmid,nullptr,0);
    if((long long)memstart==-1L)
    {
        std::cerr<<"shmat"<<errno<<":"<<strerror<<std::endl;
        exit(3);
    }
    return memstart;
} 
void detchShm(void* memStart)//去关联
{
    if(shmdt(memStart)==-1)
    {
        std::cerr<<"shmdt"<<errno<<":"<<strerror<<std::endl;
        exit(4);
    }
}   
void delShm(int shmid)//删除共享内存
{
    if(shmctl(shmid,IPC_RMID,nullptr)==-1)
    {
        std::cerr<<"shmctl"<<errno<<":"<<strerror<<std::endl;
    }
}
// shm_server.cc
#include "comm.hpp"
int main()
{
    key_t k=getKey();
    printf("0X%x\n",k);
    int shmid=createShm(k);
 
    char* memStart=(char*)attachShm(shmid);//让共享内存与虚拟内存建立联系
    printf("memStart address:%p\n",memStart); 
 
    //通信接收代码
    while(true)
    {
        printf("client say:%s\n",memStart); 
        sleep(1);
 
        //调用用户级结构体
        struct shmid_ds ds;//创建结构体对象ds
        shmctl(shmid,IPC_STAT,&ds);//获取ds对象的状态
        printf("获取属性:%d,pid:%d,myself:%d,key:%d\n",ds.shm_segsz,getpid(),ds.shm_cpid,ds.shm_perm.__key);
    }
 
 
    detchShm(memStart);//去关联
    sleep(10);
 
    delShm(shmid);//删除共享内存,client和server都能删除共享内存,尽量谁创建谁删
    return 0;
}
// shm_client.cc
#include "comm.hpp"
int main()
{
    key_t k=getKey();
    printf("0X%x\n",k);
    int shmid=getShm(k);//获取共享内存
    sleep(5);
 
    char* memStart=(char*)attachShm(shmid);//让共享内存与虚拟内存建立联系
    printf("memStart address:%p\n",memStart); 
 
    //通信传输代码
    const char* massage="I am client";
    pid_t id=getpid();
    int cnt=0;//发送计数
    while(true)
    {
        snprintf(memStart,MAX_SIZE,"%s[%d]:%d\n",massage,getpid,++cnt);
        sleep(1);
    }
 
    detchShm(memStart);//去关联
    return 0;
}

🕒 5. 共享内存的优缺点

优点:共享内存是所有进程间通信中速度最快的。(无需缓冲区,能大大减少通信数据的拷贝次数)

缺点:如果服务端读取速度较快,用户端发送数据较慢,就会产生同一段消息被服务端读取多遍。共享内存是不进行同步和互斥的,没有对数据进行任何保护。

共享内存大小的建议:因为系统分配共享内存是以4KB为基本单位,一般建议申请共享内存的大小为4KB的整数倍。


OK,以上就是本期知识点“共享内存”的知识啦~~ ,感谢友友们的阅读。后续还会继续更新,欢迎持续关注哟📌~
💫如果有错误❌,欢迎批评指正呀👀~让我们一起相互进步🚀
🎉如果觉得收获满满,可以点点赞👍支持一下哟~

❗ 转载请注明出处
作者:HinsCoder
博客链接:🔎 作者博客主页

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

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

相关文章

气象站是什么设备?功能是什么?

气象站是一种用于测量和记录气象数据的设备。它通常是由各种传感器及其数据传输设备、固定设备和供电设备组成&#xff0c;可以测量风速、风向、温度、湿度、气压、降水量等气象要素&#xff0c;并将这些数据记录下来&#xff0c;以便进一步分析和研究。 气象站通常设置在广阔…

从MySQL数据库中查出时间与数据库存储的时间不一致

1、TimeZone t TimeZone.getDefault(); 通过该方法查看系统的时区。 2、select global.time_zone,session.time_zone; 查看数据库的时区 3、在application.properties中配置&#xff1a; #SpringBoot默认的是Jackson框架转换&#xff0c;而Jackson默认的时间时区是GMT&…

动作分析:提升效率、改进质量、创新流程的秘密武器

动作分析的主要的用途和目的是排除多余的动作、无效的动作或缓慢的动作&#xff0c;如弯腰、站在凳子上、蹲着、工作场所差、缺少合适的工作设备、处于搜索状态的人和事等&#xff0c;形成标准化作业规范&#xff0c;从而通过最省力的方法达到最大的工作效率。这项研究可以不花…

为什么良好的客户服务对于成功的企业至关重要

图片来源于&#xff1a;SaleSmartly官网 良好的客户服务是任何成功企业的重要组成部分。提供卓越的客户服务有助于建立客户的信任和忠诚度&#xff0c;从而增加销售额和利润。它还对任何企业的声誉产生积极影响&#xff0c;带来更多客户和更好的口碑推荐。因此&#xff0c;企业…

【FusionInsight 迁移】HBase从C50迁移到6.5.1(03)6.5.1上准备Loader

【FusionInsight 迁移】HBase从C50迁移到6.5.1&#xff08;03&#xff09;6.5.1上准备Loader HBase从C50迁移到6.5.1&#xff08;03&#xff09;6.5.1上准备Loader登录新集群FusionInsight 6.5.1的Manager准备Loader服务准备Loader Role准备Loader User HBase从C50迁移到6.5.1&…

商家为什么要做微信小程序开发

一、引言 在移动互联网时代&#xff0c;微信已经成为人们生活中不可或缺的社交工具。微信小程序作为微信的一个重要扩展&#xff0c;以其轻便、易用、无需下载等特性&#xff0c;正在受到越来越多的关注。本文将详细探讨为什么需要进行微信小程序开发&#xff0c;以及微信小程…

Nacos Helm Chart Deploy Demo

文章目录 介绍准备条件定制 values.yaml检查登陆 介绍 Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service的首字母简称&#xff0c;一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。 Nacos 致力于帮助您发现、配置和管理微服务。Nacos 提供…

华为云Stack的学习(五)

六、华为云stack服务简介 1.云服务在华为云Stack中的位置 云服务对接多个数据中心资源池层提供的资源&#xff0c;并向各种行业应用提供载体。 2.华为云Stack通用服务 2.1 云计算的服务模式 2.2 计算相关的云服务 2.3 存储相关的云服务 2.4 网络相关的云服务 3.云化案例 **…

VSCode 中的用户代码片段

用户代码块 在 VSCode 中&#xff0c;用户可以通过自定义代码片段&#xff08;User Snippets&#xff09;来快速插入常用的代码块。 官方文档 如何创建和使用用户代码片段&#xff1a; 点击用户片段后&#xff0c;即会弹出面板如下&#xff1a; 添加一个代码片段 "片…

elasticsearch wildcard 慢查询原因分析(深入到源码!!!)

大家好&#xff0c;我是蓝胖子&#xff0c;前段时间线上elasticsearch集群遇到多次wildcard产生的性能问题&#xff0c; elasticsearch wildcard 一直是容易引发elasticsearch 容易宕机的一个风险点&#xff0c; 但究竟它为何消耗cpu呢&#xff1f;又该如何理解elasticsearch p…

深圳-海岸城购物中心数据分析

做数据分析的时候&#xff0c;如果要对商场进行分析&#xff0c;可以从这些数据纬度进行分析&#xff0c;如下图所示&#xff1a; 截图来源于数位观察&#xff1a;https://www.swguancha.com/

【JPC出版】第二届能源与电力系统国际学术会议 (ICEEPS 2023)

第二届能源与电力系统国际学术会议 (ICEEPS 2023) 2023 2nd International Conference on Energy and Electrical Power Systems 第二届能源与电力系统国际学术会议 (ICEEPS 2023)将于2023年10月27日至29日在中国厦门举行。ICEEPS 将汇集能源科学、电气工程和电力系统领域的…

Windows中多线程的基础知识——2事件对象

上一节我们介绍了线程同步、以及利用互斥对象实现线程同步的方法。这一节&#xff0c;我们继续介绍另一种线程同步的方法&#xff1a;事件对象。如果对线程概念、互斥对象概念不清楚的同学&#xff0c;请查看Windows中多线程的基础知识——1互斥对象。 1 事件对象 1.1 事件对…

SAP MM BP供应商主数据学习实践总结

一 常用Tcode 基于Tcode的顺序排列 供应商主数据物料主数据货源清单配额安排采购信息记录采购订单框架协议采购询价/报价采购申请订单收货发票校验物料需求计划BP-供应商主数据MM01 - 物料主数据新增ME01 - 维护MEQ1 - 维护ME11 - 创建ME21N - 创建框架协议-合同:询价:ME51…

LeetCode 133. Clone Graph【图,DFS,BFS,哈希表】中等

本文属于「征服LeetCode」系列文章之一&#xff0c;这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁&#xff0c;本系列将至少持续到刷完所有无锁题之日为止&#xff1b;由于LeetCode还在不断地创建新题&#xff0c;本系列的终止日期可能是永远。在这一系列刷题文章…

Tequila Works x Incredibuild

关于 Tequila Works Tequila Works 是一家位于西班牙马德里的电子游戏开发商&#xff0c;由劳尔鲁比奥 (Raul Rubio) 和卢兹桑乔 (Luz Sancho) 于2009年创立。该公司著名的游戏产品包括《死亡曙光》(Deadlight)、《霜华》(Rime)、《联盟外传&#xff1a;努努之歌》(Song of Nu…

革命性的电子元件:RAD继电器 | 百能云芯

在现代电子和通信系统中&#xff0c;RAD继电器是一种关键的电子元件&#xff0c;它在各种应用中发挥着重要作用。RAD继电器&#xff08;Reed-relay Actuated Device&#xff09;是一种基于磁性原理的电子开关&#xff0c;其特点是极其高速、可靠、低功耗和长寿命。下面云芯将为…

企业架构LNMP学习笔记12

1、Server配置&#xff1a; Server虚拟主机的配置&#xff1a; 在实际生产业务环境中&#xff0c;一台web服务器&#xff0c;需要使用多个网站部署。搭建vhost虚拟主机实现不同域名&#xff0c;解析绑定到不同的目录。 #基于http的web服务 server{#监听端口listen 80#配置虚…

2023年7月京东咖啡行业数据分析(京东数据报告)

瑞幸咖啡与贵州茅台推出的联名咖啡“酱香拿铁”正式上市&#xff0c;一度掀起消费者的消费浪潮。在此之前&#xff0c;瑞幸也与多个品牌推出过联名款咖啡&#xff0c;每次的热度都颇高&#xff0c;这在背后&#xff0c;除了有消费者的猎奇心理外&#xff0c;更重要的是&#xf…

生成与调用C++动态链接库(so文件)

文章目录 前言生成C动态链接库步骤1&#xff1a;编写C源码步骤2&#xff1a;生成共享库步骤3&#xff1a;验证生成的SO文件 调用C动态链接库步骤1&#xff1a;修改原来makefile步骤2&#xff1a;编译调用程序步骤3&#xff1a;运行调用程序 总结 前言 动态链接库是代码重用和模…