Linux进程间通信(IPC)编程实践(六)共享内存的使用-mmap

原创 2015年11月21日 14:46:21

共享内存区是最快的IPC形式。一旦这样的内存映射到共享它的进程的地址空间,这些进程间数据传递不再涉及到内核,换句话说是进程不再通过执行进入内核的系统调用来传递彼此的数据(如图)。


共享内存和其他进程间通信方式的比较:

用管道或者消息队列传递数据:


使用共享内存传递数据:


共享内存生成之后,传递数据并不需要再走Linux内核,共享内存允许两个或多个进程共享一个给定的存储区域,数据并不需要在多个进程之间进行复制,因此,共享内存的速度更快!

mmap函数及其相关系统调用

mmap()系统调用使得进程之间通过映射同一个普通文件实现共享内存。普通文件被映射到进程地址空间后,进程可以向访问普通内存一样对文件进行访问,不必再调用read(),write()等操作。

注:实际上,mmap()系统调用并不是完全为了用于共享内存而设计的。它本身提供了不同于一般对普通文件的访问方式,进程可以像读写内存一样对普通文件的操作。而Posix或系统V的共享内存IPC则纯粹用于共享目的,当然mmap()实现共享内存也是其主要应用之一。

#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);  

参数:

    addr:  要映射的起始地址, 通常指定为NULL, 让内核自动选择;

    length: 映射到进程地址空间的字节数;

    prot: 映射区保护方式(见下);

    flags: 标志(见下);

    fd: 文件描述符;

    offset: 从文件头开始的偏移量;

mmap返回值:

    成功: 返回映射到的内存区的起始地址;

    失败: 返回MAP_FAILED;


内存映射文件示意图:


注意: 内存映射时, 是以页面(4K)作为单位,也就是会对齐到页面的整数倍地址,映射区域至少是一个页面,哪怕len<一个页面。

lseek定位出一段空洞空间,最后写入\0。然后使用mmap进行映射,对文件的操作就相当于对内存的访问,结束之后用munmap解除映射。

(一)两个进程通过映射普通文件实现共享内存通信

范例1包含两个子程序:map_normalfile1.c及map_normalfile2.c。编译两个程序,可执行文件分别为map_normalfile1及map_normalfile2。两个程序通过命令行参数指定同一个文件来实现共享内存方式的进程间通信。map_normalfile2试图打开命令行参数指定的一个普通文件,把该文件映射到进程的地址空间,并对映射后的地址空间进行写操作。map_normalfile1把命令行参数指定的文件映射到进程地址空间,然后对映射后的地址空间执行读操作。这样,两个进程通过命令行参数指定同一个文件来实现共享内存方式的进程间通信。

下面是两个程序代码:

/*-------------map_normalfile1.c-----------*/  
#include <sys/mman.h>  
#include <sys/types.h>  
#include <fcntl.h>  
#include <unistd.h>  
typedef struct{  
    char name[4];  
    int  age;  
}people;  
main(int argc, char** argv) // map a normal file as shared mem:  
{  
    int fd,i;  
    people *p_map;  
    char temp;  
      
    fd=open(argv[1],O_CREAT|O_RDWR|O_TRUNC,00777);  
    lseek(fd,sizeof(people)*5-1,SEEK_SET);  
    write(fd,"",1);  
      
    p_map = (people*) mmap( NULL,sizeof(people)*10,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0 );  
    close( fd );  
    temp = 'a';  
    for(i=0; i<10; i++)  
    {  
        temp += 1;  
        memcpy( ( *(p_map+i) ).name, &temp,2 );  
        ( *(p_map+i) ).age = 20+i;  
    }  
    printf(" initialize over \n ");  
    sleep(10);  
    munmap( p_map, sizeof(people)*10 );  
    printf( "umap ok \n" );  
}  
/*-------------map_normalfile2.c-----------*/  
#include <sys/mman.h>  
#include <sys/types.h>  
#include <fcntl.h>  
#include <unistd.h>  
typedef struct{  
    char name[4];  
    int  age;  
}people;  
main(int argc, char** argv) // map a normal file as shared mem:  
{  
    int fd,i;  
    people *p_map;  
    fd=open( argv[1],O_CREAT|O_RDWR,00777 );  
    p_map = (people*)mmap(NULL,sizeof(people)*10,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);  
    for(i = 0;i<10;i++)  
    {  
    printf( "name: %s age %d;\n",(*(p_map+i)).name, (*(p_map+i)).age );  
    }  
    munmap( p_map,sizeof(people)*10 );  
}  

在map_normalfile1输出initialize over 之后,输出umap ok之前,在另一个终端上运行map_normalfile2 /tmp/test_shm,将会产生如下输出(为了节省空间,输出结果为稍作整理后的结果):

name: b	age 20;	name: c	age 21;	name: d	age 22;	name: e	age 23;	name: f	age 24;
name: g	age 25;	name: h	age 26;	name: I	age 27;	name: j	age 28;	name: k	age 29;

在map_normalfile1 输出umap ok后,运行map_normalfile2则输出如下结果:

name: b	age 20;	name: c	age 21;	name: d	age 22;	name: e	age 23;	name: f	age 24;
name:	age 0;	name:	age 0;	name:	age 0;	name:	age 0;	name:	age 0;

从程序的运行结果中可以得出的结论

1、 最终被映射文件的内容的长度不会超过文件本身的初始大小,即映射不能改变文件的大小;

2、 可以用于进程通信的有效地址空间大小大体上受限于被映射文件的大小,但不完全受限于文件大小。打开文件被截短为5个people结构大小,而在map_normalfile1中初始化了10个people数据结构,在恰当时候(map_normalfile1输出initialize over 之后,输出umap ok之前)调用map_normalfile2会发现map_normalfile2将输出全部10个people结构的值,后面将给出详细讨论。 
注:在linux中,内存的保护是以页为基本单位的,即使被映射文件只有一个字节大小,内核也会为映射分配一个页面大小的内存。当被映射文件小于一个页面大小时,进程可以对从mmap()返回地址开始的一个页面大小进行访问,而不会出错;但是,如果对一个页面以外的地址空间进行访问,则导致错误发生,后面将进一步描述。因此,可用于进程间通信的有效地址空间大小不会超过文件大小及一个页面大小的和。

3、 文件一旦被映射后,调用mmap()的进程对返回地址的访问是对某一内存区域的访问,暂时脱离了磁盘上文件的影响。所有对mmap()返回地址空间的操作只在内存中有意义,只有在调用了munmap()后或者msync()时,才把内存中的相应内容写回磁盘文件,所写内容仍然不能超过文件的大小。

(二)父子进程通过匿名映射实现共享内存(匿名映射只能是父子进程间)

#include <sys/mman.h>  
#include <sys/types.h>  
#include <fcntl.h>  
#include <unistd.h>  
typedef struct{  
    char name[4];  
    int  age;  
}people;  
main(int argc, char** argv)  
{  
    int i;  
    people *p_map;  
    char temp;  
    p_map=(people*)mmap(NULL,sizeof(people)*10,PROT_READ|PROT_WRITE,MAP_SHARED|MAP_ANONYMOUS,-1,0);  
    if(fork() == 0)  
    {  
        sleep(2);  
        for(i = 0;i<5;i++)  
            printf("child read: the %d people's age is %d\n",i+1,(*(p_map+i)).age);  
        (*p_map).age = 100;  
        munmap(p_map,sizeof(people)*10); //实际上,进程终止时,会自动解除映射。  
        exit();  
    }  
    temp = 'a';  
    for(i = 0;i<5;i++)  
    {  
        temp += 1;  
        memcpy((*(p_map+i)).name, &temp,2);  
        (*(p_map+i)).age=20+i;  
    }  
    sleep(5);  
    printf( "parent read: the first people,s age is %d\n",(*p_map).age );  
    printf("umap\n");  
    munmap( p_map,sizeof(people)*10 );  
    printf( "umap ok\n" );  
}  

考察程序的输出结果,体会父子进程匿名共享内存:

child read: the 1 people's age is 20
child read: the 2 people's age is 21
child read: the 3 people's age is 22
child read: the 4 people's age is 23
child read: the 5 people's age is 24
parent read: the first people,s age is 100
umap
umap ok

msync函数

  1. int msync(void *addr, size_t length, int flags);  

对映射的共享内存执行同步操作,内存同步到磁盘

参数:

    addr: 内存起始地址;

    length: 长度

    flags: 选项

flags

说明

MS_ASYNC

执行异步写

MS_SYNC

执行同步写, 直到内核将数据真正写入磁盘之后才返回

MS_INVALIDATE

使高速缓存的数据失效

返回值:

    成功: 返回0;

    失败: 返回-1;









版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

mmap映射区和shm共享内存的区别总结

linux中的两种共享内存。一种是我们的IPC通信System V版本的共享内存,另外的一种就是我们今天提到的存储映射I/O(mmap函数) 在说mmap之前我们先说一下普通的读写文件的原理,进...

linux网络编程之共享内存简介和mmap 函数

一、共享内存简介 共享内存区是最快的IPC形式,这些进程间数据传递不再涉及到内核,换句话说是进程不再通过执行进入内核的系统调用来传递彼此的数据。 即每个进程地址空间都有一个共享存储器的映射区,当...

共享内存mmap

mmap的是将文件指定的区域映射到内存中,操作内存

Linux进程间通信--mmap共享内存(一)

共享内存可以说是最有用的进程间通信方式,也是最快的IPC形式。两个不同进程A、B共享内存的意思是,同一块物理内存被映射到进程A、B各自的进程地址空间。进程A可以即时看到进程B对共享内存中数据的更新,反...

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

系统调用和System V共享内存 (一) 介绍 管道和消息队列等通信方式,需要在内核和用户空间进行四次的数据拷贝;而共享内存只拷贝两次数据,一次从输入文件到共享内存区,另一次从共享内存区...

[转] linux下使用mmap实现进程间共享内存

转自:http://www.rigongyizu.com/linux-threads-mmap-share-memory/ Linux下的mmap函数是把文件内容映射到一块内存(准确的说是虚拟内存)...

Linux进程间通信--mmap()共享内存(二)

内核怎样保证各个进程寻址到同一个共享内存区域的内存页面 1、page cache及swap cache中页面的区分:一个被访问文件的物理页面都驻留在page cache或swap cache中,一个页...

mmap共享内存

mmap系统调用使得进程之间通过映射同一个普通文件实现共享内存,但并不是完全为了用于共享内存而设计的。它本身提供了不同于一般对普通文件的访问方式,进程可以像读写内存一样对普通文件的操作。而Posix或...

Linux共享内存(mmap详解)

mmap()系统调用使得进程之间通过映射同一个普通文件实现共享内存。普通文件被映射到进程地址空间后,进程可以向访问普通内存一样对文件进行访问,不必再调用read(),write()等操作。
  • MJ813
  • MJ813
  • 2016-08-01 06:47
  • 1929

Linux进程间通信(IPC)编程实践(七)共享内存的使用-System V共享内存(API)

上一篇博文提到的系统调用mmap通过映射一个普通文件实现共享内存。那么本文中介绍的System V 共享内存则是通过映射特殊文件系统shm中的文件实现进程间的共享内存通信。也就是说,每个共享内存区域...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)