存储映射I/O 介绍
存储映射IO(Memory-mapped I/O) 使用一个磁盘文件与存储空间的一个缓冲区相映射。于是当缓冲区中取出数据,就相当于读文件中的相应字节。于此类似。间数据存入缓冲区,则相应的字节就会自动写入文件。因为数据已经写入文件,即使机器重启数据还在。
TIM截图20190517162610.png
mmap函数
使用这种方法,首先应通知内核,间一个指定文件映射到存储区域中。可以通过mmap函数来实现。
函数原型 :
void *mmap(void *adrr, size_t length, int prot, int flags, int fd, off_t offset);
参数:
addr:建立映射区的首地址,有Linux内核指定。使用时传NULL即可
length:与创建映射取的大小
prot: 映射区权限PROT_READ、PROT_WRITE、PROT_READ|PROT_WRITE
flags:标志位参数(常用于设定更新物理区域、设置共享、创建匿名映射区)
MAP_SHARED: 会将映射区所做的操作反映到物理设备(磁盘)上。
MAP_PRIVATE: 映射区所做的修改不会反映到物理设备。
fd: 用来建立映射区的文件描述符
offset: 映射文件的偏移(4k的整数倍)
munmap函数
当不需要这个映射内存时通过munmap函数来释放
函数原型:
int munmap(void *addr, size_t length);
参数:
addr :mmap的返回值
length:释放区域的长度
示例代码
mem.txt文件
jjjjjjjj
mmap.c文件
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <string.h>
int main()
{
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <string.h>
int main()
{
int fd = open("mem.txt",O_RDWR);//创建并且截断文件
//int fd = open("mem.txt",O_RDWR|O_CREAT|O_TRUNC,0664);//创建并且截断文件
//创建映射区
char *mem = mmap(NULL,20,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
//char *mem = mmap(NULL,8,PROT_READ|PROT_WRITE,MAP_PRIVATE,fd,0);
if(mem == MAP_FAILED){
perror("mmap err");
return -1;
}
//拷贝数据
strcpy(mem,"helloworld");
// mem++;
//释放mmap
if(munmap(mem,20) < 0){
perror("munmap err");
}
close(fd);
return 0;
}
运行代码之后,发现mem.txt文件内容已经改变
[root@VM_0_11_centos linux]# cat mem.txt
helloworl[root@VM_0_11_centos linux]#
**读写例 **
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <sys/wait.h>
typedef struct _Student{
int sid;
char sname[20];
}Student;
int main(int argc,char *argv[])
{
//open file
int fd = open(argv[1],O_RDWR);
//mmap
int length = sizeof(Student);
Student *stu = mmap(NULL,length,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
if(stu == MAP_FAILED){
perror("mmap err");
return -1;
}
//read data
while(1){
printf("sid=%d,sname=%s\n",stu->sid,stu->sname);
sleep(1);
}
//close and munmap
munmap(stu,length);
close(fd);
return 0;
}
写示例
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <sys/wait.h>
typedef struct _Student{
int sid;
char sname[20];
}Student;
int main(int argc,char *argv[])
{
if(argc != 2){
printf("./a.out filename\n");
return -1;
}
// 1. open file
int fd = open(argv[1],O_RDWR|O_CREAT|O_TRUNC,0666);
int length = sizeof(Student);
ftruncate(fd,length);
// 2. mmap
Student * stu = mmap(NULL,length,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
if(stu == MAP_FAILED){
perror("mmap err");
return -1;
}
int num = 1;
// 3. 修改内存数据
while(1){
stu->sid = num;
sprintf(stu->sname,"xiaoming-%03d",num++);
sleep(1);//相当于没隔1s修改一次映射区的内容
}
// 4. 释放映射区和关闭文件描述符
munmap(stu,length);
close(fd);
return 0;
}
map的一些问题
1.如果对mem越界操作会怎样?
多出的数据会丢失,尽量避免这样的操作
2.如果文件描述符向关闭,对mmap映射有没有影响
没有影响
3.open的时候,可以新创建一个文件来创建映射区吗?
不可以,使用的文件大小不能为0
4.如果文件偏移随便填一个数会怎么样?
运行时会报错
匿名映射
通过使用我们发现,使用映射区来完成文件读写十分方便,赋值进程通信也比较容易。但是缺陷是,每次创建映射区一定要依赖一个文件才能实现。通常为了建立映射区要open一个temp文件,创建好了再unlink,close掉,比较麻烦。可以直接使用匿名映射来代替。Linux系统给我们提供了创建匿名映射区的方法,无需依赖一个文件,即可创建映射取。同样需要借助标志位参数flags来指定。
示例代码:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <sys/wait.h>
int main()
{
//修改创建的参数
int *mem = mmap(NULL,4,PROT_READ|PROT_WRITE,MAP_SHARED|MAP_ANON,-1,0);
if(mem == MAP_FAILED){
perror("mmap err");
return -1;
}
...
网友评论