说明:内容来自APUE第2版中文版和相关函数的man手册。
Memory-mapped I/O
存储映射I/O使一个磁盘文件与存储空间中的一个缓冲区相映射。对缓冲区的操作,相当于对文件的操作。从缓冲区取数据,相当于读文件中的相应文件;将数据写入缓冲区,则数据自动地写入文件。这样就可以在不使用read和write的情况下执行I/O。
mmap
为了使用这一功能,应首先告诉内核将一个文件映射到一个存储区域中。这一任务由mmap函数实现,其函数原型与所在头文件如下:
#include <sys/mman.h>
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
void *mmap64(void *addr, size_t length, int prot, int flags, int fd, off64_t offset);
// 成功,返回映射区域的起始地址;失败,返回MAP_FAILED[(void *) -1],并设置errno。
mmap在虚拟地址空间中创建一个映射。
函数mmap() 与函数 mmap64() 的区别在最后一个参数,可以使应用程序访问更大的文件。
每个参数及含义如下:
参数 | 描述 |
---|---|
addr | 映射存储区的起始地址。 |
length | 存储区长度,单位:字节。 |
prot | 对映射存储区的保护要求。 |
flags | 确定映射的更新对映射相同区域的其他进程是否可见,以及是否对文件执行更新。 |
fd | 要映射文件的文件描述符。 |
offset | 要映射字节在文件中的起始偏移量。 |
相关参数的详细信息:
addr: 通常设置为0。
addr | 描述 |
---|---|
0(NULL) | 由内核选择该映射区的起始地址。 |
非空 | 若未设置MAP_FIXED,内核将此视作在何处开辟映射区的一个建议,并不一定使用它,Linux将在页边界附近创建映射。 若设置了MAP_FIXED,则在指定的位置开辟映射区。 |
prot: 指定映射存储区的保护要求不能超过文件open模式访问权限,若文件是使用O_RDONLY打开的,那么对映射存储区就不能指定PROT_WRITE,否则,对映射区执行写操作时,将产生SIGSEGV信号,发生Segment Fault。prot的取值如下表,可取任意一个或任意组合的按位或。
prot | 描述 |
---|---|
PROT_EXEC | 映射区可执行。 |
PROT_READ | 映射区可读。 |
PROT_WRITE | 映射区可写。 |
PROT_NONE | 映射区不可访问。 |
flags: 其值通常为MAP_SHARED。其他常用值如下表:
flags | 描述 |
---|---|
MAP_SHARED | 共享存储区。存储操作将修改映射文件,即此映射区的存储操作相当于对该文件的写操作。 调用msync() 或 munmap()之前,文件可能并未被更新。 |
MAP_PRIVATE | 对映射区的存储操作导致创建该映射文件的一个私有副本。(Create a private copy-on-write mapping.) 所有后来对该映射区的引用都是引用该副本,而不是原始文件。 |
MAP_FIXED | 返回值必须等于addr,即在指定的addr处开辟映射区。移植性差,不鼓励使用此标志。 |
还有其他的一些标志,详见mmap手册。
MAP_SHARED和MAP_PRIVATE是POSIX所要求的。在遵循POSIX的系统中,对MAP_FIXED的支持是可选的,遵循XSI的系统则要求支持此标志。
addr 和 offset通常应该是系统虚拟页长度的倍数。虚拟页长可使用带参数 _SC_PAGESIZE 或 _SC_PAGE_SIZE 的 sysconf 函数获取。addr 和 offset 通常设置为0,所以这种要求一般并不重要。
mprotect
创建映射存储区后,其保护权限可以通过 mprotect() 函数来更改。其原型如下:
int mprotect(const void *addr, size_t len, int prot);
// 成功,返回0;失败,返回-1,并设置errno。
参数及意义同函数 mmap() 。
msync
此函数可以将映射存储区中被修改的内容冲洗到被映射的文件中。其原型如下:
int msync(void *addr, size_t length, int flags);
// 成功,返回0;失败,返回-1,并设置errno。
如果映射是私有的,那么不修改被映射的文件。
flags参数使我们对如何冲洗存储区有某种程度的控制。其可用取值如下:
flags | 描述 |
---|---|
MS_ASYNC | 设定了更新计划,函数调用立即返回。简化被写页的调度。 |
MS_SYNC | 在此函数返回前等待写操作完成。 |
MS_INVALIDATE | 可选项。要求使同一文件的其他映射无效。 |
可以使用按位或的方式使用这些值,但不能同时指定 MS_ASYNC 与 MS_SYNC 。
munmap
要解除映射,一是终止进程,二是调用 munmap() 函数,关闭文件描述符并不解除映射区。munmap() 函数的原型如下:
int munmap(void *addr, size_t length);
// 成功,返回0;失败,返回-1,并设置errno。
相关示例可以查看mmap的手册。
将一个普通文件复制到另一个普通文件中时,存储映射I/O比较快。但是有一些限制,例如,不能用其在某些设备(如网络设备或终端设备)之间进行复制,并且在对被复制的文件进行映射后,也要注意该文件的长度是否改变。尽管如此,某些应用程序会从存储映射I/O得到好处,因为它处理的是存储空间而不是读、写一个文件,所以常常可以简化算法。从存储映射I/O中得益的一个例子是对帧缓冲区设备的操作,该设备引用一个位图式显示(bitmapped display)。