内存映射
内存映射,简而言之就是将用户空间的一段内存区域映射到内核空间,映射成功后,用户对这段内存区域的修改可以直接反映到内核空间,同样,内核空间对这段区域的修改也直接反映用户空间。那么对于内核空间<---->用户空间两者之间需要大量数据传输等操作的话效率是非常高的。
因为mmap系统调用使得进程之间通过映射同一个普通文件实现共享内存。普通文件被映射到进程地址空间后,进程可以像访问普通内存一样对文件进行访问,不必再调用read(),write()等操作。
映射需要包含头文件 #include<sys/mman.h>
映射函数 void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
接触映射函数 int munmap(void *addr, size_t length);
参数代表的意义
第一个参数void *addr:指定被映射到进程地址空间的起始地址,一般指定为NULL,表示让内核自己选择。
第二个参数size_t length:需要映射的大小(字节)。
第三个参数int prot:数据的访问权限,一般PROT_READ|PROT_WRITE表示可读写。
第四个参数int flags:一般指定为MAP_SHARED
第五个参数int fd:映射的文件描述符
第六个参数off_t offset:从文件的什么位置开始映射,一般指定为0。
下面我来写一个映射的程序:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<sys/mman.h>
void sys_err(char *s)
{
perror(s);
exit(1);
}
int main(int argc,char** argv)
{
//判断传入的参数个数是否大于等于2
if(argc < 2)
{
printf("usage: mmap1 filename\n");
return 1;
}
//创建一个fd用来接收文件描述符,即第二个参数,也是要被操作的文件
int fd;
fd = open(argv[1],O_RDWR);//argv[1]就是第二个参数
if(fd < 0)
{
sys_err("open");
}
//获得文件长度
off_t len;
len = lseek(fd,0,SEEK_END);
//将文件映射到当前进程,mem代表映射后内存的首地址
void* mem = mem = mmap(NULL,len,PROT_WRITE,MAP_SHARED,fd,0);
//#define MAP_FAILED ((void*)-1)
if(mem == MAP_FAILED)
{
sys_err("mmap");
}
//关闭文件描述
close(fd);
//用映射后的地址输出文件内容
printf("%s\n",mem);
//通过映射来修改文件
*(char*)mem = 'N';
*(char*)(mem + 1) = 'I';
((char*)mem)[2]='H';
((char*)mem)[3]='A';
//解除映射
if(-1 == munmap(mem,len))
{
sys_err("munmap");
}
return 0;
}
执行结果为:
成功的通过映射对文件进行修改!
注意:
mmap并不分配空间, 只是将文件映射到调用进程的地址空间里(但是会占掉你的 virutal memory)