【共享内存】

news/2024/11/8 14:46:40/

1 共享内存示意图

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

 


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。

 shmflg一般常用的权限位有两个:IPC_CREAT and IPC_EXCL

  • 单独使用IPC_CREAT: 创建一个共享内存,如果共享内存不存在,就创建之,如果已经存在,获取已经存在的共享内存并返回;
  • IPC_EXCL不能单独使用,一般都要配合IPC_CREAT;
  • IPC_CREAT | IPC_EXCL: 创建一个共享内存,如果共享内存不存在,就创建之, 如果已经存在,则立马出错返回 -- 如果创建成功,对应的shm,一定是最新的.

 我们如何获得该函数的第一个参数key呢?

我们可以使用ftok函数:

用返回值接受到生成的key_t类型即可。

  • shmat函数:
功能:将共享内存段连接到进程地址空间
原型 :
void *shmat(int shmid, const void *shmaddr, int shmflg);
参数 :
shmid: 共享内存标识
shmaddr: 指定连接的地址 ,不指定就设置位空;
shmflg: 它的两个可能取值是 SHM_RND SHM_RDONLY ,也可以设置为0;
返回值:成功返回一个指针,指向共享内存第一个节;失败返回 -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 用共享内存实现一个客户端与服务端通信的案例

comm.hpp:

#include<iostream>
#include<cerrno>
#include<cstring>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<sys/stat.h>
#include<unistd.h>
using namespace std;#define PATHNAME "."
#define PROJID 6666
key_t getKey()
{key_t key=ftok(PATHNAME,PROJID);if(key==-1){cout<<"error"<<errno<<":"<<strerror(errno)<<endl;return 1;}return key;
}int creatShm(size_t size,int shmflag=0)
{umask(0);int shmid=shmget(getKey(),size,IPC_CREAT | IPC_EXCL | 0666);if(shmid==-1){cout<<"error"<<errno<<":"<<strerror(errno)<<endl;return 1;}return shmid;
}int getShm(size_t size,int shmflag=0)
{int shmid=shmget(getKey(),size,IPC_CREAT );if(shmid==-1){cout<<"error"<<errno<<":"<<strerror(errno)<<endl;return 1;}return shmid;
}char* attachShm(int shmid)
{char* start=(char*)shmat(shmid,nullptr,0);return start;
}void detouchShm(char* start)
{int n=shmdt(start);if(n==-1){cout<<"error"<<errno<<":"<<strerror(errno)<<endl;return ;}
}void delShm(int shmid)
{int n=shmctl(shmid,IPC_RMID,nullptr);if(n==-1){cout<<"error"<<errno<<":"<<strerror(errno)<<endl;return ;}
}

server.cc:

#include<iostream>
#include"comm.hpp"int main()
{//1 创建共享内存int shmid=creatShm(4096);//2 将共享内存与自己关联起来char* start=attachShm(shmid);//3 使用共享内存通信int cnt=0;while(cnt<=15){cout<<start<<endl;sleep(1);++cnt;}//4 解除自己与共享内存的关联detouchShm(start);//5 删除共享内存delShm(shmid);return 0;
}

client.cc:

#include<iostream>
#include"comm.hpp"int main()
{//1 创建共享内存int shmid=getShm(4096);//2 将共享内存与自己关联起来char* start=attachShm(shmid);//3 使用共享内存通信char ch='A';while(ch<='Z'){start[ch-'A']=ch;++ch;start[ch-'A']=0;sleep(1);}    //4 解除自己与共享内存的关联detouchShm(start);//客户端不要删掉共享内存return 0;
}

这样我们便能够正常运行我们的程序了,但是假如我们不小心终止了./server进程而导致共享内存没有被删除,我们可以用命令行来删除共享内存。

查看共享内存命令:

ipcs -m

删除指定共享内存:

ipcrm -m 共享内存的id

共享内存是没有互斥同步机制的,也就是说你在写数据的时候我能够直接读取数据,这个跟管道有很大区别,管道是你写完了后我再来读取数据,管道是需要互斥同步机制的,这个我们放到后面多线程再来补充。


http://www.ppmy.cn/news/82211.html

相关文章

WalMiner插件之xlog解析恢复使用教程

WalMiner插件主要有两个功能&#xff0c;在此记录一下第二个功能数据页挽回&#xff08;坏块修复&#xff09;&#xff0c;学习一下关于这块的使用方法&#xff0c;方便日后回顾。 1 环境搭建 创建WalMiner的extension create extension walminer;语句解析: 该句SQL功能是安…

「教程」微信小程序获取地理位置信息自动查询天气预报信息(附详细代码)

引言 天气预报是我们日常生活中经常关注的信息之一。通过结合微信小程序的地理位置获取和天气预报查询 API&#xff0c;我们可以轻松实现自动获取当前位置的天气信息。 本文将详细介绍如何利用微信小程序获取地理位置和经纬度&#xff0c;并通过天气预报查询 API 获取实时天气…

BPMN2.0 -条件序列流和默认序列流

序列流是流程中两个元素或者活动的连接器。在流程执行过程中访问一个元素之后,将继续执行素有的序列流,默认是并行的。传出的序列流将创建独立的并行执行路径。 顺序流需要有流程唯一的id,并引用存在的源与目标元素。 <sequenceFlow id="flow1" sourceRef=&qu…

Redis SETEX命令解密:掌握过期时间计算,轻松管理键值对

Redis是一种开源的内存数据存储系统&#xff0c;它支持多种数据结构&#xff0c;包括字符串、哈希表、列表、集合、有序集合等。Redis提供了丰富的命令集&#xff0c;其中之一是SETEX命令。SETEX命令用于设置具有过期时间的键值对&#xff0c;让我们详细介绍一下SETEX命令。 1…

Unity基础 异步加载场景

异步加载场景的基本概念 在Unity中&#xff0c;异步加载场景是指在游戏运行时&#xff0c;将场景中的资源分批次加载到内存中&#xff0c;以便提高游戏的加载速度和性能。通常情况下&#xff0c;加载场景的过程会在主线程中执行&#xff0c;而异步加载场景可以在后台线程中执行…

首站中科院!百度商业AI技术创新大赛开启巡回宣讲

近日&#xff0c;百度商业AI技术创新大赛正式启动&#xff0c;并于5月18日起开启高校巡回宣讲。 宣讲会首站落地中国科学院大学&#xff0c;中国科学院大学人工智能学院副院长、教授、博士生导师肖俊教授&#xff0c;百度商业研发主任架构师焦学武&#xff0c;百度商业资深工程…

redo log

redo log 属于Innodb存储引擎的 大小是固定的&#xff0c;循环写&#xff0c;MySQL启动的时候&#xff0c;就申请了一段连续的内存空间。可以类比成一个环形。 所以&#xff0c;redo log 保存的不是全量的日志&#xff0c;所以不小心整个数据库的数据被删除了&#xff0c;不能使…

009、实例连接访问控制

实例连接访问控制 1、实例连接访问控制概述2、pg_hba.conf文件3、名单格式4、pg_hba.conf 示例:5、当有重复或者冲突的时候1、实例连接访问控制概述 • 实例访问控制就像是一道防火墙,用它来控制来自于不同主机、不同用户是否 允许访问指定的数据库、以及验证方式。 2、pg…