共享内存区概述
共享内存区是IPC形式中最快的。共享内存区中的单个数据副本对于共享该内存区的所有线程或者进程都是可用的。
一旦这样的内存区映射到共享它的进程的地址空间,这些进程间的数据传递就不再涉及内核(进程不再通过执行任何进入内核的系统调用来彼此传递信息,即不再使用read/write/lseek来访问文件),而是存取已由mmap映射到该文件的内存位置。将显示的文件I/O操作变换成存取内存单元。
但是往该共享内存区存放信息或从中取走信息的进程间则需要某种形式的同步。
mmap函数
mmap函数将一个文件或一个posix共享内存对象映射到调用进程的地址空间,实现三个目的:
1.使用普通文件以提供内存映射I/O.(所有的I/O都在内核的掩盖下完成)
2.使用特殊文件以提供匿名内存映射
3.使用shm_open以提供无亲缘关系进程间的posix共享内存区
#include <sys/mman.h> void *mmap(void *addr,size_t len,int prot,int flags,int fd,off_t offset);//若成功则返回被映射区起始地址,出错则返回MAP_FAILED.
addr 可以指定描述符fd应被映射到的进程内空间的起始地址,通常被指定为一个空指针,这样告诉内核自己去选择起始地址。
len映射到调用进程地址空间中的字节数,它从被映射文件开头起第offset个字节开始算。
prot参数指定内存映射区的保护,值为:PROT_READ数据可读,PROT_WRITE数据可写,PROT_EXEC数据可执行,PROT_NONE数据不可执行。
flag参数MAP_SHARED变动是共享的,MAP_PRIVATE变动是私自的,MAP_FIXED准确解释addr参数。
munmap函数
为从某个进程的地址空间删除一个映射关系,可以使用munmap:
#include <sys/mman.h> int munmap(void *addr,size_t len);
addr参数是mmap返回的地址,len是映射区的大小,
msync函数
内核的虚拟内存算法保存内存映射文件(一般在硬盘上)与内存映射区(在内存中)的同步,前提是它是一个MAP_SHARED内存区,即如果我们修改了处于内存映射到某个文件的内存区值某个位置的内容,那么内核将在稍后的某个时刻相应的更新文件。然而有时候我们希望确信硬盘上的文件内容与内存映射区中的内容一致,于是调用msync来执行这种强制立即同步。
#include <sys/mman.h> int msync(void *addr,size_t len,int flags);
addr 和 len直到内存中的整个内存映射区,flags为 MS_ASYNC执行异步写(一旦写操作已由内核排入队列,则立即返回),MS_SYNC执行同步写(等待写操作完成后才返回),MS_INVALIDATE使高速缓存的数据失效。
父子进程使用共享内存映射文件
#define SEM_NAME "mysem" int main(int argc,char **argv) { int fd,i,nloop,zero=0; int *ptr; sem_t *mutex; if(argc != 3) err_quite(); nloop = atoi(argv[2]); fd = open(argv[1],O_RDWR | O_CREAT,FILE_MODE);//打开一个文件 write(fd,&zero,sizeof(int)); ptr = mmap(NULL,sizeof(int),PROT_READ | PROT_WRITE,MAP_SHAED,fd,0)//NULL空指针由系统选择起始地址。将文件映射到一个地址ptr,MAP_SHAED父子进程共享该地址 close(fd); mutex = sem_open(px_ipc_name(SEM_NAME),O_CREAT | O_EXCL,FILE_MODE,1);//创建一个有名信号量 sem_unlink(px_ipc_name(SEM_NAME)); setbuf(stdout,NULL); if(fork() ==0 ) { for(i=0;i<nloop;i++) { sem_wait(mutex); printf("child:%d \n",(*ptr)++);//ptr地址由mmap将fd映射过来,且设为MAP_SHAED共享,则父子进程间相互修改可以影响。否则父子进程各自使用副本。 sem_post(mutex); } exit(0); } for(i = 0;i< nloop;i++) { sem_wait(mutex); printf("parent:%d \n",(*ptr)++);//ptr地址由mmap将fd映射过来,且设为MAP_SHAED共享,则父子进程间相互修改可以影响。否则父子进程各自使用副本。 sem_post(mutex); } exit(0); }