共享内存
注意:急着直接使用的直接到最后一个地方复制我封装的类,简单看一下前面的说明,直接用就行了
开始正文吧
首先用我自己的理解介绍共享内存是什么:共享内存就是在当前进程上 连接或开辟 一块内存空间。连接表示的意思是共享空间已经存在,被其它进程开辟过了,两个或多个进程都可以访问共享空间达到了进程间通信的目的
我们在linux下可以使用:
ipcs -m 查看当前系统的共享内存
ipcrm -m shmid(ipcs -m 查看) 删除共享内存
创建共享内存
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);
参数:
key =》最好写成16进制(0x …),取IPC_PRIVATE(也是0)表示新开只属于本进程的一块共享内存
size =》 想要的共享内存大小,单位字节
shmflg =》 此处设置权限和操作命令,权限建议0640,操作指令见下
IPC_CREAT 对照 key值, 如果共享内存不存在,则创建一个共享内存,否则打开操作。
IPC_EXCL 只有在共享内存不存在的时候,新的共享内存才建立,否则错误。
返回:成功返回 shmid(共享内存标志符) 失败 -1
示例:
int shmid = shmget((key_t)0x5005, 1024, 0640|IPC_CREAT);
0640 表示是八进制数,这是内存权限
共享内存连接到当前进程的地址空间
#include <sys/types.h>
#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr, int shmflg);
参数:
shm_id =》 shmget函数返回的共享内存标识。
shm_addr指定共享内存连接到当前进程中的地址位置,通常为0,表示让系统来选择共享内存的地址。
shm_flg是一组标志位,通常为0。
返回:成功时返回一个指向共享内存第一个字节的指针(注意是void指针) 失败返回-1
示例
char *ptext = (char *)shmat(shmid, 0, 0);
将共享内存从当前进程中分离
#include <sys/types.h>
#include <sys/shm.h>
int shmdt(const void *shmaddr);
参数:shmaddr =》 shmat函数返回的地址。
返回:成功返回0,失败返回-1。
示例
char *ptext = (char *)shmat(shmid, 0, 0); // 连接共享内存
shmdt(ptext); // 分离共享内存
删除共享内存
#include <sys/ipc.h>
#include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
参数:
shm_id =》shmget函数返回的共享内存标识符。
cmd 填IPC_RMID。
buf 填0即可
返回:成功 0, 失败 -1
此函数不只有删除功能
参数 cmd 选择功能:
IPC_STAT:得到共享内存的状态,把共享内存的shmid_ds结构复制到 buf 中 结构体见下 (注意buf别填0了)
IPC_SET:改变共享内存的状态,把buf所指的shmid_ds结构中的uid、gid、mode复制到共享内存的shmid_ds结构内 (注意buf别填0了)
IPC_RMID:删除这片共享内存 (因为buf没用,可以填0)
/* Data structure describing a shared memory segment. */
struct shmid_ds
{
struct ipc_perm shm_perm; /* operation permission struct */
#if !__SHM_SEGSZ_AFTER_TIME
size_t shm_segsz; /* size of segment in bytes */
#endif
__SHM_PAD_TIME (shm_atime, 1); /* time of last shmat() */
__SHM_PAD_TIME (shm_dtime, 2); /* time of last shmdt() */
__SHM_PAD_TIME (shm_ctime, 3); /* time of last change by shmctl() */
#if __SHM_PAD_BETWEEN_TIME_AND_SEGSZ
unsigned long int __glibc_reserved4;
#endif
#if __SHM_SEGSZ_AFTER_TIME
size_t shm_segsz; /* size of segment in bytes */
#endif
__pid_t shm_cpid; /* pid of creator */
__pid_t shm_lpid; /* pid of last shmop */
shmatt_t shm_nattch; /* number of current attaches */
__syscall_ulong_t __glibc_reserved5;
__syscall_ulong_t __glibc_reserved6;
};
示例
shmctl(shmid, IPC_RMID, 0);
好了,看了这么啰嗦的东西大家一定感觉很难用啊,有些东西譬如“shmctl”函数里面除了删除其它功能我基本不可能用得到啊
这里我封装了一个类,方便大家使用共享内存
/**
* @file share_mem.hpp
* @author csu-lf (csu.lifeng@qq.com)
* @brief 封装共享内存基本操作
* @version 0.1
* @date 2021-07-04
*
* @copyright Copyright (c) 2021
*
*/
#ifndef _SHMEM
#define _SHMEM 1
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <stdio.h>
#include <string.h>
class CShmem
{
private:
int _shmid; // 共享内存id
key_t _key; // 共享内存键值
size_t _size; // 共享内存大小
int _authority; // 共享内存权限
void *p; // 指向共享内存第一个地址的指针
bool Init()
{
// 创建共享内存
_shmid = shmget(_key, _size, _authority|IPC_CREAT);
if(_shmid == -1) // 创建失败
{
perror("共享内存创建失败:");
p = NULL;
return false;
}
// 连接共享内存到当前进程地址空间
p = shmat(_shmid, 0, 0);
if(p == (void *) -1) // 连接失败
{
perror("共享内存连接到当前进程地址空间失败:");
p = NULL;
return false;
}
return true;
}
public:
/**
* @brief 创建一块共享内存,默认1k大小,如果有相同_key共享内存已创建则返回它,创建失败共享内存指针为 NULL
*
* @param key 共享内存键值,默认 0x5005,请使用十六进制
* @param size 共享内存大小,字节
* @param authority 共享内存权限,默认0640,请使用八进制
*/
CShmem(key_t key = 0x5005, size_t size = 1024, int authority = 0640) // 默认创建 1k 大小共享内存
{
_key = key;
_size = size;
_authority = authority;
Init();
}
int Shmid()
{
return _shmid;
}
size_t Size()
{
return _size;
}
int Authority()
{
return _authority;
}
/**
* @brief 返回共享内存首地址指针
*
* @return void* 共享内存首地址指针
*/
void* Ptr()
{
return p;
}
/**
* @brief 擦除共享内存内容
*
* @return void*
*/
void* Erase()
{
return memset(p, 0, _size);
}
/**
* @brief 把共享内存从当前进程分离
*
* @return true 分离成功
* @return false 分离失败
*/
bool Depart()
{
if (shmdt(p) == -1) // 分离共享内存
{
return false;
}
else return true;
}
/**
* @brief 删除共享内存
*
* @return true 删除成功
* @return false 删除失败
*/
bool Destory()
{
if (shmctl(_shmid, IPC_RMID, 0) == -1) // 删除共享内存
{
return false;
}
else return true;
}
};
#endif
看到这了,给个赞吧。这个使用示例大家可以参考我的GitHub示例,还可以通过我封装的信号量锁给共享内存加把锁保证内存不会同时被两段程序同时使用。编译只需要在代码文件夹下执行make命令即可,首先运行write写,在运行read读即可查看效果。