共享内存简介
共享内存可以说是最有用的进程间通信方式,也是最快的IPC形式。两个不同进 程A、B共享内存的意思是,同一块物理内存被映射到进程A、B各自的进程地址空 间。进程A可以即时看到进程B对共享内存中数据的更新,反之亦然。由于多个进 程共享同一块内存区域,必然需要某种同步机制,互斥锁和信号量都可以。1
采用共享内存通信的一个显而易见的好处是效率高,因为进程可以直接读写内存, 而不需要任何数据的拷贝。对于像管道和消息队列等通信方式,则需要在内核和 用户空间进行四次的数据拷贝,而共享内存则只拷贝两次数据1 :一次从输入 文件到共享内存区,另一次从共享内存区到输出文件。实际上,进程之间在共享 内存时,并不总是读写少量数据后就解除映射,有新的通信时,再重新建立共享 内存区域。而是保持共享区域,直到通信完毕为止,这样,数据内容一直保存在 共享内存中,并没有写回文件。共享内存中的内容往往是在解除映射时才写回文 件的。因此,采用共享内存的通信方式效率是非常高的。1
本文介绍了共享内存的两种方法,第一种是通过 mmap 实现共享内存,第二种是 System V 进程间通信机制。
mmap 方式
mmap()系统调用使得进程之间通过映射同一个普通文件实现共享内存。普通文件被映射到进程地址空间后,进程可以向访问普通内存一样对文件进行访问,不必再调用read(),write()等操作。
实际上,mmap()系统调用并不是完全为了用于共享内存而设计的。它本身提供了 不同于一般对普通文件的访问方式,进程可以像读写内存一样对普通文件的操作。 而Posix或系统V的共享内存IPC则纯粹用于共享目的,当然 mmap() 实现共享内 存也是其主要应用之一。
下图是 mmap 将文件映射到进程地址空间的示意图3 :
![]() |
mmap 示意图 |
创建文件并将其 mmap 到地址空间的主要流程如下所示:
int fd;
void* memptr;
/* create a tmp file.*/
fd = open ("/var/tmp/mmapfile", O_WRONLY | O_CREAT | O_TRUNC, 0644);
/* Write data to file */
write (fd, &data, sizeof (data));
/* mmap file to address space of calling process*/
memptr = mmap (0, sizeof (data), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
......
/* Delete the mapping .*/
unlink ("/var/tmp/mmapfile");
munmap (memptr, sizeof (data));
映射文件到自己的地址空间的主要流程如下所示:
int fd;
void* memptr;
fd = open ("/var/tmp/mmapfile", O_RDONLY);
size = lseek (fd, 0, SEEK_END);
memptr = mmap (0, size, PROT_READ, MAP_SHARED, fd, 0);
......
unlink ("/var/tmp/mmapfile");
munmap (memptr, size);
System V 共享内存
System V 共享内存是通过映射特殊文件系统 shm 中的文件实现进程间的共享内 存通信,即把所有共享数据放在共享内存区域 (IPC shared memory region), 任何想要访问该数据的进程都必须在本进程的地址空间新增一块内存区域,用来 映射存放共享数据的物理内存页面。2
创建共享内存的主要流程如下面的伪代码所示:
key_t shnm_key;
int shmid;
void* memptr;
/* get a shm key */
shm_key = ftok (filename, 0);
/* allocate a shared memory segment */
shmid = shmget (shm_key, sizem SHM_PARAM | IPC_CREAT | IPC_EXCL);
/* Attach the shared memory segment identified by shmid to the address space
of calling process */
memptr = shmat (shmid, 0, 0);
/* write data to shared memory */
memcpy (memptr, &data, sizeof (data));
/* Mark the segment to be destroyed. The segment will only actually be
destroyed after the last process detached it. */
shmctl (shmid, IPC_RMID, NULL);
.......
/* Detached the shared memory segment from the address space of
calling process. */
shmdt (memptr);
attach 共享内存到 calling process 的流程如下所示:
void* memptr;
get shmid from shared memory segment creator
/* Attach shared memory segment to the address space of calling process.*/
memptr = shmat(shmid, 0, SHM_RDONLY);
... ...
/* Detached the shared memory segment.*/
shmdt (memptr);
See Also
在 IBM Developworks 上有两篇文章对 mmap 和 System V 这两种共享内存方式 进行了详细描述和比较:
- Linux 环境进程间通信(五):共享内存 (上)
- Linux 环境进程间通信(五):共享内存 (下)
- mmap 的示意图来自 http://www.bitctrl.com/qnx/qnx6_sysarch/qnx6_sysarch_2b.htm
- mmap、munmap、shmget, shmat, shmctl, shmdt 手册
- sharedmem.tar.gz 是我整理的一个共享内存的示例代码。