在内存共享问题上,UNIX历史上主要有两个标准:Posix与System V,相比来说Posix标准更符合统一风格要求,与文件系统结合,更易于使用。除了这两个标准,还有一种基于磁盘文件映射的机制。
Posix标准提供一种共享内存文件设备,通过访问共享内存文件设备来实现进程间的数据共享。共享内存文件是通过shm_open创建,通过shm_unlink删除,这和普通文件的创建很相似。shm_open包含创建与打开两个动作,如果已经存在的话,也是通过shm_open打开。可以指定一些flag规定shm_open的行为,比如不存在时是否创建,是否排出已存在的实例,以及打开的读写权限。
#include <sys/mman.h> #include <sys/stat.h> /* For mode constants */ #include <fcntl.h> /* For O_* constants */ int shm_open(const char *name, int oflag, mode_t mode); int shm_unlink(const char *name);
刚创建的共享内存文件长度为0,一般需要马上扩展其长度,和普通文件一样,通过 ftruncate调整长度。
#include <unistd.h> #include <sys/types.h> int ftruncate(int fd, off_t length);
在共享内存不使用的时候,通过close关闭,和普通文件关闭的接口是同一个。关闭不会删除共享内存文件,即使最后一个打开被close了也不会删除,之后还可以再打开。只有调用shm_unlink才会删除共享内存文件。
#include <unistd.h> int close(int fd);
如果要访问共享数据,需要将共享内存文件映射到进程空间,mmap被用来实现这个映射,而munmap解除相应的映射。mmap还有其他的应用方式,与系统内存相关的操作几乎都离不开mmap和munmap。
#include <sys/mman.h> void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset); int munmap(void *addr, size_t length);
System V共享内存是System V IPC的成员,没有与文件绑定,但是通过ipc命令如ipcs,ipcrm可以操作System V IPC对象。输入ipcs命令会打印下面的内容:
------ Shared Memory Segments -------- key shmid owner perms bytes nattch status ------ Semaphore Arrays -------- key semid owner perms nsems ------ Message Queues -------- key msqid owner perms used-bytes messages
其中Shared Memory Segments就是共享内存。
可以看到ipc对象的前四项属性是所有ipc类型都有的,key是访问的索引值,打开时由使用者指定;id是ipc内部分配的编号,在打开时返回;owner是所有者id;perms是权限信息。
在命令行中,可以通过ipcrm命令删除ipc对象。ipcrm的参数指定删除那种对象,-m表示共享内存(Shared Memory Segments),-s表示信号量集(Semaphore Arrays),-q表示消息队列(Message Queues),也可以是大写的-M、-S、-Q,小写使用内部id查找,大写使用key查找。在ipc对象被删除后,内部id会变成0,就不能再被打开了。
打开和创建System V共享内存的接口是shmget,key可以随便指定,保证唯一且不等于-1就行。
#include <sys/ipc.h> #include <sys/shm.h> int shmget(key_t key, size_t size, int shmflg);
也可以通过库函数 ftok 来生成一个唯一的key。
#include <sys/types.h> #include <sys/ipc.h> key_t ftok(const char *pathname, int proj_id);
ftok使用一个文件名和一个唯一id作为参数,这个id虽然是int类型,但是只有8位被使用,所以只能传一个字节,另外标准中规定不能为0。
#include <sys/types.h> #include <sys/shm.h> void *shmat(int shmid, const void *shmaddr, int shmflg); int shmdt(const void *shmaddr);
#include <sys/ipc.h> #include <sys/shm.h> int shmctl(int shmid, int cmd, struct shmid_ds *buf);
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int open(const char *pathname, int flags); int open(const char *pathname, int flags, mode_t mode); int creat(const char *pathname, mode_t mode);
HANDLE WINAPI CreateFileMapping( __in HANDLE hFile, __in_opt LPSECURITY_ATTRIBUTES lpAttributes, __in DWORD flProtect, __in DWORD dwMaximumSizeHigh, __in DWORD dwMaximumSizeLow, __in_opt LPCTSTR lpName );
HANDLE WINAPI OpenFileMapping( __in DWORD dwDesiredAccess, __in BOOL bInheritHandle, __in LPCTSTR lpName );
BOOL WINAPI CloseHandle( __in HANDLE hObject );
LPVOID WINAPI MapViewOfFile( __in HANDLE hFileMappingObject, __in DWORD dwDesiredAccess, __in DWORD dwFileOffsetHigh, __in DWORD dwFileOffsetLow, __in SIZE_T dwNumberOfBytesToMap );
BOOL WINAPI UnmapViewOfFile( __in LPCVOID lpBaseAddress );
HANDLE WINAPI CreateFile( __in LPCTSTR lpFileName, __in DWORD dwDesiredAccess, __in DWORD dwShareMode, __in_opt LPSECURITY_ATTRIBUTES lpSecurityAttributes, __in DWORD dwCreationDisposition, __in DWORD dwFlagsAndAttributes, __in_opt HANDLE hTemplateFile );
BOOL WINAPI CloseHandle( __in HANDLE hObject );