概述
第一部分:Linux内存映射mmap详解
函数原型:void* mmap(void* start,size_t length,int prot,int flags,int fd,off_t offset);
头文件:#include <sys/mman.h>
参数说明:
- start:指定文件应被映射到进程空间的起始地址,一般被指定一个空指针,此时选择起始地址的任务留给内核来完成。函数的返回值为最后文件映射到进程空间的地址,进程可直接操作起始地址为该值的有效地址
- length:是映射到调用进程地址空间的字节数.
- prot:参数指定共享内存的访问权限。可取如下几个值的或:PROT_READ(可读) , PROT_WRITE (可写), PROT_EXEC (可执行), PROT_NONE(不可访问)
- flags:由以下几个常值指定:MAP_SHARED , MAP_PRIVATE , MAP_FIXED,其中,MAP_SHARED , MAP_PRIVATE必选其一,而MAP_FIXED则不推荐使用
- fd:有效的文件描述词。一般是由open()函数返回,其值也可以设置为-1,此时需要指定flags参数中的MAP_ANON,表明进行的是匿名映射。
- offset:被映射对象内容的起点。
返回值: 成功执行时,mmap()返回被映射区的指针,失败时,mmap()返回MAP_FAILED[其值为(void *)-1]
- 虚拟地址:由被映射区的指针加上内存对齐的偏移量求得虚拟地址指针。获取虚拟地址后,可通过虚拟地址以内存读写的方式操作所映射的物理地址或文件
注意事项:
1. 使用完需要nt munmap(void* start,size_t length)函数释放
2.用于共享内存需要注意进程间读写数据的同步
3.mmap()必须以PAGE_SIZE为单位进行映射,而内存也只能以页为单位进行映射,若要映射非PAGE_SIZE整数倍的地址范围,要先进行内存对齐,强行以PAGE_SIZE的倍数大小进行映射。
- 映射文件一般从文件开头开始,不需要对齐。
- 查看PAGE_SIZE大小方法: 使用 sysconf 函数 或控制台 getconf 命令
- 内存对齐方法是计算被物理内存的修正起始地址offset:Physical address & ~(PAGE_SIZE-1) ,其中Physical address是要映射的物理起始地址
第二部分:mmap实现进程间通信-共享内存法
mmap操作提供了一种机制,让用户程序直接访问设备内存,这种机制,相比较在用户空间和内核空间互相拷贝数据,效率更高。在要求高性能的应用中比较常用。mmap()系统调用使得进程之间通过映射同一个普通文件实现共享内存。普通文件被映射到进程地址空间后,进程可以像访问普通内存一样对文件进行访问,不必再调用read(),write()等操作,效率可大大提升。
注:实际上,mmap()系统调用并不是完全为了用于共享内存而设计的。它本身提供了不同于一般对普通文件的访问方式,进程可以像读写内存一样对普通文件的操作。而Posix或System V的共享内存IPC则纯粹用于共享目的,当然mmap()实现共享内存也是其主要应用之一,下面介绍两种共享内存的方法。
(1)使用普通文件提供的内存映射:适用于任何进程之间;此时,需要打开或创建一个文件,然后再调用mmap();
典型调用代码如下:
fd=open(name, flag, mode);
if(fd<0)
...
ptr=mmap(NULL, len , PROT_READ|PROT_WRITE, MAP_SHARED , fd , 0);
通过mmap()实现共享内存的通信方式有许多特点和要注意的地方,我们将在范例中进行具体说明。
(2)使用特殊文件提供匿名内存映射:适用于具有亲缘关系的进程之间;由于父子进程特殊的亲缘关系,在父进程中先调用mmap(),然后调用fork()。那么在调用fork()之后,子进程继承父进程匿名映射后的地址空间,同样也继承mmap()返回的地址,这样,父子进程就可以通过映射区域进行通信了。注意,这里不是一般的继承关系。一般来说,子进程单独维护从父进程继承下来的一些变量。而mmap()返回的地址,却由父子进程共同维护。 对于具有亲缘关系的进程实现共享内存最好的方式应该是采用匿名内存映射的方式。此时,不必指定具体的文件,只要设置相应的标志即可。
代码示例:
/*-------------map_normalfile1.c-----------*/
#include<sys/mman.h>
#include<sys/types.h>
#include<fcntl.h>
#include<string.h>
#include<stdio.h>
#include<unistd.h>
#include <errno.h>
typedef struct{
char name[4];
int age;
}people;
void 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);
if (p_map == (void *)-1)
{
fprintf(stderr, "mmap: %sn", strerror(errno));
return ;
}
close(fd);
temp='a';
for(i=0;i<10;i++)
{
temp+=1;
(*(p_map+i)).name[1] = '