[Linux]system V共享内存
文章目录
共享内存是Linux系统下的一种进程间通信手段,其符合system V标准。System V标准下的通信手段接口在使用方法上是相似的,包括信号量、共享内存和消息队列这三种通信机制,
共享内存通信的原理
共享内存是操作系统在内存中申请的一块空间,进程将该内存映射到自身进程地址空间的共享区,然后通过这个映射往共享内存写入或读取数据,完成进程间的通信。
完成通信后,进程要先解除映射关系,然后让操作系统释放共享内存。
系统接口
创建共享内存接口
Linux系统中提供了shmget
接口用于创建共享内存:
//shmget所在的头文件和声明
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);
- 成功返回共享内存的编号
shmid
,失败返回-1,错误码被设置。
key参数
共享内存是操作系统在内存中申请的一块内存空间,操作系统中可能会有大量的共享内存,操作系统为了管理这些共享内存就要用相应的结构进行描述,然后将描述的结构组织起来,共享内存中存在着一个字段用于唯一标识共享内存,这个字段使用的就是key参数
传入的值。在进程使用共享内存通信前,一个进程创建共享内存并将key参数
写入,然后另一个想要与其通信的进程就用同样的key参数
和操作系统中的共享内存描述结构中的key参数
进行比对,找到对应的共享内存,从而进行通信。
为了让使用的key
参数唯一,保证进程使用的共享内存不会出错,我们使用ftok
系统接口:
//ftok所在的头文件和声明
#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);
ftok
会根据传入的参数使用算法返回一个重复率极低的数字。- 成功返回0,失败返回-1,错误码被设置。
size参数
size参数
用于指明要创建的共享内存的大小,单位为字节。
操作系统创建共享内存是以page页
为单位的,大小为4KB。
shmflg参数
shmflg参数
用于指明shmget
的使用模式。
IPC_CREAT
:创建一个共享内存,如果共享内存不存在,就创建之,如果已经存在,获取已经存在的共享内存并返回。IPC_EXCL
不能单独使用,一般都要配合IPC_CREAT
。IPC_CREAT | IPC_EXCL
: 创建一个共享内存,如果共享内存不存在,就创建之, 如果已经存在,则立马出错返回 – 如果创建成功,对应的shm,一定是最新的!- 传入
umask
可以给要创建的共享内存设置权限。
关联共享内存接口
要想使用共享内存必须进行共享内存的关联,将共享内存映射到自己的进程地址空间。Linux系统中提供了shmat
接口用于关联共享内存:
//shmat所在的头文件和声明
#include <sys/types.h>
#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr, int shmflg);
- shmid参数 – 用于指明要关联的共享内存编号。
- shmaddr参数 – 指明要关联到的地址处,传入空指针操作系统会自己进行关联。
- shmflg参数 – 指明对要关联的共享内存的权限,传入0为读写权限。
- 成功返回共享内存映射在进程地址空间中的地址,失败返回-1,错误码被设置。
去关联共享内存接口
在使用完共享内存进行通信后,要进行共享内存的去关联操作。Linux系统中提供了shmdt
接口用于去关联共享内存:
//shmdt所在的头文件和声明
#include <sys/types.h>
#include <sys/shm.h>
int shmdt(const void *shmaddr);
- shmaddr参数 – 要去关联的共享内存的地址,即调用
shmat
关联共享内存时返回的地址。 - 成功返回0,失败返回-1,错误码被设置。
删除共享内存接口
Linux系统中提供了shmctl
接口用于控制共享内存:
//shmctl所在的头文件和声明
#include <sys/ipc.h>
#include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
- shmid参数 – 用于指明要控制的共享内存编号。
- cmd参数 – 用于指定控制选项,其中
IPC_RMID
选项就是删除共享内存。 - buf参数 – 输出型参数,用于接收描述共享内存的结构体信息。
使用指令操作共享内存
查看共享内存
在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 <iostream>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <cstdio>
#include <cerrno>
#include <cstring>
#include <cassert>
#include <unistd.h>
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;
}
编译代码运行并查看结果: