参考: https://www.cnblogs.com/charlesblc/p/6263665.html
mmap使用细节
1、使用mmap需要注意的一个关键点是,mmap映射区域大小必须是物理页大小(page_size)的整倍数(32位系统中通常是4k字节)。原因是,内存的最小粒度是页,而进程虚拟地址空间和内存的映射也是以页为单位。为了匹配内存的操作,mmap从磁盘到虚拟地址空间的映射也必须是页。
2、内核可以跟踪被内存映射的底层对象(文件)的大小,进程可以合法的访问在当前文件大小以内又在内存映射区以内的那些字节。也就是说,如果文件的大小一直在扩张,只要在映射区域范围内的数据,进程都可以合法得到,这和映射建立时文件的大小无关。具体情形参见“情形三”。
3、映射建立之后,即使文件关闭,映射依然存在。因为映射的是磁盘的地址,不是文件本身,和文件句柄无关。同时可用于进程间通信的有效地址空间不完全受限于被映射文件的大小,因为是按页映射。
缺点如下:
-
文件如果很小,是小于4096字节的,比如10字节,由于内存的最小粒度是页,而进程虚拟地址空间和内存的映射也是以页为单位。虽然被映射的文件只有10字节,但是对应到进程虚拟地址区域的大小需要满足整页大小,因此mmap函数执行后,实际映射到虚拟内存区域的是4096个字节,11~4096的字节部分用零填充。因此如果连续mmap小文件,会浪费内存空间。
-
对变长文件不适合,文件无法完成拓展,因为mmap到内存的时候,你所能够操作的范围就确定了。
-
如果更新文件的操作很多,会触发大量的脏页回写及由此引发的随机IO上。所以在随机写很多的情况下,mmap方式在效率上不一定会比带缓冲区的一般写快。
解决办法
fileSize = lseek( srcFd, 0, SEEK_END );
lseek( srcFd, 0, SEEK_SET );
lseek( destFd, fileSize-1, SEEK_SET );//下面write的时候多了一个字符,这里减去
write( destFd, "", 1);//cannot access memory, 扩充为真实大小
char *destMmap = (char *)mmap( NULL, fileSize, PROT_READ|PROT_WRITE, MAP_SHARED, destFd, 0);
if( destMmap == MAP_FAILED ){
fprintf( stderr, "mmap error : %s.\\n", strerror(errno));
return SHARE_MEM_WRONG;
}