共享内存是操作系统在内存中申请的一块空间,进程将该内存映射到自身进程地址空间的共享区,然后通过这个映射往共享内存写入或读取数据,完成进程间的通信。
完成通信后,进程要先解除映射关系,然后让操作系统释放共享内存。
Linux系统中提供了shmget
接口用于创建共享内存:
//shmget所在的头文件和声明
#include
#include
int shmget(key_t key, size_t size, int shmflg);
shmid
,失败返回-1,错误码被设置。key参数
共享内存是操作系统在内存中申请的一块内存空间,操作系统中可能会有大量的共享内存,操作系统为了管理这些共享内存就要用相应的结构进行描述,然后将描述的结构组织起来,共享内存中存在着一个字段用于唯一标识共享内存,这个字段使用的就是key参数
传入的值。在进程使用共享内存通信前,一个进程创建共享内存并将key参数
写入,然后另一个想要与其通信的进程就用同样的key参数
和操作系统中的共享内存描述结构中的key参数
进行比对,找到对应的共享内存,从而进行通信。
为了让使用的key
参数唯一,保证进程使用的共享内存不会出错,我们使用ftok
系统接口:
//ftok所在的头文件和声明
#include
#include
key_t ftok(const char *pathname, int proj_id);
ftok
会根据传入的参数使用算法返回一个重复率极低的数字。size参数
size参数
用于指明要创建的共享内存的大小,单位为字节。
操作系统创建共享内存是以page页
为单位的,大小为4KB。
shmflg参数
shmflg参数
用于指明shmget
的使用模式。
IPC_CREAT
:创建一个共享内存,如果共享内存不存在,就创建之,如果已经存在,获取已经存在的共享内存并返回。IPC_EXCL
不能单独使用,一般都要配合IPC_CREAT
。IPC_CREAT | IPC_EXCL
: 创建一个共享内存,如果共享内存不存在,就创建之, 如果已经存在,则立马出错返回 – 如果创建成功,对应的shm,一定是最新的!umask
可以给要创建的共享内存设置权限。要想使用共享内存必须进行共享内存的关联,将共享内存映射到自己的进程地址空间。Linux系统中提供了shmat
接口用于关联共享内存:
//shmat所在的头文件和声明
#include
#include
void *shmat(int shmid, const void *shmaddr, int shmflg);
在使用完共享内存进行通信后,要进行共享内存的去关联操作。Linux系统中提供了shmdt
接口用于去关联共享内存:
//shmdt所在的头文件和声明
#include
#include
int shmdt(const void *shmaddr);
shmat
关联共享内存时返回的地址。Linux系统中提供了shmctl
接口用于控制共享内存:
//shmctl所在的头文件和声明
#include
#include
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
IPC_RMID
选项就是删除共享内存。在Linux系统中使用ipcs -m
可以查看系统中的共享内存:
key
:进程调用shmget
创建共享内存时传入的key参数
。shmid
:共享内存编号。owner
:共享内存的拥有者。perms
:拥有者对共享内存的权限。bytes
:共享内存的大小。在Linux系统中使用ipcrm -m 对应shmid
可以删除对应的共享内存:
在编码测试共享内存前,创建三个文件:common.hpp
、server.cc
、client.cc
。
common.hpp
用于实现使用共享内存的类和接口,具体内容如下:
#ifndef __COMN_HPP__
#define __COMM_HPP__
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define PATHNAME "."
#define PROJID 0x6666
#define gsize 4096
//key_t类型实际也是int类型
key_t GetKey()//获取key值
{
key_t k = ftok(PATHNAME, PROJID);
if(k == -1)//错误检测
{
cerr << "errno: " << errno << " strerror: " << strerror(errno) << endl;
exit(1);
}
return k;
}
//只在本文件生效
static int createShmHelper(key_t key, size_t size, int shmflg)
{
int shmid = shmget(key, size, shmflg);
if(shmid == -1)//错误检测
{
cerr << "errno: " << errno << " strerror: " << strerror(errno) << endl;
exit(2);
}
return shmid;
}
int createShm(key_t k, int size)
{
umask(0);
return createShmHelper(k, size, IPC_CREAT | IPC_EXCL | 0666);
}
int getShm(key_t k, int size)
{
return createShmHelper(k, size, IPC_CREAT);
}
void* attachShm(int shmid)
{
void* start = shmat(shmid, NULL, 0);
return start;
}
void detachShm(void* start)
{
int n = shmdt(start);
assert(n != -1);
(void)n;
}
void delShm(int shmid)
{
int n = shmctl(shmid, IPC_RMID, nullptr);
assert(n != -1);
(void)n;
}
#define SERVER 1
#define CLIENT 0
class Shm
{
public:
Shm(int type):_type(type)
{
key_t key = GetKey();
if (_type == SERVER) _shmid = createShm(key, gsize);
else _shmid = getShm(key, gsize);
_start = attachShm(_shmid);
}
void* getStart()
{
return _start;
}
~Shm()
{
detachShm(_start);
if (_type == SERVER) delShm(_shmid);
}
private:
void* _start;
int _shmid;
int _type;
};
#endif
server.cc
用于创建共享内存,读取共享内存中的数据,完成共享内存的释放,具体内容如下:
#include "common.hpp"
int main()
{
Shm shm(SERVER);
char* start = (char*)shm.getStart();
int n = 0;
while(n < 30)
{
cout << "client send me : " << start << endl;
n++;
sleep(1);
}
return 0;
}
client.cc
用于向共享内存写入数据,具体内容如下:
#include "common.hpp"
int main()
{
Shm shm(CLIENT);
char* start = (char*)shm.getStart();
char ch = 'a';
while(ch <= 'z')
{
start[ch - 'a'] = ch;
ch++;
start[ch - 'a'] = 0;
sleep(1);
}
return 0;
}
编译代码运行并查看结果: