目录
系统设计的只能本地进行通信;
直接原理
共享内存也是进程间通信的方案
原理图
理解:
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的指令
标签:__,include,int,理解,key,共享内存,shm From: https://blog.csdn.net/yiqizhuashuimub/article/details/142286249