内存映射区
存储映射I/O (Memory-mapped I/O) 使一个磁盘文件与存储空间中的一个缓冲区相映射。从缓冲区中取数据,就相当于读文件中的相应字节;将数据写入缓冲区,则会将数据写入文件。这样,就可在不使用read和write函数的情况下,使用地址(指针)完成I/O操作。
使用存储映射这种方法,首先应通知内核,将一个指定文件映射到存储区域中。这个映射工作可以通过mmap函数来实现。
内存映射 mmap的理解
mmap()函数
函数作用
建立存储映射区
函数原型
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
返回值
成功:返回创建的映射区首地址
失败:MAP_FAILED宏
函数参数
- addr:指定映射的起始位置,通常设置成NULL,由系统指定
- length:映射到内存的文件长度
- prot:映射区的保护方式,最常用:
读:PROT_READ
写:PROT_WRITE
读写:PROT_READ | PROT_WRITE - flags:映射区的特性,可以是
MAP_SHARED:写入映射区的数据会写回文件,且允许其他映射该文件的进程共享
MAP_PRIVATE:对映射区的会产生一个映射区的复制(copy-on-write),对此区域所做的修改不会写回原文件。 - fd:由open()返回的文件描述符,代表要映射的文件
- offset:以文件开始处的偏移量,必须是4k的整数倍,通常为0,表示从文件头开始映射。
[注]mmap使用细节
1.mmap映射区域大小必须是物理页大小(page_size)的整倍数(32位系统中通常是4k字节)。原因是,内存的最小粒度是页,而进程虚拟地址空间和内存的映射也是以页为单位。为了匹配内存的操作,mmap从磁盘到虚拟地址空间的映射也必须是页。
2. 内核可以跟踪被内存映射的底层对象(文件)的大小,进程可以合法的访问在当前文件大小以内又在内存映射区以内的那些字节。也就是说,如果文件的大小一直在扩张,只要在映射区域范围内的数据,进程都可以合法得到,这和映射建立时文件的大小无关。具体情形参见“情形三”。
3. 映射建立之后,即使文件关闭,映射依然存在。因为映射的是磁盘的地址,不是文件本身,和文件句柄无关。同时可用于进程间通信的有效地址空间不完全受限于被映射文件的大小,因为是按页映射。
//使用mmap函数完成父子进程间通信
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/mman.h>
int main()
{
//使用mmap函数建立共享映射区
//void *mmap(void *addr, size_t length, int prot, int flags,
// int fd, off_t offset);
int fd = open("./test.log", O_RDWR);
if(fd<0)
{
perror("open error");
return -1;
}
int len = lseek(fd, 0, SEEK_END);
void * addr = mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);//
//void * addr = mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
if(addr==MAP_FAILED)
{
perror("mmap error");
return -1;
}
close(fd);
//创建子进程
pid_t pid = fork();
if(pid<0)
{
perror("fork error");
return -1;
}
else if(pid>0)
{
memcpy(addr, "hello world", strlen("hello world"));
wait(NULL);
}
else if(pid==0)
{
sleep(1);
char *p = (char *)addr;
printf("[%s]", p);
}
return 0;
}
程序运行之后