内存映射的作用是使一个磁盘文件与存储空间的一个缓冲区建立映射关系,然后当从缓冲区中取数据,就相当于读文件中的相应字节,而将数据存入缓冲区,就相当于写文件中的相应字节。这样就可以不使用read和write直接执行I/O了。参考:内存映射(memory map)
#include <sys/mman.h>
void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);
功能:将一个文件或者设备的数据映射到内存中
参数:
-addr:内存中映射的首地址,一般设置为NULL,表示由内核指定
-length:要映射的数据的长度,这个值不能为0,建议使用文件的长度(如果这个长度不是分页的整数倍,会自动取比该长度大的分页的整数倍)
获取文件的长度可以使用stat函数或者lseek函数
-prot:对申请的内存映射区的操作权限
- PROT_EXEC:可执行的权限
- PROT_READ:读权限 要操作映射内存,必须要有读的权限
- PROT_WRITE:写权限
- PROT_NONE:没有权限
-flags:
- MAP-SHARED:映射区的数据会自动和磁盘文件进行同步,进程间通信,必须设置这个选项
- MAP-PRIVATE:不同步,内存映射区的数据改变了,对原来的映射区不会修改,会重新创建一个映射区
个新的映射区。(copy on write)
-fd:需要映射的那个文件的文件描述符,通过open得到,open的是一个磁盘文件
注意:文件的大小不能为0,open指定的权限不能和prot参数有冲突
prot:PROT_READ open:只读/读写
prot:PROT_READ | PROT_WRITE open:读写
prot的权限要小于等于open中的权限,并且prot必须有读的权限
-offset:偏移量,一般不用。必须指定的是4K的整数倍。0表示不偏移,从文件的开头进行映射
返回值:返回创建的内存的首地址
失败返回MAP_FAILED,(void*)-1
int munmap(void *addr, size_t length);
功能:释放内存映射
参数:
-addr:要释放的内存的首地址
-length:要释放的内存的大小,要和mmap的length值一样。
返回值:
成功返回0,失败返回-1
使用内存映射实现进程间通信:
1.有关系的进程(父子进程)
还没有子进程的时候,通过唯一的父进程,先创建内存映射区
有了内存映射区以后,创建子进程
父子进程共享创建的内存映射区
2.没有关系的进程间通信
准备一个大小不是0的磁盘文件
进程1通过磁盘文件创建内存映射区,得到一个操作这块内存的指针
进程2通过磁盘文件创建内存映射区,得到一个操作这块内存的指针
使用内存映射区通信
注意:内存映射区通信,是非阻塞的
内存映射实现进程间通信:是把同一个文件映射到不同的进程中,也就是说,不同的进程共享的是文件
共享内存实现进程间通信:不同的进程共享的是内存,进程间通信效率最高的一种方式
内存映射实现进程通信的示意图:
内存映射需要不同的进程关联同一个文件,通过文件做进程间通信
共享内存实现进程通信的示意图:
1.使用内存映射实现父子进程间通信的实例:
先新建一个用于内存映射的文件mmap.txt,随便输入一些内容:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/mman.h>
#include <string.h>
#include <wait.h>
int main()
{
//1.打开文件
int fd=open("mmap.txt",O_RDWR); //以可读可写打开文件
if(fd==-1)
{
perror("open");
return -1;
}
//2.得到文件的大小
int size=lseek(fd,0,SEEK_END);
//3.进行文件映射
void *ptr=mmap(NULL,size,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
if(ptr==MAP_FAILED)
{
perror("mmap");
return -1;
}
//4.创建子进程
pid_t pid=fork();
if(pid==0)
{
//子进程 写数据
char buf[1024]="hello dad!";
strcpy((char*)ptr,buf); //直接对得到的指针进行操作,省去了write 和read的调用
//write(fd,buf,strlen(buf));
}
else if(pid>0)
{
//父进程 读数据
wait(NULL);
char buf[1024]="\0";
strcpy(buf,(char*)ptr);
//read(fd,buf,sizeof(buf));
printf("parent recv:%s\n",buf);
}
else
{
perror("fork");
return -1;
}
munmap(ptr,size); //6.释放内存映射
return 0;
}
执行结果:
再次查看mmap.txt文件中的内容:
2.使用内存映射实现没有关系的进程间通信:
新建一个用于内存映射的文件mmap_2.txt:
mmap_write.c:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/mman.h>
#include <string.h>
#include <wait.h>
int main()
{
int fd = open("mmap_2.txt", O_RDWR);
if (fd == -1)
{
perror("fork");
return -1;
}
int size = lseek(fd, 0, SEEK_END);
void *ptr = mmap(NULL, size, PROT_WRITE | PROT_READ, MAP_SHARED, fd, 0);
if (ptr == MAP_FAILED)
{
perror("mmap");
return -1;
}
strcpy((char *)ptr, "hello,I wrote this!");
munmap(ptr, size);
return 0;
}
mmap_read.c:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/mman.h>
#include <string.h>
#include <wait.h>
int main()
{
int fd = open("mmap_2.txt", O_RDWR);
if (fd == -1)
{
perror("fork");
return -1;
}
int size = lseek(fd, 0, SEEK_END);
void *ptr = mmap(NULL, size, PROT_WRITE | PROT_READ, MAP_SHARED, fd, 0);
if (ptr == MAP_FAILED)
{
perror("mmap");
return -1;
}
char buf[1024] = "\0";
strcpy(buf, (char *)ptr);
printf("%s\n",buf);
munmap(ptr, size);
return 0;
}
运行之后:
再次查看mmap_2.txt: