函数mmap()的使用
函数mmap是linux的一个系统函数。如下:
函数原型:void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);
配套函数原型:int munmap(void *addr, size_t length);
头文件:#include <sys/mman.h>
返回值:成功返回创建的映射区的首地址;失败返回宏MAP_FAILED。
参数介绍:
addr: 建立映射区的首地址,由Linux内核指定。使用时,直接传递NULL。
length: 欲创建映射区的大小。
prot: 映射区权限PROT_READ、PROT_WRITE、PROT_READ|PROT_WRITE。
flags: 标志位参数(常用于设定更新物理区域、设置共享、创建匿名映射区);
MAP_SHARED: 会将映射区所做的操作反映到物理设备(磁盘)上。
MAP_PRIVATE: 映射区所做的修改不会反映到物理设备。
fd: 用来建立映射区的文件描述符。
offset: 映射文件的偏移(4k的整数倍)。
munmap函数:
同malloc函数申请内存空间类似的,mmap建立的映射区在使用结束后也应调用类似free的函数来释放。
返回值:成功:0; 失败:-1
接下来来使用mamp函数创建一个映射区:代码如下
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
void sys_err(char p[])//处理错误
{
perror(p);
exit(1);
}
int main()
{
int o_ret = open("my_mmap.txt", O_CREAT | O_RDWR, 0644);//创建一个新文件
if (-1 == o_ret) //错误检查是一个很好的习惯
{
sys_err("open ");
}
int f_ret = ftruncate(o_ret, 512);//在此文件大小的基础上扩展512字节大小,即文件现有大小为512字节(是个好函数,该记着。)
if (-1 == f_ret)//成功返回0
{
sys_err("ftruncate ");
}
//———————————————————————————————————————————
struct stat statbuf;
int s_ret = stat("my_mmap.txt", &statbuf);
if (-1 == s_ret)
{
sys_err("stat ");
}
//———————————————————————————————————————————
/*
上面这段代码是我用来获取文件大小的方法,并没有一个系统函数能够直接获取文件的大小。所以我利用stat结构体存储文件的相关信息,然后获取到文件的大小:statbuf.st_size
*/
char *const address = (char *)mmap(NULL, statbuf.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, o_ret, 0);
//这里注意,我写的是char *const address这保证了address的值不变。
if (MAP_FAILED == address)//若是mmap函数调用失败
{
sys_err("mmap ");
}
strcpy(address, "hallo, mmap");//能做这个操作是有先决条件的:映射区拥有写权限。
int c_ret = close(o_ret);
if (-1 == c_ret)
{
sys_err("close ");
}
int mun_ret = munmap(address, statbuf.st_size);//释放掉映射区。
if (-1 == mun_ret)
{
sys_err("munmap ");
}
//printf("hello from mmap_test!\n");
return 0;
}
很简单,这段代码编译之后就可以良好的运行下去。现在,我们来思考几个问题:
可以open的时候O_CREAT一个新文件来创建映射区吗?
如果open时O_RDONLY, mmap时PROT参数指定PROT_READ|PROT_WRITE会怎样?
文件描述符先关闭,对mmap映射有没有影响?
若是length参数大于文件的大小会怎样?
对mmap函数的返回值进行越界操作(++,–)会怎样?
如果文件偏移量为1000会怎样?
如果不检测mmap的返回值,会怎样?
答案:
可以,只要是正确的文件描述符传递给mmap都行
O_RDONLY代表只读,PROT_READ|PROT_WRITE代表读写,通过测试,我们就知道这是不行的,文件的权限代表着能对文件执行的操作;这里有一个结论:当第四个参数为MAP_SHARED时,映射区的权限应小于等于文件打开的权限。这样做的原因是出于对映射区的保护。而当第四个参数为MAP_PRIVATE时,就无所谓了,因为mmap中的权限时对内存的限制(映射区时在缓存区中创建的,缓存区是由内核管理调动的,不属于内存区域)。
只要mmap调用成功,文件就可以立即关闭,映射区的释放、操作与文件的关闭无关,因为映射区创建成功后时通过另一种方式(指针)来管理的。
注意:当用于创建映射区的文件大小为0时,不能创建映射区,会提示总线错误。所以:用于创建映射区的文件必须有实际的文件大小,并且创建的映射区大小不能超过文件大小。当出现总线错误的时候,多半是由于共享文件储存空间大小不正确引起的。
会造成调用munmap函数失败,不能释放映射区。所以推荐使用const关键字限定指针的值。
会出错,前面说过,文件的偏移量必须是4096(4k)的整数倍。
会死的很难看,因为使用mmap函数出错的几率很高。
在补充一下:
p = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);看到标颜色的字词了吗?其中MAP_ANON是MAP_ANONYMOUS(MAP_ANONYMOUS //匿名映射,映射区不与任何文件关联。)的别称,不再被使用。因为我们创建一个匿名映射,所以我们不需要传递文件描述符。