当前位置: 首页 > news >正文

【Linux】System V 共享内存

文章目录

  • 1. 共享内存示意图
  • 2. 共享内存数据结构
  • 3. 共享内存函数
    • `shmget`
    • `shmat`
    • `shmdt`
    • `shmctl`
  • 4. 实例代码测试共享内存
  • 5. 共享内存相关命令
  • 6. System V 消息队列(了解)
  • 7. System V 信号量(了解)

在这里插入图片描述

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

1. 共享内存示意图

在这里插入图片描述

2. 共享内存数据结构

struct shmid_ds
{struct ipc_perm shm_perm;    /* operation perms */int shm_segsz;               /* size of segment (bytes) */__kernel_time_t shm_atime;   /* last attach time */__kernel_time_t shm_dtime;   /* last detach time */__kernel_time_t shm_ctime;   /* last change time */__kernel_ipc_pid_t shm_cpid; /* pid of creator */__kernel_ipc_pid_t shm_lpid; /* pid of last operator */unsigned short shm_nattch;   /* no. of current attaches */unsigned short shm_unused;   /* compatibility */void *shm_unused2;           /* ditto - used by DIPC */void *shm_unused3;           /* unused */
};

3. 共享内存函数

shmget

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

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,表示连接操作用来只读共享内存。

shmdt

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

shmctl

功能:用于控制共享内存
原型int shmctl(int shmid, int cmd, struct shmid_ds* buf);
参数shmid:由 shmget 返回的共享内存标识码cmd:将要采取的动作(有三个可取值)buf:指向一个保存着共享内存的模式状态和访问权限的数据结构
返回值:成功返回0;失败返回-1

在这里插入图片描述

4. 实例代码测试共享内存

  • 使用共享内存通信,一定是一个进程创建新的 shm,另一个直接获取共享内存即可。

代码结构

$ ls
Comm.hpp  Fifo.hpp  Makefile  ShmClient.cc  ShmServer.cc$ cat Makefile 
.PHONY:all
all:shm_client shm_servershm_server:ShmServer.ccg++ -o $@ $^ -std=c++11
shm_client:ShmClient.ccg++ -o $@ $^ -std=c++11.PHONY:clean
clean:rm -f shm_client shm_server

Fifo.hpp

#ifndef __COMM_HPP__
#define __COMM_HPP__#include <iostream>
#include <string>
#include <cerrno>
#include <cstring>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <cassert>using namespace std;#define Mode 0666
#define Path "./fifo"class Fifo
{
public:Fifo(const string &path = Path): _path(path){umask(0);int n = mkfifo(_path.c_str(), Mode);if (n == 0){cout << "mkfifo success" << endl;}else{cerr << "mkfifo failed, errno: " << errno << ", errstring: " << strerror(errno) << endl;}}~Fifo(){int n = unlink(_path.c_str());if (n == 0){cout << "remove fifo file " << _path << " success" << endl;}else{cerr << "remove failed, errno: " << errno << ", errstring: " << strerror(errno) << endl;}}private:string _path; // 文件路径 + 文件名
};class Sync
{
public:Sync(): rfd(-1), wfd(-1){}void OpenReadOrDie(){rfd = open(Path, O_RDONLY);if (rfd < 0)exit(1);}void OpenWriteOrDie(){wfd = open(Path, O_WRONLY);if (wfd < 0)exit(1);}bool Wait(){bool ret = true;uint32_t c = 0;ssize_t n = read(rfd, &c, sizeof(uint32_t));if (n == sizeof(uint32_t)){std::cout << "server wakeup, begin read shm..." << std::endl;}else if (n == 0){ret = false;}else{return false;}return ret;}void Wakeup(){uint32_t c = 0;ssize_t n = write(wfd, &c, sizeof(c));assert(n == sizeof(uint32_t));std::cout << "wakeup server..." << std::endl;}~Sync(){}private:int rfd;int wfd;
};#endif

Comm.hpp:

#pragma once#include <iostream>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <string>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>using namespace std;const char *pathname = "/home/ubuntu";
const int proj_id = 0x66;// 在内核中,共享内存的大小是以4KB为基本单位的,你只能用你申请的大小。建议申请大小是N*4KB
const int defaultsize = 4096; // 单位是字节std::string ToHex(key_t k)
{char buffer[1024];snprintf(buffer, sizeof(buffer), "0x%x", k);return buffer;
}key_t GetShmKeyOrDie()
{key_t k = ftok(pathname, proj_id);if (k < 0){std::cerr << "ftok error, errno: " << errno << ", error string: " << strerror(errno) << std::endl;exit(1);}return k;
}int CreateShmOrDie(key_t key, int size, int flag)
{int shmid = shmget(key, size, flag);if (shmid < 0){std::cerr << "shmget error, errno: " << errno << ", error string: " << strerror(errno) << std::endl;exit(2);}return shmid;
}int CreateShm(key_t key, int size)
{// IPC_CREAT: 不存在就创建,存在就获取// IPC_EXCL: 没有意义// IPC_CREAT | IPC_EXCL: 不存在就创建,存在就出错返回return CreateShmOrDie(key, size, IPC_CREAT | 0666);
}int GetShm(key_t key, int size)
{return CreateShmOrDie(key, size, IPC_CREAT);
}void DeleteShm(int shmid)
{int n = shmctl(shmid, IPC_RMID, nullptr);if (n < 0){std::cerr << "shmctl error" << std::endl;}else{std::cout << "shmctl delete shm success, shmid: " << shmid << std::endl;}
}void ShmDebug(int shmid)
{struct shmid_ds shmds;int n = shmctl(shmid, IPC_STAT, &shmds);if (n < 0){std::cerr << "shmctl error" << std::endl;return;}std::cout << "shmds.shm_segez: " << shmds.shm_segsz << std::endl;std::cout << "shmds.shm_nattch: " << shmds.shm_nattch << std::endl;std::cout << "shmds.shm_ctime: " << shmds.shm_ctime << std::endl;std::cout << "shmds.shm_perm.__key: " << ToHex(shmds.shm_perm.__key) << std::endl;
}void *ShmAttach(int shmid)
{// 核心自动选择一个地址void *addr = shmat(shmid, nullptr, 0);if ((long long int)addr == -1){std::cerr << "shmat error" << std::endl;return nullptr;}return addr;
}void ShmDetach(void *addr)
{int n = shmdt(addr);if (n < 0){std::cerr << "shmdt error" << std::endl;}
}

ShmServer:

#include "Comm.hpp"
#include "Fifo.hpp"int main()
{// 1. 获取keykey_t key = GetShmKeyOrDie();std::cout << "key: " << ToHex(key) << std::endl;// 2. 创建共享内存int shmid = CreateShm(key, defaultsize);std::cout << "shmid: " << shmid << std::endl;// 4. 将共享内存和进程进行挂接(关联)char *addr = (char *)ShmAttach(shmid);std::cout << "Attach shm success, addr: " << ToHex((uint64_t)addr) << std::endl;// 0. 先引入管道Fifo fifo;Sync syn;syn.OpenReadOrDie();// 可以进行通信了for (;;){if (!syn.Wait())break;std::cout << "shm content: " << addr << std::endl;}ShmDetach(addr);std::cout << "Detach shm success, addr: " << ToHex((uint64_t)addr) << std::endl;// 3. 删除共享内存DeleteShm(shmid);return 0;
}

ShmClient:

#include "Comm.hpp"
#include "Fifo.hpp"int main()
{key_t key = GetShmKeyOrDie();std::cout << "key: " << ToHex(key) << std::endl;int shmid = GetShm(key, defaultsize);std::cout << "shmid: " << shmid << std::endl;char *addr = (char *)ShmAttach(shmid);std::cout << "Attach shm success, addr: " << ToHex((uint64_t)addr) << std::endl;memset(addr, 0, defaultsize);Sync syn;syn.OpenWriteOrDie();// 可以进行通信了for (char c = 'A'; c <= 'Z'; c++) // shm没有使用系统调用{addr[c - 'A'] = c;syn.Wakeup();sleep(1);}ShmDetach(addr);std::cout << "Detach shm success, addr: " << ToHex((uint64_t)addr) << std::endl;sleep(5);return 0;
}

结果演示:

在这里插入图片描述

在这里插入图片描述

5. 共享内存相关命令

  • 共享内存,如果进程结束,我们没有主动释放它,则共享内存一直存在。
  • 共享内存的生命周期是随内核的,只有重启系统它才会自行销毁。
  • 为了更好地控制共享内存,我们要会使用命令删除它。
  • 不管是指令还是代码,想对共享内存进行控制,都需要使用 shmid ,shmid 是共享内存的唯一性标识!
# 查看共享内存
ipcs -m# 删除指定共享内存
ipcrm -m [shmid]
# 实机演示
$ ipcs -m------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status      
0x00000000 0          root       644        80         2                       
0x00000000 1          root       644        16384      2                       
0x00000000 2          root       644        280        2                       
0x6602fc97 10         ubuntu     666        4096       1$ ipcrm -m 10	# 指定shmid即可删除该共享内存资源

注意:不是必须通过手动来删除,这里只是为了演示相关指令,删除共享内存资源是进程该做的事情。

注意:共享内存没有进程同步与互斥!

6. System V 消息队列(了解)

  • 消息队列提供了从一个进程向另外一个进程发送一块数据的方法。
  • 每个数据块都被认为是有一个类型,接收者进程接收的数据块可以有不同的类型值。
  • IPC 资源必须删除,否则不会自动清除,除非重启,所以 System V IPC 资源的生命周期随内核。

7. System V 信号量(了解)

  • 信号量主要用于同步和互斥。
  • 由于各进程要求共享资源,而且有些资源需要互斥使用,因此各进程间竞争使用这些资源,进程的这种关系为进程的互斥。
  • 系统中某些资源一次只允许一个进程使用,称这样的资源为临界资源或互斥资源。
  • 在进程中涉及到互斥资源的程序段叫临界区。

END

相关文章:

【Linux】System V 共享内存

文章目录 1. 共享内存示意图2. 共享内存数据结构3. 共享内存函数shmgetshmatshmdtshmctl 4. 实例代码测试共享内存5. 共享内存相关命令6. System V 消息队列&#xff08;了解&#xff09;7. System V 信号量&#xff08;了解&#xff09; 共享内存区是最快的 IPC 形式。一旦这样…...

拼多多标准推广怎么玩

拼多多标准推广的玩法主要包括以下方面&#xff1a; 拼多多推广可以使用3an推客。3an推客&#xff08;CPS模式&#xff09;给商家提供的营销工具&#xff0c;由商家自主设置佣金比例&#xff0c;激励推广者去帮助商家推广商品链接&#xff0c;按最终有效交易金额支付佣金&…...

HFSS学习-day2-T形波导的优化设计

入门实例–T形波导的内场分析和优化设计 HFSS--此实例优化设计 优化设计要求1. 定义输出变量Power31、Power21、和Power11&#xff0c;表示Port3、Port2、Port1的输出功率2.参数扫描分析添加扫描变量和输出变量进行一个小设置添加输出变量进行扫描分析 3. 优化设计&#xff0c…...

贪吃蛇小游戏(c语言)

1.效果展示 屏幕录制 2024-04-28 205129 2.基本功能 • 贪吃蛇地图绘制 • 蛇吃食物的功能 &#xff08;上、下、左、右方键控制蛇的动作&#xff09; • 蛇撞墙死亡 • 蛇撞自身死亡 • 计算得分 • 蛇身加速、减速 • 暂停游戏 3.技术要点 C语言函数、枚举、结构…...

多商户Docker Supervisor进程管理器部署

Dockerfile 根目录下没有Dockerfile的可以复制下面的命令 # 使用基础镜像 FROM leekay0218/crmeb-mer## 复制代码 ## 在本地调试注释掉&#xff0c;使用映射把文件映射进去 #ADD ./ /var/www# 设置工作目录 WORKDIR /var/www# 设置时区为上海 ENV TZAsia/Shanghai RUN ln -sn…...

Vue--》从零开始打造交互体验一流的电商平台(一)

今天开始使用 vue3 ts 搭建一个电商项目平台&#xff0c;因为文章会将项目的每处代码的书写都会讲解到&#xff0c;所以本项目会分成好几篇文章进行讲解&#xff0c;我会在最后一篇文章中会将项目代码开源到我的github上&#xff0c;大家可以自行去进行下载运行&#xff0c;希…...

uniapp 自定义相机插件(组件版、缩放、裁剪)组件 Ba-CameraView

自定义相机插件&#xff08;组件版、缩放、裁剪&#xff09; Ba-CameraView 简介&#xff08;下载地址&#xff09; Ba-CameraView 是一款自定义相机拍照组件&#xff0c;支持任意界面&#xff0c;支持裁剪 支持任意自定义界面支持手势缩放支持裁剪&#xff08;手势拖动、比…...

如何在Python中生成随机密码?

你可以使用Python的random模块来生成随机密码。下面是一个生成随机密码的简单示例代码&#xff1a; import random import stringdef generate_random_password(length):characters string.ascii_letters string.digits string.punctuationpassword .join(random.choice(c…...

【Git管理工具】使用Docker+浪浪云服务器部署GitLab服务器

一、什么是GitLab 1.1.GitLab简介 GitLab 是一个开源的 DevOps 平台&#xff0c;它基于 Git 版本控制系统提供了从项目规划、源代码管理到持续集成、持续部署、监控和安全的完整生命周期管理。GitLab 是一个为开发者提供协作工作的工具&#xff0c;它使得团队能够高效地在同一…...

速盾:什么是cdn架构

CDN&#xff08;Content Delivery Network&#xff09;即内容分发网络&#xff0c;是一种分布式的架构&#xff0c;用于提高互联网上的内容传输速度和用户体验。CDN架构通过将内容分发到全球多个节点&#xff0c;使用户能够从最近的节点获取内容&#xff0c;从而减少延迟和网络…...

spring高级篇(十)

1、内嵌tomcat boot框架是默认内嵌tomcat的&#xff0c;不需要手动安装和配置外部的 Servlet 容器。 简单的介绍一下tomcat服务器的构成&#xff1a; Catalina&#xff1a; Catalina 是 Tomcat 的核心组件&#xff0c;负责处理 HTTP 请求、响应以及管理 Servlet 生命周期。它包…...

map、set底层封装模拟实现(红黑树)

文章目录 一、红黑树1.1红黑树的规则&#xff1a;1.2红黑树的插入操作1.2.1不需要旋转&#xff08;如果叔叔存在且为红,这里的C表示孩子&#xff0c;P表示父亲&#xff0c;U表示叔叔&#xff0c;G表示祖父&#xff09;&#xff0c;包含四种情况&#xff0c;无论孩子在哪里&…...

PHP8.2-xlswriter 扩展

https://pecl.php.net/package/xlswriter ### 进入/root/ cd ~ ### 下载扩展 wget https://pecl.php.net/get/xlswriter-1.5.5.tgz ### 解压扩展 tar -zxvf xlswriter-1.5.5.tgz ### 进入扩展目录 cd xlswriter-1.5.5 ### 查找对应php版本的phpize find / -name phpi…...

imx6ull开发板设置SD卡启动,SD卡中烧写uboot,kernel,设备树,根文件系统fs

IMX6ULL ARM Linux开发板SD卡启动&#xff0c;SD卡的分区与分区格式化创建_sd制作分区-CSDN博客...

2024年第七届可再生能源与环境工程国际会议(REEE 2024)即将召开!

2024年第七届可再生能源与环境工程国际会议&#xff08;REEE 2024&#xff09;将于2024 年8月28-30日在法国南特举行。共绘绿色未来&#xff0c;全球同频共振&#xff01;REEE 2024将汇聚全球可再生能源与环境工程领域的专家学者和业界精英&#xff0c;共同探讨行业发展的前沿技…...

【华为】NAT的分类和实验配置

【华为】NAT的分类和实验配置 NAT产生的技术背景IP地址分类NAT技术原理NAT分类静态NAT动态NATNAPTEasy IP&#xff08;PAT&#xff09;NAT Server 配置拓扑静态NAT测试抓包 动态NAT测试抓包 NAPT测试抓包 PAT测试抓包 NAT Server检测抓包 PC1PC2服务器 NAT产生的技术背景 随着…...

拉普拉斯丨独家冠名2024年度ATPV技术分论坛,助力产业科技持续创新

为了进一步促进行业技术交流&#xff0c;推进光伏行业发展及标准建设的进程&#xff0c;针对高效电池&#xff0c;领跑组件&#xff0c;新产品认证及应用等技术专题及国内外光伏标准的最新进程&#xff0c;由中国绿色供应链联盟光伏专委会&#xff08;ECOPV&#xff09;指导的2…...

LangChain入门教程 - 使用代理Agent

对于大模型&#xff0c;比如某些场景&#xff0c;需要数学计算&#xff0c;或者需要从某些网站获取参考资料&#xff0c;就必须使用专门的代理来完成任务。这里我们使用langchain提供的数学工具来实现一个最简单的例子&#xff0c;下一篇我们会讲如何自己实现代理。 首先创建一…...

windows驱动开发-内核编程技术汇总(五)

使用安全字符串函数 和应用层不一样的是&#xff0c;windows内核完全使用Unicode字符串&#xff0c;许多支持AsciiC的windowsAPI&#xff0c;是在应用层完成项Unicode的切换的。许多系统安全问题是由缓冲区处理不善和生成的缓冲区溢出引起的。 糟糕的缓冲区处理通常与字符串操…...

Java中的optional类是啥和例子

Optional 是 Java 8 引入的一个容器对象&#xff0c;用于表示值存在或不存在。这是一个可以为 null 的容器对象&#xff0c;但使用 Optional 比直接使用 null 更安全&#xff0c;因为 Optional 类提供了许多有用的方法&#xff0c;以便更优雅地处理可能存在或不存在的值。 使用…...

React 第五十五节 Router 中 useAsyncError的使用详解

前言 useAsyncError 是 React Router v6.4 引入的一个钩子&#xff0c;用于处理异步操作&#xff08;如数据加载&#xff09;中的错误。下面我将详细解释其用途并提供代码示例。 一、useAsyncError 用途 处理异步错误&#xff1a;捕获在 loader 或 action 中发生的异步错误替…...

Qt Widget类解析与代码注释

#include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this); }Widget::~Widget() {delete ui; }//解释这串代码&#xff0c;写上注释 当然可以&#xff01;这段代码是 Qt …...

(二)原型模式

原型的功能是将一个已经存在的对象作为源目标,其余对象都是通过这个源目标创建。发挥复制的作用就是原型模式的核心思想。 一、源型模式的定义 原型模式是指第二次创建对象可以通过复制已经存在的原型对象来实现,忽略对象创建过程中的其它细节。 📌 核心特点: 避免重复初…...

uniapp手机号一键登录保姆级教程(包含前端和后端)

目录 前置条件创建uniapp项目并关联uniClound云空间开启一键登录模块并开通一键登录服务编写云函数并上传部署获取手机号流程(第一种) 前端直接调用云函数获取手机号&#xff08;第三种&#xff09;后台调用云函数获取手机号 错误码常见问题 前置条件 手机安装有sim卡手机开启…...

【Android】Android 开发 ADB 常用指令

查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...

android RelativeLayout布局

<?xml version"1.0" encoding"utf-8"?> <RelativeLayout xmlns:android"http://schemas.android.com/apk/res/android"android:layout_width"match_parent"android:layout_height"match_parent"android:gravity&…...

macOS 终端智能代理检测

&#x1f9e0; 终端智能代理检测&#xff1a;自动判断是否需要设置代理访问 GitHub 在开发中&#xff0c;使用 GitHub 是非常常见的需求。但有时候我们会发现某些命令失败、插件无法更新&#xff0c;例如&#xff1a; fatal: unable to access https://github.com/ohmyzsh/oh…...

[USACO23FEB] Bakery S

题目描述 Bessie 开了一家面包店! 在她的面包店里&#xff0c;Bessie 有一个烤箱&#xff0c;可以在 t C t_C tC​ 的时间内生产一块饼干或在 t M t_M tM​ 单位时间内生产一块松糕。 ( 1 ≤ t C , t M ≤ 10 9 ) (1 \le t_C,t_M \le 10^9) (1≤tC​,tM​≤109)。由于空间…...

[特殊字符] 手撸 Redis 互斥锁那些坑

&#x1f4d6; 手撸 Redis 互斥锁那些坑 最近搞业务遇到高并发下同一个 key 的互斥操作&#xff0c;想实现分布式环境下的互斥锁。于是私下顺手手撸了个基于 Redis 的简单互斥锁&#xff0c;也顺便跟 Redisson 的 RLock 机制对比了下&#xff0c;记录一波&#xff0c;别踩我踩过…...

用递归算法解锁「子集」问题 —— LeetCode 78题解析

文章目录 一、题目介绍二、递归思路详解&#xff1a;从决策树开始理解三、解法一&#xff1a;二叉决策树 DFS四、解法二&#xff1a;组合式回溯写法&#xff08;推荐&#xff09;五、解法对比 递归算法是编程中一种非常强大且常见的思想&#xff0c;它能够优雅地解决很多复杂的…...