共享内存可以说是最有用的进程间通信方式,也是最快的IPC形式,因为进程可以直接读写内存,而不需要任何数据的拷贝。对于像管道和消息队列等通信方式,则需要在内核和用户空间进行四次的数据拷贝,而共享内存则只拷贝两次数据: 一次从输入文件到共享内存区,另一次从共享内存区到输出文件。实际上,进程之间在共享内存时,并不总是读写少量数据后就解除映射,有新的通信时,再重新建立共享内存区域。而是保持共享区域,直到通信完毕为止,这样,数据内容一直保存在共享内存中,并没有写回文件。共享内存中的内容往往是在解除映射时才写回文件的。因此,采用共享内存的通信方式效率是非常高的。两个不同进程A、B共享内存的意思是,同一块物理内存被映射到进程A、B各自的进程地址空间,类似虚拟内存。进程A可以即时看到进程B对共享内存中数据的更新,反之亦然。由于多个进程共享同一块内存区域,必然需要某种同步机制,互斥锁和信号量都可以。
内存映射技术:
内存映射文件,是由一个文件到一块内存的映射。Win32提供了允许应用程序把文件映射到一个进程的函数(CreateFileMapping)。内存映射文件与虚拟内存有些类似,通过内存映射文件可以保留一个地址空间的区域,同时将物理存储器提交给此区域,内存文件映射的物理存储器来自一个已经存在于磁盘上的文件,而且在对该文件进行操作之前必须首先对文件进行映射。使用内存映射文件处理存储于磁盘上的文件时,将不必再对文件执行I/O操作(直接操作文件映射到的内存)(处理存储于磁盘上的文件时,若该文件已经调入内存,则直接访问该内存,该内存是属于进程地址空间的,否则类似缺页中断,从外存中调入该文件,该文件调入到的内存地址是固定的,而处理普通文件,每次需要读写该文件时,都需要进行IO操作,调入到内存中不同地址),使得内存映射文件在处理大数据量的文件时能起到相当重要的作用。
File mapping allows the process to use both random input and output (I/O) and sequential I/O. It also allows the process to work
efficiently with a large data file, such as a database, without having to map the whole file into memory. Multiple processes can
also use memory-mapped files to share data.
有两种类型的内存映射文件:
1.共享型,在线性区页上的任何写操作都会修改磁盘上的文件;而且如果进程对共享映射中的一个页进行写,那么这种修改对于其他映射了这同一文件的所有进程来说都是可见的。所以内存映射文件也可以作为进程通信的一种方式。
2.私有型,当进程创建的映射只是为读文件,而不是写文件时才会使用这种映射。出于这种目的,私有映射的效率要比共享映射的效率更高。但是对私有映射页的任何写操作都会使内核停止映射该文件中的页。因此写操作既不会改变磁盘上的文件,对访问相同文件的其他进程也是不可见的。但是私有内存映射中的页会因为其他进程对文件的修改而更新。
采用读写函数的文件操作,至少要经过:把要处理的文件内容加载到系统内存,再从系统内存到用户指定的内存,如果你多次读写文件,就会是多次的这个 加载、复制 的操作。
而内存映射就是加载一次而已,之后就都在内存中并且可以直接使用了。
mmap()及其相关系统调用
mmap()系统调用使得进程之间通过映射同一个普通文件实现共享内存(即将文件加载到内存中的某个区域)。普通文件被映射到进程地址空间后,进程可以向访问普通内存一样对文件进行访问(类似虚拟内存),不必再调用read(),write()等操作。
mmap()系统调用形式如下:
void* mmap ( void * addr , size_t len , int prot , int flags , int fd , off_t offset )
mmap的作用是映射文件描述符fd指定文件的 [off,off + len]区域至调用进程的[addr, addr + len]的内存区域,
返回值:若成功则返回映射区的起始地址,若出错则返回MAP_FAILED
参数fd为即将映射到进程空间的文件描述字,一般由open()返回,同时,fd可以指定为-1,此时须指定flags参数中的
MAP_ANON,表明进行的是匿名映射(不涉及具体的文件名,避免了文件的创建及打开,很显然只能用于具有亲缘关系的
进程间通信)。
len是映射到调用进程地址空间的字节数,它从被映射文件开头offset个字节开始算起。
prot 参数指定共享内存的访问权限。可取如下几个值的或:PROT_READ(可读) , PROT_WRITE (可写), PROT_EXEC (可执行), PROT_NONE(不可访问)。
flags由以下几个常值指定:MAP_SHARED , MAP_PRIVATE , MAP_FIXED,其中,MAP_SHARED , MAP_PRIVATE必
选其一,而MAP_FIXED则不推荐使用。
offset参数一般设为0,表示从文件头开始映射。
参数addr指定文件应被映射到进程空间的起始地址,一般被指定一个空指针,此时选择起始地址的任务留给内核来完成。函
数的返回值为最后文件映射到进程空间的地址,进程可直接操作起始地址为该值的有效地址。
通过共享映射的方式修改文件:
#include<stdio.h> #include <sys/mman.h> #include <sys/stat.h> #include <fcntl.h> #include <stdlib.h> #include <unistd.h> #include <error.h> #define BUF_SIZE 100 int main(int argc, char **argv) { int fd, nread, i; struct stat sb; char *mapped, buf[BUF_SIZE]; for (i = 0; i < BUF_SIZE; i++) { buf[i] = '#'; } if ((fd = open(argv[1], O_RDWR)) < 0) { perror("open"); } if ((fstat(fd, &sb)) == -1) { perror("fstat"); } if ((mapped = (char *)mmap(NULL, sb.st_size, PROT_READ |PROT_WRITE, MAP_SHARED, fd, 0)) == (void *)-1) { perror("mmap"); } close(fd); printf("%s", mapped); mapped[20] = '9'; if ((msync((void *)mapped, sb.st_size, MS_SYNC)) == -1) { perror("msync"); } if ((munmap((void *)mapped, sb.st_size)) == -1) { perror("munmap"); } return 0; }
参考:
http://blog.csdn.net/chdhust/article/details/8159397
http://www.cnblogs.com/Anker/archive/2013/01/16/2862800.html