提问:
- mmap是什么?有什么用?
- mmap的接口怎么使用?每个参数又有什么作用?
回答:
1.mmap是什么?有什么用?
- mmap将一个文件或者其它对象映射进内存。mmap操作提供了一种机制,让用户程序直接访问设备内存,这种机制,相比较在用户空间和内核空间互相拷贝数据,效率更高。在要求高性能的应用中比较常用。mmap映射内存必须是页面大小的整数倍,面向流的设备不能进行mmap,mmap的实现和硬件有关。
- 两个进程可以通过映射普通文件实现共享内存通信。
- 在网络下载中,可以通过映射整个文件,使文件减少一次从内核态到用户态的拷贝,从而加快传输的效率。
2.mmap的接口怎么使用?每个参数又有什么作用?
mmap的建立
#include<sys/mman.h>
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
mmap()系统调用在调用进程的虚拟地址空间中创建一个新的映射。
成功返回映射首地址,这地址分页对齐,失败返回MAP_FAILED(在Linux实现上等同于(void*)-1)。
参数addr指定了映射被放置的虚拟地址。首选做法是将addr指定为NULL,内核会为映射选择一个合适的地址。
将addr指定为非NULL,内核会将该参数值作为一个提示信息来处理。
参数length指定了映射字节数。如果length不是分页的整数倍,内核会以分页大小为单位建立映射。
因此,length实际上会被向上提升为分页大小的下一个倍数。
参数prot和参数flags在MMAP使用二中介绍。这里只介绍它们的用处。
参数prot是一个位掩码,指定了新内存映射上的保护信息。
参数flags是一个控制映射操作各个方面的选项的位掩码。只能选一个。
(匿名映射会忽略下面两个参数)
参数fd表示映射的文件的文件描述符。
参数offset指定了映射在文件中的起点,必须是系统分页大小的倍数。
解除映射区域:munmap()
#include<sys/mman.h>
int munmap(void *addr, size_t length);
成功返回0,失败返回-1.
参数addr是待解除映射地址范围的起始地址,它必须与一个分页边界对齐。
参数length是一个非负整数,指定了待解除映射区域的大小(字节数)。
范围为系统分页大小的下一个倍数的地址空间将会被解除映射。
- 一般来讲,通常会解除整个映射。因此可以将addr指定为上一个mmap()调用返回的地址,并且length值与mmap()调用中使用的length值一样。
int fd = open(fileName, O_RDWR | O_CREAT, 0666);
if(-1 == fd)
{
perror("open");
return -1;
}
int length = 1;
char *addr = (char*)mmap(NULL, length, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
if(addr == MAP_FAILED)
{
perror("mmap");
return -1;
}
if(munmap(addr, length) == -1)
{
perror("munmap");
return -1;
}
同步映射区域:msync()
int msync(void *addr, size_t length, int flags);
让应用程序能够显式地控制何时完成共享映射与映射文件之间的同步。
这是非常有用的。如为确保数据完整性。
成功返回0,失败返回-1.
传给msync()的addr和length参数指定了需同步的内存区域的起始地址和大小。
在addr中指定的地址必须是分页对齐的,length会向上舍入到系统分页的下一个整数倍大小。
参数flags可取值为下列值中的一个:
MS_SYNC: 执行一个同步的文件写入。这个调用会阻塞直到内存区域中所有被修改过的分页被写入到底盘为止。
MS_ASYNC:执行一个异步的文件写入。内存区域中被修改过的分页会在后面某个时刻被写入磁盘并立即对在相应文件区域中执行read()
的其他进程可见。
flags参数中还可以加上下面这个值。
MS_INVALIDATE:使映射数据的缓存副本失效。
当内存区域中所有被修改过的分页被同步到文件中之后,内存区域中所有与底层文件不一致的分页会被标记为无效。
当下次引用这些分页会从文件的相应位置处复制相应的分页内容。
其结果是其他进程对文件作出的所有更新将会在内存区域中可见。
3.Mmap使用细节
- mmap中参数length大小可以超过文件大小,当然无论超不超过都会提升至系统页的整数倍大小。但是如果写入的内容超过了文件大小,超过部分不会写入文件。这时候就需要配合ftruncate或者lseek对文件进行偏移(相关概念:文件空洞)。代码如下:(文件在一开始只有6B的空间,如果不配合ftruncate或者lseek,后面"is null"不会写入文件中)