共享内存是一种用于实现进程间通信(IPC)的方法,不同进程通过访问同一块内存区域实现数据共享和交互。每个进程可以将自身的虚拟地址映射到物理内存中的特定区域,当不同进程将相同的物理内存区域与各自的虚拟地址空间关联时,这些进程就能实现通过共享内存来完成IPC。若某进程更改了共享内存区的内容,其它进程都会觉察到该区域的更改。

概述

共享内存是一种用于实现进程间通信(IPC)的方法,不同进程通过访问同一块内存区域实现数据共享和交互。每个进程可以将自身的虚拟地址映射到物理内存中的特定区域,当不同进程将相同的物理内存区域与各自的虚拟地址空间关联时,这些进程就能实现通过共享内存来完成IPC。若某进程更改了共享内存区的内容,其它进程都会觉察到该区域的更改。

每个进程有自己的进程控制块和地址空间,且都有一个与之对应的页表,负责将进程的虚拟地址与物理地址进行映射,通过内存管理单元(MMU)进行管理。两个不同的虚拟地址通过页表映射到物理空间的同一区域,它们所指向的这块区域即共享内存。

但是共享内存没有进程间同步与互斥机制。例如,进程A对共享内存执行写操作,在A的写入结束之前,进程B就可以从共享内存区读取数据,并无某种自动的机制来阻止进程B的读操作。一般为了实现进程同步和互斥,常常将共享内存和信号量配合使用。

系统接口

系统给出了共享内存相关的一些接口,常用的主要有创建共享内存、进程关联共享内存、进程去关联共享内存、设置共享内存等,各接口列举如下,其更详细的说明可参阅资料。

1
2
3
4
//创建
#include<sys/ipc.h>
#include<sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);

该接口用于获得一个共享内存标识符或创建一个共享内存对象并返回其标识符。key为标识共享内存的键值,size以字节为单位设定内存大小,shmflg为内存模式标志参数。

1
2
3
4
//关联
#include<sys/types.h>
#include<sys/shm.h>
void *shmat(int shmid, const void *shmaddr, int shmflg);

该接口将标识符为shmid的共享内存区域对象映射到调用进程的地址空间,shmaddr指定共享内存出现在进程内存地址所在的位置,常设为NULL让内核决定合适的位置。shmflg指定操作共享内存的方式。当映射成功时,函数返回附加好的共享内存地址。

1
2
3
4
//去关联
#include<sys/types.h>
#include<sys/shm.h>
int shmdt(const void *shmaddr);

该接口用于断开共享内存连接,禁止本进程访问此片共享内存区域。shmaddr指连接的共享内存的起始地址,断开成功时函数返回0。

1
2
3
4
//设置(常用来删除)
#include<sys/types.h>
#include<sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_ds *buf);

该接口实现对共享内存段的控制,获取或设置其相关属性。cmd设置为IPC_RMID时该接口删除此段共享内存。

例子

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

struct st_pid {
int pid;
char name[51];
};

int main(int argc, char *argv[]) {
int shmid;

// 申请共享内存
if ((shmid = shmget(0x5005, sizeof(struct st_pid), 0640|IPC_CREAT)) == -1) {
printf("shmget(ox5005) failed\n");
return -1;
}

struct st_pid *stpid = 0;

// 连接共享内存
if((stpid = (struct st_pid*)shmat(shmid, 0, 0)) == (void *)-1) {
printf("shmat(ox5005) failed\n");
return -1;
}

printf("pid=%d, name=%s\n", stpid->pid, stpid->name);
stpid->pid = getpid();
strcpy(stpid->name, argv[1]);

printf("pid=%d, name=%s\n", stpid->pid, stpid->name);
// 分离共享内存
shmdt(stpid);
// 删除共享内存
if(shmctl(shmid, IPC_RMID, 0) == -1) {
printf("shmctl failed\n");
return -1;
}
return 0;
}