github位置:codetoys/ctfc.git src/function/myshm.h
关于共享内存的介绍看这里:
这里继续对编程接口进行介绍。下面提供了一个共享内存包装类。基本上能用到的都在这里了(因为我用了好多年)。
其实挺简单的。
目录
一、总代码
代码是64位的,可以运行在IBM\HP\SUN的小型机和x86-64的Linux服务器上。
#include <errno.h>
#include <sys/shm.h>
#include <sys/ipc.h>
typedef char * PSHM;
class CShmMan
{
public:
static PSHM ConnectByID(int id,bool isreadonly,void * addr=NULL)
{
PSHM pshm;
if((pshm=(PSHM)shmat(id,addr,(isreadonly?SHM_RDONLY:0)))==(PSHM)(-1L))
{
return NULL;
}
return pshm;
}
static PSHM ConnectByKey(key_t key,bool isreadonly)
{
int shmid;
if(0>(shmid=shmget(key,0,SHM_R|(SHM_R>>3)|(SHM_R>>6)|SHM_W|(SHM_W>>3)|(SHM_W>>6))))
{//必须是已经存在的
return NULL;
}
PSHM pshm;
if((pshm=(PSHM)shmat(shmid,0,(isreadonly?SHM_RDONLY:0)))==(PSHM)(-1L))
{
return NULL;
}
return pshm;
}
static int CreatePrivateSHM(long size){return CreateSHMByKey(IPC_PRIVATE,size);}
static int CreateSHMByKey(key_t key,long size)
{
int shmid;
if(0>(shmid=shmget(key,size,SHM_R|(SHM_R>>3)|(SHM_R>>6)|SHM_W|(SHM_W>>3)|(SHM_W>>6)|IPC_CREAT|IPC_EXCL)))
{//必须不是已经存在的
//LOG<<"shmid "<<shmid<<ENDE;
return -1;
}
return shmid;
}
//删除共享内存,删除后不可再使用
static bool Destory(int shmid)
{
if(shmctl(shmid,IPC_RMID,0)<0)
{
return false;
}
return true;
}
static bool Disconnect(PSHM p)
{
if(0!=shmdt(p))
{
return false;
}
return true;
}
static bool GetState(int shmid,size_t & size,time_t & ctime)
{
shmid_ds ds;
if(0!=shmctl(shmid,IPC_STAT,&ds))
{
return false;
}
size=ds.shm_segsz;
ctime=ds.shm_ctime;
return true;
}
};
二、详解
2.1 创建
有两个不同的函数:CreatePrivateSHM和CreateSHMByKey:
static int CreatePrivateSHM(long size){return CreateSHMByKey(IPC_PRIVATE,size);}
static int CreateSHMByKey(key_t key,long size)
这两个函数内部调用IPC函数shmget创建共享内存,区别仅仅是指定KEY还是不使用KEY。
按照C函数的传统习惯,shmget既可以创建也可以连接,传统处理此类功能的逻辑是通过参数区分“如果不存在就创建”或者“如果不存在就失败”。现在我们主张把创建和访问分开,比较符合正常逻辑(也就是为什么要把这几个简单函数包装一下)。
2.2 连接
连接可分两种,通过KEY连接和通过ID连接,通过KEY连接其实是先通过KEY获取ID,然后再通过ID连接:
static PSHM ConnectByID(int id,bool isreadonly,void * addr=NULL)
{
PSHM pshm;
if((pshm=(PSHM)shmat(id,addr,(isreadonly?SHM_RDONLY:0)))==(PSHM)(-1L))
{
return NULL;
}
return pshm;
}
static PSHM ConnectByKey(key_t key,bool isreadonly)
{
int shmid;
if(0>(shmid=shmget(key,0,SHM_R|(SHM_R>>3)|(SHM_R>>6)|SHM_W|(SHM_W>>3)|(SHM_W>>6))))
{//必须是已经存在的
return NULL;
}
PSHM pshm;
if((pshm=(PSHM)shmat(shmid,0,(isreadonly?SHM_RDONLY:0)))==(PSHM)(-1L))
{
return NULL;
}
return pshm;
}
这里面参数有点讲究:
- isreadonly 可以做只读连接,这样会杜绝用户误修改数据的可能,毕竟,跑飞的指针可不管你是什么内存
- void * addr 指定连接地址,前文介绍过,这种方式很困难,但是如果确实可以用,可以省不少事
2.3 断开
断开很简单,断开之后共享内存的连接数减一。
2.4 删除
删除也很简单,不过,删除是延迟的,删除操作只会把共享内存设置为删除状态,阻止新的连接,当连接数为0时才真正删除。也就是说,如果有个程序一直挂着,这块内存共享不会被释放,系统可用的内存不会增加,新的创建操作很可能会因为物理内存不足而失败。
2.5 获取状态
为了确保连接的是正确的共享内存,应该检查共享内存的状态。状态信息包括创建时间、大小和连接数,连接数当然没什么用,创建时间和大小可以用来确认是不是预期的共享内存。
static bool GetState(int shmid,size_t & size,time_t & ctime)
{
shmid_ds ds;
if(0!=shmctl(shmid,IPC_STAT,&ds))
{
return false;
}
size=ds.shm_segsz;
ctime=ds.shm_ctime;
return true;
}
三、结束语
到这里你就能建立自己的共享内存程序了,剩下的功夫就在数据结构上了。
(这里是结束)