共享内存的理解
目录
直接原理
原理图编辑
直接代码
创建唯一键值代码
创建共享内存代码
共享内存的指令操作
简单封装
补充指令集
系统设计的只能本地进行通信;
直接原理
共享内存也是进程间通信的方案
原理图
理解:
1. 所有图中所说的操作都是os所做的
2. os必须提供1和2步骤的系统调用,供进程A和B进行调用
3. 共享内存在os中可以同时存在多份(因为不止A和B进程,可能还有E和F,G和J等),供不同对进程同时进行通信!
4. os注定了要对共享内存进行管理!------先描述后组织--------决定了共享内存不是简单的一段内存空间,也要有描述并管理共享内存的数据结构和匹配的算法!
5. 共享内存=内存空间(数据)+共享内存的操作
直接代码
这个shmget完成的是1步骤,并且是系统调用;
ftok 是一个用于生成唯一的系统V IPC (Inter-Process Communication) 标识符的函数,用于根据文件路径和一个项目标识符生成一个唯一的键值;pathname: 指向一个文件路径的字符串。ftok 将使用这个文件的 inode 信息来生成键值;proj_id: 一个项目标识符,通常是一个字符,用于在同一文件路径下区分不同的键值,通常是单字符的整数,范围从 0 到 255;
创建唯一键值代码
shm.hpp
//shm是共享内存的缩写 #ifndef __SHM_HPP__ //检查 __SHM_HPP__ 这个宏是否未定义。如果未定义,则执行接下来的代码,直到遇到 #endif。 #define __SHM_HPP__ //定义 __SHM_HPP__ 宏。这样,如果这个头文件再次被包含,#ifndef 检查将失败,文件内容不会被再次处理,从而避免了重复定义的问题。#include<iostream> #include<string> #include <sys/ipc.h> #include <sys/shm.h>const std::string pathname="/root/pipe/4.shm";//绝对路径 const int proj_id=0x66;//项目标识符key_t getcommkey(const std::string & pathname, int proj_id){key_t k=ftok(pathname.c_str(),proj_id); //生成键值if(k<0){perror("ftok");}return k; }#endif //结束 #ifndef 的条件编译指令。
client.cc
#include"shm.hpp" int main(){key_t key=getcommkey(pathname,proj_id);std::cout<<"key: "<<key<<std::endl;return 0; }
serve.cc
#include"shm.hpp" int main(){key_t key=getcommkey(pathname,proj_id);std::cout<<"key: "<<key<<std::endl;return 0; }
创建共享内存代码
shm.hpp
//shm是共享内存的缩写 #ifndef __SHM_HPP__ //检查 __SHM_HPP__ 这个宏是否未定义。如果未定义,则执行接下来的代码,直到遇到 #endif。 #define __SHM_HPP__ //定义 __SHM_HPP__ 宏。这样,如果这个头文件再次被包含,#ifndef 检查将失败,文件内容不会被再次处理,从而避免了重复定义的问题。#include<iostream> #include<string> #include <sys/ipc.h> #include <sys/shm.h>const std::string pathname="/root/pipe/4.shm";//绝对路径 const int proj_id=0x66;//项目标识符key_t getcommkey(const std::string & pathname, int proj_id){//创建唯一键值key_t k=ftok(pathname.c_str(),proj_id); //生成键值if(k<0){//失败perror("ftok");}return k; }int shmgget(key_t key,int size){//创建共享内存int shmid=shmget(key,size,IPC_CREAT | IPC_EXCL);//返回值是共享内存的标识符if(shmid<0){//失败perror("shmget");}return shmid; }#endif //结束 #ifndef 的条件编译指令。
silent.cc
#include"shm.hpp" int main(){key_t key=getcommkey(pathname,proj_id);std::cout<<"key: "<<key<<std::endl;int shmid=shmgget(key,4096);std::cout<<"shmid: "<<shmid<<std::endl;return 0; }
为什么第二次就失败了呢?因为文件已存在,因为我创建时用的是IPC_CREAT | IPC_EXCL(不存在就创建,存在就出错返回)第二次调用原本就有了所以第二次出错返回;
而且共享内存不随着进程的释放而释放,所以就第二次报错;他会一直存在,直到系统重启,所以需要手动释放(指令或者其他系统调用)。
他的生命周期随内核,文件的生命周期随进程;
共享内存的指令操作
ipcs -m 命令用于显示系统中的共享内存段信息。它会列出所有当前存在的共享内存段的详细信息,比如标识符、大小、创建时间等。这个命令有助于你了解系统内存的使用情况。
删除的话只能用shmid删除: ipcrm -m 是一个用于删除共享内存段的命令
可以理解为key是内核用的,shmid是用户用的;注意:
在删除共享内存段之前,请确保没有任何进程正在使用它。
删除共享内存段会释放该内存区域,并且该共享内存段中的数据将会丢失。
删除之后再运行就能跑,细节是shmid变化了,key没变
简单封装
shm.hpp
// shm是共享内存的缩写
#ifndef __SHM_HPP__
// 检查 __SHM_HPP__ 这个宏是否未定义。如果未定义,则执行接下来的代码,直到遇到 #endif。
#define __SHM_HPP__
// 定义 __SHM_HPP__ 宏。这样,如果这个头文件再次被包含,#ifndef 检查将失败,文件内容不会被再次处理,从而避免了重复定义的问题。#include <iostream>
#include <string>
#include <sys/ipc.h>
#include <sys/shm.h>const int creater = 1;
const int user = 2;
const std::string gpathname = "/root/pipe/4.shm"; // 绝对路径
const int gproj_id = 0x66; // 项目标识符class shm
{
private: // 不用暴露给用户key_t getcommkey(){ // 创建唯一键值key_t k = ftok(_pathname.c_str(), _proj_id); // 生成键值if (k < 0){ // 失败perror("ftok");}return k;}int getcomm(key_t key, int size, int flag) // 创建共享内存的共同方法{int shmid = shmget(key, size, flag); // 返回值是共享内存的标识符if (shmid < 0){ // 失败perror("shmget");}return shmid;}public:shm(const std::string &pathname, int proj_id, int who) // 顺便告诉是谁: _pathname(pathname), _proj_id(proj_id), _who(who){_key = getcommkey(); // 键值初始化if (_who == creater)getshmforcreat();else if (_who == user)getshmforuse();std::cout << "key: " << tohex(_key) << std::endl; // 16进制打印std::cout << "shmid: " << _shmid << std::endl;}~shm(){if (_who == creater){ // 只有创建者才能删除int res = shmctl(_shmid, IPC_RMID, nullptr); // 进行删除操作通过调用系统接口}std::cout << "shm remove done " << std::endl;}std::string tohex(key_t key){ // 转化成16进制char buffer[128];snprintf(buffer, sizeof(buffer), "0x%x", key);return buffer;}bool getshmforcreat(){ // 创建者if (_who == creater){_shmid = getcomm(_key, 4096, IPC_CREAT | IPC_EXCL); // 调用if (_shmid >= 0){std::cout << "shm create done " << std::endl;return true; // 成功}}return false;}bool getshmforuse(){ // 使用者if (_who == user){_shmid = getcomm(_key, 4096, IPC_CREAT); // user的话只用IPC_CREAT即可if (_shmid >= 0){std::cout << "shm get done " << std::endl;return true; // 成功}}return false;}private:key_t _key;int _shmid;int _proj_id;std::string _pathname;int _who;
};#endif
// 结束 #ifndef 的条件编译指令。
serve.cc
#include"shm.hpp"int main(){shm sshm(gpathname,gproj_id,creater);return 0;
}
client.cc
#include"shm.hpp"int main(){shm sshm(gpathname,gproj_id,user);return 0;
}
我先执行serve后执行client,导致执行client时又创建了shm,因为user是IPC_CREAT,而且打印的"shm remove done "没有删除因为他不是creater;而serve运行后会创建又删除;
如果太快的话可以用sleep()在创建的语句停顿一下;
shmctl 是一个用于控制共享内存段的系统调用。
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
参数:
shmid:共享内存段的标识符。
cmd:操作命令,决定要执行的具体操作。
buf:指向 shmid_ds 结构的指针,用于存储共享内存段的信息或设置参数。
常用命令 (cmd):
IPC_STAT:获取共享内存段的状态。
IPC_SET:设置共享内存段的属性。
IPC_RMID:删除共享内存段。
补充对共享内存理论的理解
共享内存的接口
补充指令集
IPC的指令