Linux 共享内存(shared memory)

Linux 共享内存 API 的基本用法。

struct shmid_ds

1
2
3
4
5
6
7
8
9
10
11
struct shmid_ds {
struct ipc_perm shm_perm; /* Ownership and permissions */
size_t shm_segsz; /* Size of segment (bytes) */
time_t shm_atime; /* Last attach time */
time_t shm_dtime; /* Last detach time */
time_t shm_ctime; /* Last change time */
pid_t shm_cpid; /* PID of creator */
pid_t shm_lpid; /* PID of last shmat(2)/shmdt(2) */
shmatt_t shm_nattch; /* No. of current attaches */
/* ... */
};

shmget()函数

1
2
3
4
#include <sys/ipc.h>
#include <sys/shm.h>

int shmget(key_t key, size_t size, int shmflg);

函数shmget()返回与key相联系的共享内存标识符,或者在keyIPC_PRIVATE或与
key相关联的共享内存不存在且shmflg中指定了IPC_CREAT时创建新的共享内存。新创
建的共享内存的内容会被初始化为 0. size会被扩展为PAGE_SIZE的整数倍。

在我使用这个函数的时候,因为在使用函数ftok()生成 key 时使用的文件路径所指向的
文件权限问题,出现了permission denied的情况,需要注意一下。

shmctl()函数

1
2
3
4
#include <sys/ipc.h>
#include <sys/shm.h>

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

函数shmctl()根据参数cmd对共享内存进行控制或获取信息。

shmat()shmdt()函数

1
2
3
4
5
6
#include <sys/types.h>
#include <sys/shm.h>

void *shmat(int shmid, const void *shmaddr, int shmflg);

int shmdt(const void *shmaddr);

函数shmat()把由shmid标识的共享内存映射(attach)到地址shmaddrshmaddr可以
NULL,此时系统会自己选取一个合适的地址进行映射,如果自己指定地址,则需要地址
与页对齐,或者在参数shmflg中指定SHM_RND,此时系统会自动对地址进行舍入。
shmflg用来指定映射方式,除了SHM_RND之外还有SHM_EXEC SHM_RDONLY等选项。

函数shmdt则是将已经映射的内存取消映射(detach)。参数shmaddr应该与函数
shmat()的返回值相同。程序退出时,共享内存也会自动取消映射(但不会自动消失)。
以下是一个示例程序:

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
#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>

#define SHMSIZE 4096

int main(int argc, char *argv[])
{
int i;
int shmid;
key_t key;
char *addr;
void *attach_addr;
struct shmid_ds shmds;

key = ftok("/home/qi/test/cc/a.cc", 1);
shmid = shmget(key, SHMSIZE, IPC_CREAT | 0666);
if (shmid == -1) {
perror("shmget error");
exit(1);
}

if (shmctl(shmid, IPC_STAT, &shmds) == -1) {
perror("shmctl error");
exit(2);
}

printf("values in shmid_ds key(%#010x):\n", key);
printf("\tshmid_ds.shm_segsz : [%zu]\n", shmds.shm_segsz);
printf("\tshmid_ds.shm_atime : [%ld]\n", shmds.shm_atime);
printf("\tshmid_ds.shm_dtime : [%ld]\n", shmds.shm_dtime);
printf("\tshmid_ds.shm_ctime : [%lu]\n", shmds.shm_ctime);
printf("\tshmid_ds.shm_cpid : [%d]\n", shmds.shm_cpid);
printf("\tshmid_ds.shm_lpid : [%d]\n", shmds.shm_lpid);
printf("\tshmid_ds.shm_nattch: [%lu]\n", shmds.shm_nattch);

attach_addr = shmat(shmid, NULL, SHM_RDONLY);
if (attach_addr == (void *)-1) {
perror("shmat error");
exit(3);
}
addr = (char *)attach_addr;
for (i=0; i < SHMSIZE; i++)
if (addr[i] != 0)
break;
if (i != SHMSIZE)
printf("shared memory has non-zero value!\n");
else
printf("shared memory is all zero\n");

i = shmdt(attach_addr); /* don't want define another variable */
if (i == -1) {
perror("shmdt error");
exit(4);
} else {
printf("shmdt succeeds\n");
}

i = shmctl(shmid, IPC_RMID, 0);
if (i == -1) {
perror("shmctl error");
exit(4);
} else {
printf("shared memory delete successfully\n");
}

return 0;
}

注意

创建的共享内存不会自动消失,需要在程序中使用函数shmctl()将其删除(示例程序中已
使用此方法进行删除),或使用命令ipcrm -m <shmid>进行清理。

References

shmget()–Get ID of Shared Memory Segment with Key - IBM Knowledge Center

0%