内存映射技术

一、内存映射

内存映射,Memory mapping,即mmap。是一种将文件或其他设备的内容直接映射到进程虚拟地址空间的技术。内存映射常见的一般是文件或IO映射。通过内存映射,可以将相关的数据映射到内存中,减少对IO操作的次数,从而提高文件或IO的处理速度,大幅提高应用的性能。
在不同的系统上,内存映射的实现也有所不同,大家要注意。

二、内部机制和原理

为什么会有内存映射?这对于理解其内部机制和原理有着重要的帮助。大家在学习计算机技术时,对存储的读写都有过接触,也明白内存的处理速度比IO等操作要快的多。虽然在内存前面还有缓存和寄存器等被速度更快的,但其应用成本和大小与内存的处理有一个动态的平衡点。换句话说,在处理一些特定场景时,需要一个大小合适和速度较快的处理手段。那么在内存里处理就会是一个非常适合的情况。而且随着硬件技术的发展,内存的成本也在快速的下降中,这就更容易让内存的应用得到广泛的应用。
内存映射工作原理:
内存映射其实和内存的虚拟地址有着相当强的关联。在Linux下,一切都是文件,而一个文件被映射到内存后,操作系统会在进程内存地址空间中维护一段区域,这段内存地址范围会与文件内容建立直接的映射关系。进程利用指针可以直接访问这段内存,而操作系统负责将映射的文件数据加载到物理内存中,并在需要时将内存中的数据同步回磁盘文件进行保存。
一般来说,操作系统的虚拟内存管理中,当一个进程访问内存映射的具体的数据时,假如该数据尚未加载到内存中,则会触发一个中断(缺页中断,page fault),随后操作系统将相关文件对应的数据加载到内存中并开始继续访问的操作。

三、应用场景

内存映射的场景很多,常见的有以下几种情况:
1、高性能数据读写
处理一些大型文件的快速读写,如大图片、视频以及相关数据库文件,当然也包括其它一些特定场景下的文件处理。硬件设备IO的相关处理(其实设备也是文件)。
2、进程间通信(IPC)
通过共享内存实现高效数据交换。包括现在流行的分布式相关内存共享,同样可以在某种程度上达到消弥不同节点上数据交换。
3、内存动态管理
在特定场景下,如处理大型视频文件的分段加载,图形处理中的显存和内存间的映射以及DirectIO、零拷贝等技术。

四、实例分析

下面看一个Linux平台基于Posix库的例子:

#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>

int main() {
    int fd = open("example.dat", O_RDWR);  
    size_t file_size = lseek(fd, 0, SEEK_END);

    // 映射文件
    char* mapped_mem = mmap(
        NULL,             // 由系统选择映射地址
        file_size,         // 大小
        PROT_READ | PROT_WRITE, // 标志
        MAP_SHARED,        // 共享标志
        fd,               
        0                  // 偏移量
    );


    mapped_mem[0] = '123';   

    // 同步磁盘
    msync(mapped_mem, file_size, MS_SYNC);

    // 删除映射
    munmap(mapped_mem, file_size);
    close(fd);
    return 0;
}

再看一个Windows平台的例子:

#include <Windows.h>

int main() {
    HANDLE hFile = CreateFile(L"example.dat", GENERIC_READ | GENERIC_WRITE,
                              FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
                              FILE_ATTRIBUTE_NORMAL, NULL);
    HANDLE hMapping = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 0, NULL);
    LPVOID mapped_mem = MapViewOfFile(hMapping, FILE_MAP_WRITE, 0, 0, 0);

    ((char*)mapped_mem)[0] = '123';

    FlushViewOfFile(mapped_mem, 0);  // 同步
    UnmapViewOfFile(mapped_mem);
    CloseHandle(hMapping);
    CloseHandle(hFile);
    return 0;
}

这里对内存映射的相关API不再详细赘述,这方面的资料太多了,有一些不明白的直接翻看相关的帮助即可。

五、内存映射特点

正如前面所分析,凡事有利则有弊,内存映射的优势有以下几点:
1、提高性能:减少了正常情况下文件IO的开销,包括内核与用户层间的数据拷贝等,提高了性能
2、易于编程:直接访问系统API,通过指针操作文件而不用调用open等文件操作API
3、易于共享:内存映射的文件可以被多个进程共享,便于实现进程间通信
4、动态加载:文件等数据根据需要动态加载到内存,而不用一次性全部加载,提高内存的利用率
5、文件自动处理:操作系统会在适当的时候将修改后的内存数据同步回磁盘文件

而内存映射的主要缺点也不少:
1、内存消耗较大:映射大文件时,如果需要加载数据较多,则可能消耗大量内存
2、大小限制:在32位系统中,内存映射的内存大小范围不超过2GB
3、错误处理:内存映射文件在操作内存时可能会产生IO错误导致异常,从而导致复杂度加大
4、数据同步的处理:共享内存需要同步来处理竞态条件

六、总结

内存映射可以认为是对成本和效率的一种折衷 ,当然这种折衷也是对底层系统(特别是硬件)的一种抽象。而面向抽象进行的操作相对来说要更简单和更容易实施。明白了这一点,开发者就应该明白了如何学习使用内存映射了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值