存储映射I/O能将一个磁盘文件映射到存储空间中的一个缓冲区上,
从缓冲区读数据就相当于读文件中的相应字节,将数据写入缓冲区,就相当于对文件写入相应字节
这样,就可在不适用read和write函数的情况下,使用地址(指针)完成I/O操作。
存储映射函数
创建:
#include<sys/mman.h>
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
功能:
一个文件或者其它对象映射进内存
参数:
- addr : 指定映射的起始地址, 通常设为NULL, 由系统指定
- length:映射到内存的文件长度
- prot: 映射区的保护方式, 最常用的 :
- a) 读:PROT_READ
- b) 写:PROT_WRITE
- c) 读写:PROT_READ | PROT_WRITE
flags: 映射区的特性, 可以是
- a) MAP_SHARED : 写入映射区的数据会复制回文件, 且允许其他映射该文件的进程共享。
- b) MAP_PRIVATE : 对映射区的写入操作会产生一个映射区的复制(copy - on - write), 对此区域所做的修改不会写回原文件。
fd:由open返回的文件描述符, 代表要映射的文件。
offset:以文件开始处的偏移量, 必须是4k的整数倍, 通常为0, 表示从文件头开始映射
返回值:
成功:返回创建的映射区首地址
失败:MAP_FAILED宏
解除映射
#include <sys/mman.h>
int munmap(void *addr, size_t length);
功能:
释放内存映射区
参数:
addr:使用mmap函数创建的映射区的首地址
length:映射区的大小
返回值:
成功:0
失败:-1
#include<stdio.h>
#include<sys/mman.h>
#include<unistd.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<fcntl.h>
#include<string.h>
int main()
{
int fd=open("txt",O_RDWR);
if(fd==-1)
{
perror("open");
return 1;
}
void *addr=NULL;
addr=mmap(addr,1000,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
if(addr==MAP_FAILED)
{
perror("mmap");
return 1;
}
printf("创建映射成功............\n");
//向文件写数据
strcpy((char*)addr,"this is ret");
int ret=munmap(addr,1000);
if(ret==-1)
{
perror("munmap");
return 1;
}
return 0;
}
注意事项:
- 创建映射区的过程,隐含一次对映射文件的读操作
- 当MAP_SHAREAD,要求:映射区的权限应<=文件打开的权限(出于对映射区的保护).
- 映射区的释放对文件关闭无关,只要建立成功文件.可以立即关闭
- 特别注意:当映射文件大小为0时,不能创建映射区,所以,用于映射的文件必须要有实际大小mmap使用时长会出现总线错误,通常是由于共享文件存储空间大小引起的
- 文件偏移量必须为4k的整数倍(最后一个参数偏移量必须是4K的整数倍,这是操作系统的限制,文件最小门槛就是8个512,也就是4096。如果一个文件本身大小没有达到4K,那么它占用的空间大小也是4096。比如下面mem.txt实际上只有10字节大小,但是占据了8个512,这就证明文件最小占据4K空间。)
使用mmap实现父子进程通信
#include<stdio.h>
#include<unistd.h>
#include<sys/mman.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdlib.h>
#include<string.h>
#include<sys/wait.h>
int main()
{
int fd=open("txt",O_RDWR);
if(fd==-1)
{
perror("open");
return 1;
}
//存储映射
void *addr=NULL;
addr=mmap(addr,10,PROT_WRITE|PROT_READ,MAP_SHARED,fd,0);
if(addr==MAP_FAILED)
{
perror("mmap");
return 1;
}
//创建父子进程
pid_t pid=-1;
pid=fork();
if(pid==0)
{
sleep(2);
printf("%s\n",(char*)addr);
}
else{
//parent
strcpy((char*)addr,"123456789");
wait(NULL);
}
int ret= munmap(addr,10);
if(ret==-1)
{
perror("munmap");
return 1;
}
return 0;
}
匿名映射实现父子进程通信
无需依赖一个文件即可创建映射区。同样需要借助标志位参数flags来指定。
使用MAP_ANONYMOUS (或MAP_ANON)。
int *p = mmap(NULL, 4, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
- 4"随意举例,该位置表示映射区大小,可依实际需要填写。
- MAP_ANONYMOUS和MAP_ANON这两个宏是Linux操作系统特有的宏。在类Unix系统中如无该宏定义,可使用如下两步来完成匿名映射区的建立。
#include<stdio.h>
#include<string.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<unistd.h>
#include<stdlib.h>
#include<fcntl.h>
#include<sys/mman.h>
int main()
{
void *addr=NULL;
addr= mmap(addr,10,PROT_READ|PROT_WRITE,MAP_ANON|MAP_SHARED,-1,0);
if(addr==MAP_FAILED)
{
perror("mmap");
return 1;
}
pid_t pid =-1;
pid=fork();
if(pid==0)
{
//child
sleep(2);
printf("%s\n",(char*)addr);
}
else
{
strcpy((char*)addr,"123456789w");
wait(NULL);
}
int ret=munmap(addr,10);
if(ret<0)
{
perror("munmap");
return 1;
}
return 0;
}