进程间通信之共享内存-系统调用mmap详解(基础篇)

        IPC-进程间通信之共享内存-系统调用mmap详解(基础篇)
(一) 系统调用和System V共享内存介绍
管道和消息队列等通信方式,需要在内核和用户空间进行四次的数据拷贝;而共享内存只拷贝两次数据,一次从输入文件到共享内存区,另一次从共享内存区到输出文件; 进程之间在共享内存时,一直保持共享区域,直到通信完毕为止,这样,数据内容一直保存在共享内存中,并没有写回文件。共享内存中的内容往往是在解除映射时才写回文件的。因此,采用共享内存的通信方式效率是非常高的。 Linux内核支持多种共享内存方式,如mmap()系统调用,Posix共享内存,以及System V共享内存。linux发行版本支持mmap()系统调用及System V共享内存,但还没实现Posix共享内存.

(二)系统调用mmap()
  系统调用mmap()使得进程之间通过映射同一个普通磁盘文件实现共享内存。如果采用MAP_SHARE共享内存,则对映射区域的写入数据会复制回磁盘文件内,而且允许其他映射该文件的进程共享, 原来的文件会改变。
  mmap最大的优势,就是映射文件到进程后,可以直接操作这段虚拟地址进行文件的读写操作,如常通过strxxx,memxxx函数完成,不必再调用read,write等系统调用.但需注意,直接对该段内存写时不会写入超过当前文件大小的内容.
函数原型
void* mmap ( void * addr , size_t len , int prot , int flags , int fd , off_t offset ) ;
函数入参:
  fd:即将映射到进程空间的文件描述符,可见要用fd,必须在open一个磁盘文件返回,同时,fd可以指定为-1,此时须指定flags参数中的MAP_ANON,表明进行的是匿名映射(不涉及具体的文件名,避免了文件的创建及打开,很显然只能用于具有亲缘关系的进程间通信);
  len:映射到进程地址空间的字节数,它从被映射磁盘文件开头offset个字节开始算起;
  prot:指定共享内存的访问权限。可取如下几个值的或:PROT_READ(页可读) , PROT_WRITE (页可写), PROT_EXEC (页可执行), PROT_NONE(页不可访问),默认页大小4K,所以len最好为4K整数倍,并且要注意与磁盘文件权限的匹配;
flag:由以下几个常值指定:MAP_SHARED , MAP_PRIVATE , MAP_FIXED,其中,MAP_SHARED , MAP_PRIVATE必选其一,而MAP_FIXED则不推荐使用
 offset: 一般设为0,表示从文件头开始映射;
 addr: 指定文件应被映射到进程空间的起始地址,一般被指定一个空指针,此时选择起始地址的任务留给内核来完成。
函数返回值:
  最后文件映射到进程空间的地址;

int munmap(void *addr, size_t length);
该调用在进程地址空间中解除一个映射关系,并将内存中数据写入到磁盘文件中。当映射关系解除后,对原来映射地址的访问将导致段错误发生。
函数入参:
addr是调用mmap()时返回的地址;
length是映射区的大小。
函数返回值:
  成功返回0,出错返回-1。

int msync ( void * addr , size_t len, int flags) ;
调用msync()实现磁盘上文件内容与共享内存区的内容一致;
函数入参:
addr是调用mmap()时返回的地址;
length是映射区的大小。
Flags:写回动作设置
  MS_ASYNC : 异步写,Kernel立即将资料写入返回。
  MS_SYNC : 同步写,在msync结束返回前,将资料写入。
  MS_INVALIDATE : 让核心自行决定是否写入,仅在特殊状况下使用。
函数返回值:
  成功返回0,出错返回-1。
http://man7.org/linux/man-pages/man2/mmap.2.html
进程间通信之共享内存-系统调用mmap详解(基础篇)_第1张图片
进程间通信之共享内存-系统调用mmap详解(基础篇)_第2张图片
(三) 应用实例
范例1: 通过共享映射的方式修改文件
  创建一个磁盘文件hellowrold,内容为helloworld!,然后分别使用msync和munmap函数,修改此磁盘文件。
源代码:

#include 
#include 
#include 
int main(void)
{
    int *p;
    int fd = open("helloworld", O_RDWR);
    if (fd < 0) {
        perror("open");
        exit(1);
    }
    p = mmap(NULL,4096, PROT_WRITE, MAP_SHARED, fd, 0);
    if (p == MAP_FAILED) {
        perror("mmap");
        exit(1);
    }
    close(fd);
    /* 两种方式实现修改 大家可以体会一下 */
    p[0] = 0x30313233;
    //if ((msync((void *)p, 4096, MS_SYNC)) == -1) {  
    //    perror("msync");  
    //} 
    munmap(p, 4096);
    return 0;
}

调试:
进程间通信之共享内存-系统调用mmap详解(基础篇)_第3张图片
总结:
1.映射磁盘文件到进程后,就可以直接操作这段虚拟地址进行文件的读写操作;
2.所有对mmap()返回地址空间的操作 只在内存中有意义,只有在调用了munmap()后或者msync()时,才把内存中的相应内容写回磁盘文件,所写内容仍然不能超过文件的大小

你可能感兴趣的:(Linux编程开发)