什么是mmap
mmap是一种系统调用,用于在进程地址空间和文件之间建立一个映射关系。通过mmap,进程可以将一个文件或其他对象的内容映射到它的地址空间中,从而实现对该文件或对象的访问。mmap可以提高文件读写的效率,并且可以在多个进程之间共享内存。在Linux系统中,mmap的实现是通过操作内核页表来完成的。
mmap是怎样提高读写效率的
mmap 可以提高文件读写效率的原因主要有两个:
-
避免了内核态和用户态之间的数据拷贝:在传统的文件读写中,文件数据需要从内核态的页缓存复制到用户态的缓冲区,或者反之。这个过程中需要频繁地进行上下文切换和数据拷贝,会消耗大量的 CPU 时间。而 mmap 将文件内容直接映射到进程的地址空间中,避免了数据拷贝,因此可以提高读写效率。
-
通过内存映射实现了文件的缓存:内存映射可以使文件数据直接缓存在内存中,避免了频繁地从磁盘读取数据,因此可以减少 I/O 操作的次数。此外,mmap 映射的文件内容会被分页存储在内存中,当需要访问某一页数据时,只需将该页从磁盘读入内存即可,而不必一次性将整个文件读入内存,这也可以减少 I/O 操作的次数,提高读写效率。
以下是使用 mmap 实现文件读写的示例代码:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#define FILESIZE (1024 * 1024)
int main(int argc, char *argv[])
{
int fd, offset;
char *data;
/* 打开文件 */
fd = open("file.txt", O_RDWR | O_CREAT, 0666);
if (fd == -1) {
perror("open");
exit(EXIT_FAILURE);
}
/* 拓展文件大小 */
if (ftruncate(fd, FILESIZE) == -1) {
perror("ftruncate");
exit(EXIT_FAILURE);
}
/* 将文件映射到内存中 */
data = mmap(NULL, FILESIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (data == MAP_FAILED) {
perror("mmap");
exit(EXIT_FAILURE);
}
/* 将数据写入文件 */
for (offset = 0; offset < FILESIZE; offset += 4096) {
sprintf(data + offset, "Hello, World!\n");
}
/* 同步数据到磁盘 */
if (msync(data, FILESIZE, MS_SYNC) == -1) {
perror("msync");
exit(EXIT_FAILURE);
}
/* 解除映射 */
if (munmap(data, FILESIZE) == -1) {
perror("munmap");
exit(EXIT_FAILURE);
}
/* 关闭文件 */
if (close(fd) == -1) {
perror("close");
exit(EXIT_FAILURE);
}
return 0;
}
上述代码实现了将一个大小为 1MB 的文件映射到内存中,并向其中写入 "Hello, World!\n" 字符串。下面是代码中各部分的解释:
-
打开文件:使用
open
系统调用打开一个名为file.txt
的文件,并指定读写权限。如果打开失败,输出错误信息并退出程序。 -
拓展文件大小:使用
ftruncate
系统调用将文件大小设置为 1MB。如果操作失败,输出错误信息并退出程序。 -
将文件映射到内存中:使用
mmap
系统调用将文件fd
映射到进程的地址空间中,并返回一个指向映射区域的指针data
。NULL
表示让内核自动选择映射地址,MAP_SHARED
表示映射区域可以被其他进程共享,PROT_READ | PROT_WRITE
表示映射区域可读可写。如果操作失败,输出错误信息并退出程序。 -
将数据写入文件:使用循环将字符串 "Hello, World!\n" 写入映射区域中。
-
同步数据到磁盘:使用
msync
系统调用将映射区域的内容同步到文件中。MS_SYNC
表示要求同步所有被修改的页,直到同步完成才返回。 -
解除映射:使用
munmap
系统调用将映射区域解除映射。如果操作失败,输出错误信息并退出程序。 -
关闭文件:使用
close
系统调用关闭文件。如果操作失败,输出错误信息并退出程序。
上述代码中,使用 mmap
将文件映射到内存中,避免了数据拷贝的过程,从而提高了文件读写的效率。同时,映射区域还可以缓存文件数据,避免了频繁地进行 I/O 操作,进一步提高了读写效率。使用 msync
将映射区域的内容同步到文件中,保证了数据的持久性。
需要注意的是,使用 mmap
时需要考虑内存的使用量,避免出现内存不足的情况。此外,还需要考虑多个进程同时访问同一映射区域时的同步问题。