共享内存是最有用,也是最快的IPC方式。有三种实现共享内存的方法:系统调用mmap( )、POSIX共享内存、系统V共享内存。
三者都是先将文件映射到物理内存页面中,然后各个进程再把物理内存页面映射到本进程的地址空间中,从而实现进程间通信。所不同的是mmap( )映射的是普通文件;而系统V映射的是特殊文件系统shm中的文件,文件系统shm安装在交换分区上,系统重启后,shm的内容会丢失。
文件被映射到内存后,进程可以像访问内存一样对文件进行访问,不必再使用read( ),write( )等函数。由于多个进程共享内存,所以需要某种同步机制,如互斥锁、信号灯等。
1.在Linux中,内存是以页为基本单位的,即使映射文件只有一个字节大小,内核在映射时也会分配一个页面大小的内存。
2.所有对共享内存的操作只在共享内存中有意义,只有在munmap( )或msync( )后,才能把共享内存中的内容写入文件。
#include
void*mmap ( void * addr, size_t len , int prot , int flags , int fd , off_t offset )
#include
longint sysconf( int name )
intgetpagesize( void ) = sysconf(_SC_PAGE_SIZE)
成功返回内存地址,失败返回(void*)-1。
addr :用户希望映射到当前进程地址空间中的内存地址;如果是NULL,由系统完成映射
fd :文件描述符,可以为-1,此时参数flags必须指定MAP_ANONYMOUS
len :文件映射到内存的字节数
offset :一般设为0,表示从文件起始处开始映射,应该是页面大小的整数倍
prot :内存访问权限:PROT_READ,PROT_WRITE,PROT_EXEC,PROT_NONE
flags :
MAP_ ANONYMOUS :匿名映射,忽略fd和offset,用于具有亲缘关系的进程间通信
MAP_SHARED :对内存的更改能够影响文件(但要通过msync( )或munmap( )写入)
MAP_PRIVATE :对内存的更改不能影响文件,只在当前进程可见
MAP_FIXED :必须映射到指定的内存地址
MAP_LOCKED :锁定映射的内存
#include
intmunmap( void * addr, si ze_t len )
取消[addr,addr+len-1]内涉及到的所有映射;成功返回0,失败返回-1。
注意:当进程结束时,映射会自动被取消。
#include
intmprotect( void *addr, size_t len, int prot )
更改[addr,addr+len-1]内涉及到的所有映射的访问权限;成功返回0,失败返回-1。
#include
intmsync ( void * addr , size_t len, int flags )
同步内存和文件;成功返回0,失败返回-1。
参数flags可以取:
MS_ASYNC :立即返回,写入操作异步进行
MS_SYNC :直到写入操作完成,才能返回
MS_INVALIDATE:刷新缓存,取消写入操作
注意:
1.内存可以被锁定多次,但只要一次解锁就可以完成解锁操作;
2.由于每个进程会将文件映射到自己的进程空间里面去,所以当前进程的内存解锁操作,并不影响其它进程的内存锁定操作;
#include
intmlock( const void * addr, size_t len )
intmunlock( const void * addr, size_t len )
锁定[addr,addr+len-1]内涉及到的所有映射内存;成功返回0,失败返回-1。
#include
int mlockall( int flags )
int munlockall( void )
锁定当前进程地址空间中的所有映射内存;成功返回0,失败返回-1。
参数flags可以取:
MCL_CURRENT:锁定当前进程地址空间中的所有映射内存。
MCL_FUTURE:不但锁定当前进程地址空间中的映射内存,之后新建的映射内存也被锁定。
#include
#include
key_tftok( const char *name, int id )
参数name指定了文件名(包含路径),该文件必须存在,而且当前进程必须能够访问该文件。
参数id只有低8位有效。
只有当name和id取值都相同时,返回的键值key才是相同的。
成功返回键值,失败返回-1。
#include
#include
intshmget( key_t key, size_t size, int shmflg )
获得或创建一个共享内存;成功返回和键值key相关联的共享内存id,失败返回-1。
参数shmflg可以取IPC_CREAT、IPC_EXCL、IPC_NOWAIT。
单独使用IPC_CREAT,如果不存在与键值key相关联的共享内存,就创建一个新的共享内存,并返回其id,如果已经存在与键值key相关联的共享内存,就返回该共享内存的id。
单独使用IPC_EXCL是没有意义的,如果IPC_EXCL和IPC_CREAT一起使用,当与键值key相关联的共享内存已经存在时,就失败返回-1。
下面两种情况会创建一个新的共享内存:
1.参数shmflg指定IPC_CREAT,而且没有共享内存与键值key相关联;
2.参数key指定IPC_PRIVATE;
参数shmflg的低9位指定新创建共享内存的访问权限:
所有者 |
组成员 |
其他成员 |
||||||
读 |
写 |
执行 |
读 |
写 |
执行 |
读 |
写 |
执行 |
#include
#include
void*shmat( int shmid, const void *shmaddr, int shmflg )
映射共享内存到当前进程的地址空间,成功返回映射地址,失败返回(void*)-1。
参数shmaddr指定映射到当前进程地址空间的位置,如果为NULL,系统自动进行映射。
参数shmflg可以取:
SHM_RDONLY:共享内存只读,默认可读写。
#include
#include
intshmdt( const void *shmaddr )
取消共享内存到当前进程的地址空间的映射;成功返回0,失败返回-1。
#include
#include
intshmctl( int shmid, int cmd, struct shmid_ds *buf )
获取或设置共享内存的属性;成功返回0,失败返回-1。
参数cmd可以指定三种操作:
IPC_STAT :获取共享内存的属性,返回的信息存放到buf指向的shmid_ds结构中。
IPC_SET :设置共享内存的属性,设置的信息存放在buf指向的shmid_ds结构中。
IPC_RMID :删除指定的共享内存。
#include
int shm_open( const char *name, int flags, mode_t mode )
参数name指定与共享内存相关联的名字;成功返回共享内存描述符,失败返回-1。
参数flags可以取:
O_RDONLY O_RDWR(二选一)
O_CREAT
单独使用O_CREAT,如果name指定的共享内存不存在,就创建该共享内存,并返回其描述符;如果name指定的共享内存已经存在,就返回该共享内存的描述符。如果指定了O_CREAT,参数mode指定了新创建共享内存的访问权限。
用户 |
|
组成员 |
|
其他成员 |
|
S_IXUSR |
100 |
S_IXGRP |
010 |
S_IXOTH |
001 |
S_IWUSR |
200 |
S_IWGRP |
020 |
S_IWOTH |
002 |
S_IRUSR |
400 |
S_IRGRP |
040 |
S_IROTH |
004 |
S_IRWXU |
700 |
S_IRWXG |
070 |
S_IRWXO |
007 |
O_EXCL
单独使用O_EXCL是没有意义的,如果O_EXCL和O_CREAT一起使用,当name指定的共享内存已经存在时,就失败返回。
O_TRUNC
清空文件内容,如果同时满足以下条件:共享内存已经存在,而且在参数flags指定支持写操作(O_WRONLY)。
#include
intshm_unlink( const char * name )
成功返回0,失败返回-1。
删除指定的共享内存,但是如果当前仍有其它进程正在使用该共享内存,则暂时放弃删除操作,并立即返回,直到其它进程通过调用shm_unlink( )删除之后,再进行删除操作。