Linux下共享内存的使用方法

开发环境:Ubuntu16.04 LTS。

前言

共享存储允许两个或多个进程共享一个给定的存储区,是进程间通信最快的一种方式。不要同时对共享存储空间进行写操作。通常,可以将信号量用于同步共享存储访问。

最简单的共享内存的使用流程如下:

  1. ftok函数生成键值

  2. shmget函数创建共享内存空间

  3. shmat函数获取第一个可用共享内存空间的地址

  4. shmdt函数进行分离(对共享存储段操作结束时的步骤,并不是从系统中删除共享内存和结构)

  5. shmctl函数进行删除共享存储空间

上述的五个函数都是系统自带的函数,接下来对它们一一介绍。

系统函数介绍

ftok函数

1.功能:生成键值。每一个共享存储段都有一个对应的键值(key)相关联(消息队列、信号量也同样需要)。

2.所需头文件:

1
#include<sys/ipc.h>

3.函数原型:

1
key_t ftok(const char *path ,int id);

path为一个已存在的路径名;id为0~255之间的一个数值,代表项目ID。

4.返回值:成功返回键值(相当于32位的int),出错返回-1。

5.示例:

1
key_t key = ftok( “/tmp”, 66);

shmget函数

1.功能:创建共享存储空间并返回一个共享存储标识符。

2.所需头文件:

1
#include<sys/shm.h>

3.函数原型:

1
int shmget(key_t key, size_t size,int flag);

key为ftok生成的键值。
size为共享内存的长度,以字节为单位。
flag为所需要的操作和权限,可以用来创建一个共享存储空间并返回一个标识符或者获得一个共享标识符。所取值可为IPC_CREAT、IPC_CREAT | IPC_EXCL。
flag的值为IPC_CREAT:如果不存在key值的共享存储空间,且权限不为0,则创建共享存储空间,并返回一个共享存储标识符。如果存在,则直接返回共享存储标识符。
flag的值为 IPC_CREAT | IPC_EXCL:如果不存在key值的共享存储空间,且权限不为0,则创建共享存储空间,并返回一个共享存储标识符。如果存在,则产生错误。

4.返回值:成功返回共享存储ID,出错返回-1。

5.示例:

1
int id = shmget(key,4096,IPC_CREAT|0666);

说明:创建一个大小为4096个字节的权限为0666(所有用户可读可写,具体查询linux权限相关内容)的共享存储空间,并返回一个共享存储标识符,如果key值已经存在有共享存储空间了,则直接返回一个共享存储标识符。

1
int id = shmget(key, 4096, IPC_CREAT|IPC_EXCL|0666);

说明:创建一个大小为4096个字节的权限为0666(所有用户可读可写,具体查询linux权限相关内容)的共享存储空间,并返回一个整形共享存储标识符,如果key值已经存在有共享存储空间了,则出错返回-1。

shmat函数

1.功能:获取第一个可用共享内存空间的地址。

2.所需头文件:

1
#include<sys/shm.h>

3.函数原型:

1
void *shmat(int shmid, const void *addr, int flag);

shmid为shmget生成的共享存储标识符。
addr指定共享内存出现在进程内存地址的什么位置,直接指定为NULL让内核自己决定一个合适的地址位置。
flag为对数据的操作,如果指定为SHM_RDONLY则以只读方式连接此段,其他值为读写方式连接此段。
翻阅linux下shm.c文件得到#define SHM_RDONLY 010000 // read-only access 。
flag的值为 IPC_CREAT | IPC_EXCL:如果不存在key值的共享存储空间,且权限不为0,则创建共享存储空间,并返回一个共享存储标识符。如果存在,则产生错误。

4.返回值:成功返回指向共享存储段的指针;错误返回-1(打印出指针的值为全F)。

5.示例:

1
char *addr  = shmat(id, NULL, 0);      //返回第一个可用的共享内存地址的指针的值给addr

shmdt函数

1.功能:当不需要对此共享内存进行操作时候,调用shmdt函数进行分离,不是删除此共享存储空间哟。

2.所需头文件:

1
#include<sys/shm.h>

3.函数原型:

1
int shmdt(const void *addr);

addr为shmat函数返回的地址指针。

4.返回值:成功返回0;错误返回-1。

5.示例:

1
int ret = shmdt(addr);

shmctl函数

1.功能:对共享内存进行控制。最简单的操作就是删除共享存储空间了,也可以获取和改变共享内存的状态。

2.所需头文件:

1
#include<sys/shm.h>

3.函数原型:

1
int shmctl(int shmid, int cmd, struct shmid_ds *buf);

shmid就是shmget函数返回的共享存储标识符。
cmd有三个:IPC_RMID——删除共享内存;
IPC_STAT——得到共享内存的状态,把共享内存的shmid_ds结构复制到buf中;
IPC_SET——改变共享内存的状态,把buf所指的shmid_ds结构中的uid、gid、mode复制到共享内存的shmid_ds结构内。(内核为每个共享存储段维护着一个结构,结构名为shmid_ds,这里就不讲啦,里面存放着共享内存的大小,pid,存放时
间等一些参数)
buf就是结构体shmid_ds。

4.返回值:成功返回0;错误返回-1。

5.示例:

1
int ret = shmctl(id, IPC_RMID,NULL);    //删除id号的共享存储空间

Linux共享内存的查看方法

1.使用ipcs -m命令可以查看当前系统所有的共享内存空间信息,如下图所示。

01

2.如果程序创建了一个共享内存段,但没有被正确销毁,可能在下次打开程序时出现错误,这时可使用ipcrm -m shmid命令先删除共享内存段。例如要删除上图中的最后一块共享内存,可使用命令ipcrm -m 6455308,删除后的效果如下图所示。

02

3.在Linux下,如果你申请24字节大小的共享存储空间,系统还是会默认给你分配一页的大小,但你还是只能使用这一页上24字节的空间。使用getconf PAGE_SIZE命令就能显示出一页的大小。

共享内存使用示例程序

整体介绍

本例采用两个独立程序(server与client)来进行共享内存测试。首次打开程序时,先用server开辟一段共享内存,再用client连接到该共享内存;之后使用时,server与client可以互相向共享内存中读和写。两个程序都具有如下目录结构:

07

两个程序的区别是在main.cpp和CMakeLists.txt中。

CKX.h

该头文件只是定义一个数据类,用来向内存中写入姓名和年龄。

1
2
3
4
5
6
7
8
9
10
11
12
13
#ifndef  __CKX_H_
#define __CKX_H_

#define NAME_LEN 20

class ckx {
public:
char name[NAME_LEN];
int age;
ckx() {};
};

#endif // __CKX_H_

CShareMemory.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#ifndef  __CSHAREMEMORY_H_
#define __CSHAREMEMORY_H_

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>

class CShareMemory {
public:
CShareMemory() {addr = NULL; shmid=0; size=0;};
int create_ipc(int key_id, int ipc_size);
int get_ipc(int key_id, int ipc_size);
int destroy_sharememory();
int close_shm();
void* get_address();
private:
int shmid;
int size;
void* addr;
int sharememory(int key_id, int ipc_size, int flag);
};

#endif // __CSHAREMEMORY_H_

CShareMemory.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<sys/shm.h>
#include "CShareMemory.h"
int CShareMemory::sharememory(int key_id, int ipc_size, int flag) {
key_t key = ftok("/tmp", key_id);
if (key < 0) {
printf("get key error\n");
return -1;
}
shmid = shmget(key, ipc_size, flag);
addr = shmat(shmid, (void *)0, 0);
if (shmid < 0) {
printf("get shmid error\n");
return -1;
}
return shmid;
}

int CShareMemory::create_ipc(int key_id, int ipc_size) {
return sharememory(key_id, ipc_size, IPC_CREAT|IPC_EXCL|0666);
}

int CShareMemory::get_ipc(int key_id, int ipc_size) {
return sharememory(key_id, ipc_size, IPC_CREAT|0666);
}

int CShareMemory::destroy_sharememory() {
return shmctl(shmid, IPC_RMID, NULL);
}

/**
* @brief free the share memory
* <long-description>
*
* @param
* @return <ReturnValue>
*/
int CShareMemory::close_shm()
{
int rtn;
rtn = shmdt(addr);
return rtn;
}

/**
* @brief get address of share memory
* <long-description>
*
* @param
* @return <ReturnValue>
*/
void* CShareMemory::get_address()
{
if (addr == NULL) return NULL;
return (void*) addr;
}

Server的main.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/shm.h>
#include<sys/types.h>
#include<stdlib.h>
#include "CKX.h"
#include "CShareMemory.h"

using namespace std;

int main() {
int i = 0;
int key = 66;
ckx *p2 = new ckx();
CShareMemory shmm;
int id = shmm.create_ipc(key, sizeof(*p2));
cout << "sizeof *p2 is: " << sizeof(*p2) << endl;
cout << "id = " << id << endl;
if (id < 0) {
cout << "Create sharememory error!" << endl;
return 0;
}

while(1) {
cout << "\n\n1.input data to sharememory\n2.get sharememory data\n3.destroy sharememory\ninput select:" << endl;
cin >> i;
if(i > 3 || i < 1) {
cout << "Input error!" << endl;
}
id = shmm.get_ipc(key, sizeof(*p2));
cout << "id = " << id << endl;
if(id < 0) {
cout << "Get sharememory error!" << endl;
break;
}
p2 = (ckx *)shmat(id, NULL, 0);
if(p2 < 0) {
cout << "Get sharememory address error!" << endl;
p2 = NULL;
break;
}
switch (i) {
case 1: {
char name[NAME_LEN];
int age = 0;
cout << "Input name:" << endl;
fflush(stdin);
getchar();
gets(name);
strcpy(p2->name, name);
cout << "Input age: " << endl;
cin >> age;
p2->age = age;
cout << "Write success!" << endl;
break;
}
case 2: {
cout << "name: " << p2->name << "\t" << "age: " << p2->age << endl;
break;
}
case 3: {
if(shmm.destroy_sharememory() == -1)
cout << "-1" << endl;
id = 0;
break;
}
default:
break;
}

if(shmm.close_shm() == -1) {
cout << "shmdt error!" << endl;
break;
}
if(id == 0) {
break;
}
}
}

Server的CMakeLists.txt

1
2
3
4
5
cmake_minimum_required(VERSION 3.1.0)
project(shrmmTestServer)
set (CMAKE_CXX_STANDARD 11)
include_directories("./include/")
add_executable(shrmmTestServer main.cpp ./src/CShareMemory.cpp)

Client的main.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/shm.h>
#include<sys/types.h>
#include<stdlib.h>
#include "CKX.h"
#include "CShareMemory.h"

using namespace std;

int main() {
int i = 0;
int key = 66;
ckx *p2 = new ckx();
CShareMemory shmm;
int id = 0;
cout << "sizeof *p2 is: " << sizeof(*p2) << endl;
cout << "id = " << id << endl;

if (id < 0) {
cout << "Create sharememory error!" << endl;
return 0;
}

while(1) {
cout << "\n\n1.input data to sharememory\n2.get sharememory data\n3.destroy sharememory\ninput select:" << endl;
cin >> i;
if(i > 3 || i < 1) {
cout << "Input error!" << endl;
}
id = shmm.get_ipc(key, sizeof(*p2));
cout << "id = " << id << endl;
if(id < 0) {
cout << "Get sharememory error!" << endl;
break;
}
p2 = (ckx *)shmat(id, NULL, 0);
if(p2 < 0) {
cout << "Get sharememory address error!" << endl;
p2 = NULL;
break;
}
switch (i) {
case 1: {
char name[NAME_LEN];
int age = 0;
cout << "Input name:" << endl;
fflush(stdin);
getchar();
gets(name);
strcpy(p2->name, name);
cout << "Input age: " << endl;
cin >> age;
p2->age = age;
cout << "Write success!" << endl;
break;
}
case 2: {
cout << "name: " << p2->name << "\t" << "age: " << p2->age << endl;
break;
}
case 3: {
if(shmm.destroy_sharememory() == -1)
cout << "-1" << endl;
id = 0;
break;
}
default:
break;
}

if(shmm.close_shm() == -1) {
cout << "shmdt error!" << endl;
break;
}
if(id == 0) {
break;
}
}
}

Client的CMakeLists.txt

1
2
3
4
5
cmake_minimum_required(VERSION 3.1.0)
project(shrmmTestClient)
set (CMAKE_CXX_STANDARD 11)
include_directories("./include/")
add_executable(shrmmTestClient main.cpp ./src/CShareMemory.cpp)

程序运行结果

1.从server中输入名字和年龄存储到共享内存中,并从client中读取:

03
04

2.从client中输入名字和年龄存储到共享内存中,并从server中读取:

05
06

Note:自己创建build文件夹并进行编译!

参考链接Linux下共享内存编程(共享存储空间)

------ 本文结束感谢您的阅读------
Donate a cup of cola?