深入理解MMAP原理,大厂爱不释手的技术手段

简介: 深入理解MMAP原理,大厂爱不释手的技术手段

image.png

为什么大厂爱不释手


如微信的MMKV 组件、美团的Logan组件,还有微信的日志模块xlog,为什么大厂偏爱它呢?他到底有什么魔力么?我认为主要原因如下:

  • 跨平台,C++编写,可以支持多平台
  • 跨进程,通过文件共享可以实现多个进程内存共享,实现进程通信
  • 高性能,实现用户空间和内核空间的零拷贝,速度快且节约内存等
  • 高稳定,页中断保护神,由操作系统实现的,稳定性可想而知

函数介绍


void* mmap(void* addr, size_t length, int prot, int flags, int fd, off_t offset);
  • addr 代表映射的虚拟内存起始地址;
  • length 代表该映射长度;
  • prot 描述了这块新的内存区域的访问权限;
  • flags 描述了该映射的类型;
  • fd 代表文件描述符;
  • offset 代表文件内的偏移值。

mmap的强大之处在于,它可以根据参数配置,用于创建共享内存,从而提高文件映射区域的IO效率,实现IO零拷贝,后面讲下零拷贝的技术,对比下,决定这些功能的主要就是三个参数,下面一一解释

prot


四种情况如下:

  • PROT_EXEC,代表该内存映射有可执行权限,可以看成是代码段,通常存储CPU可执行机器码
  • PROT_READ,代表该内存映射可读
  • PROT_WRITE,代表该内存映射可写
  • PROT_NONE,代表该内存映射不能被访问

flags


比较有代表性的如下:

  • MAP_SHARED,创建一个共享映射区域
  • MAP_PRIVATE,创建一个私有映射区域
  • MAP_ANONYMOUS,创建一个匿名映射区域,该情况只需要传入-1即可
  • MAP_FIXED,当操作系统以addr为起始地址进行内存映射时,如果发现不能满足长度或者权限要求时,将映射失败,如果非MAP_FIXED,则系统就会再找其他合适的区域进行映射

fd


当参数fd不等于0时,内存映射将与文件进行关联,如果等于0,就会变成匿名映射,此时flags必为MAP_ANONYMOUS

应用场景


image.png

一个mmap竟有如此丰富的功能,从申请分配内存到加载动态库,再到进程间通信,真的是无所不能,强大到让人五体投地。下面就着四种情况,拿一个我最关心的父子进程通信来举例看下,实现一个简单的父子进程通信逻辑,毕竟我们学习的目的就是为了应用,光有理论怎么能称之为合格的博客呢?

父子进程共享内存


#include <iostream>
#include <sys/wait.h>
#include <unistd.h>
#include <sys/mman.h>
int main() {
    pid_t c_pid = fork();
    char* shm = (char*)mmap(nullptr, 4096, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
    if (c_pid == -1) {
        perror("fork");
        exit(EXIT_FAILURE);
    } else if (c_pid > 0) {
        printf("parent process pid: %d\n", getpid());
        sprintf(shm, "%s", "hello, my child");
        printf("parent process got a message: %s\n", shm);
        wait(nullptr);
    } else {
        printf("child process pid: %d\n", getpid());
        sprintf(shm, "%s", "hello, father.");
        printf("child process got a message: %s\n", shm);
        exit(EXIT_SUCCESS);
    }
    return EXIT_SUCCESS;
}

运行后打印如下

parent process pid: 87799
parent process got a message: hello, my child
child process pid: 87800
child process got a message: hello, father.
Process finished with exit code 0

用mmap创建了一块匿名共享内存区域,fd传入**-1MAP_ANONYMOUS配置实现匿名映射,使用MAP_SHARED**创建共享区域,使用fork函数创建子进程,这样来实现子进程通信,通过sprintf将格式化后的数据写入到共享内存中。

通过简单的几行代码就实现了跨进程通信,如此简单,这么强大的东西,背后有什么支撑么?带着问题我们接着一探究竟。

MMAP背后的保护神


说到MMAP的保护神,首页了解下内存页:在页式虚拟存储器中,会在虚拟存储空间和物理主存空间都分割为一个个固定大小的页,为线程分配内存是也是以页为单位。比如:页的大小为 4K,那么 4GB 存储空间就需要4GB/4KB=1M 条记录,即有 100 多万个 4KB 的页,内存页中,当用户发生文件读写时,内核会申请一个内存页与文件进行读写操作,如图

image.png

这时如果内存页中没有数据,就会发生一种中断机制,它就叫缺页中断,此中断就是MMAP的保护神,为什么这么说呢?我们知道mmap函数调用后,在分配时只是建立了进程虚拟地址空间,并没有分配虚拟内存对应的物理内存,当访问这些没有建立映射关系的虚拟内存时,CPU加载指令发现代码段是缺失的,就触发了缺页中断,中断后,内核通过检查虚拟地址的所在区域,发现存在内存映射,就可以通过虚拟内存地址计算文件偏移,定位到内存所缺的页对应的文件的页,由内核启动磁盘IO,将对应的页从磁盘加载到内存中。最终保护mmap能顺利进行,无私奉献。了解完缺页中断,我们再来细聊下mmap四种场景下的内存分配原理

四种场景分配原理


image.png

上面是一个简单的原理总结,并没有详细的展开,感兴趣可以自己查查资料哈。

总结


本次分享,主要介绍了mmap的四种应用场景,通过一个实例验证了父子进程间的通信,并深入mmap找到它的保护神,且深入了解到mmap在四种场景下,操作系统是如何组织分配,通过对这些的了解,在你之后的mmap实战应用有了更好的理论基础,可以根据不同的需求,不同的性能要求等,选择最合适的实现。

目录
相关文章
|
存储 Web App开发 缓存
CleanMyMac X安全吗?及优缺点测评简述
如果Mac 电脑经常卡机、死机、速度很慢,跳出“存储过满”等问题,那么就需要用到世界上最受欢迎的电脑清理软件之一CleanMyMac X,它提供免费试用版,会告诉您可以释放多少磁盘空间,使用更多功能的完整版更能优化和保护 Mac 计算机,让速度大大提升。网上对CleanMyMac X的评论有的超级正面,有的超级负面,甚至有的“专家”说它是流氓软件,然后我们从实际出发,在测评这款清理软件之后,我们100% 支持它,因为它真的效果好,确实比市面上任何Mac 清理软件好用,价格不是最便宜的,但值得拥有,它在清理文件、改善隐私、删除垃圾或恶意软件以及提高设备的使用寿命和保持性能方面创造了奇迹。
860 0
|
2月前
|
监控 算法 数据可视化
深入解析Android应用开发中的高效内存管理策略在移动应用开发领域,Android平台因其开放性和灵活性备受开发者青睐。然而,随之而来的是内存管理的复杂性,这对开发者提出了更高的要求。高效的内存管理不仅能够提升应用的性能,还能有效避免因内存泄漏导致的应用崩溃。本文将探讨Android应用开发中的内存管理问题,并提供一系列实用的优化策略,帮助开发者打造更稳定、更高效的应用。
在Android开发中,内存管理是一个绕不开的话题。良好的内存管理机制不仅可以提高应用的运行效率,还能有效预防内存泄漏和过度消耗,从而延长电池寿命并提升用户体验。本文从Android内存管理的基本原理出发,详细讨论了几种常见的内存管理技巧,包括内存泄漏的检测与修复、内存分配与回收的优化方法,以及如何通过合理的编程习惯减少内存开销。通过对这些内容的阐述,旨在为Android开发者提供一套系统化的内存优化指南,助力开发出更加流畅稳定的应用。
67 0
|
3月前
|
UED 开发工具 iOS开发
Uno Platform大揭秘:如何在你的跨平台应用中,巧妙融入第三方库与服务,一键解锁无限可能,让应用功能飙升,用户体验爆棚!
【8月更文挑战第31天】Uno Platform 让开发者能用同一代码库打造 Windows、iOS、Android、macOS 甚至 Web 的多彩应用。本文介绍如何在 Uno Platform 中集成第三方库和服务,如 Mapbox 或 Google Maps 的 .NET SDK,以增强应用功能并提升用户体验。通过 NuGet 安装所需库,并在 XAML 页面中添加相应控件,即可实现地图等功能。尽管 Uno 平台减少了平台差异,但仍需关注版本兼容性和性能问题,确保应用在多平台上表现一致。掌握正确方法,让跨平台应用更出色。
47 0
|
6月前
|
缓存 算法 调度
探索现代操作系统之芯:内核性能优化的艺术
【5月更文挑战第29天】 在本文中,我们将深入探讨操作系统中最关键的组成部分——内核,以及如何通过各种技术手段提升其性能。与传统的摘要不同,我们不会概述文章的每个部分,而是直接点出核心内容:操作系统内核的性能优化是一个复杂而精细的过程,它涉及到算法改进、内存管理、并发控制等多个方面。本文的目的是为读者提供一个关于现代操作系统内核性能优化的全面视角,并讨论实现这些优化所采取的策略和技术。
|
6月前
|
监控 Android开发 UED
构建高效的Android应用:从内存管理到用户体验
【5月更文挑战第28天】 在移动开发的世界中,打造一个既快速又高效的Android应用是每个开发者追求的目标。本文将深入探讨如何通过合理的内存管理策略、优化的代码实践和用户界面设计的提升来增强应用性能。我们将剖析内存泄漏的根源,提供解决方案,探索Kotlin与Java在性能上的差异,并分析如何利用Android系统提供的工具监控和改善应用表现。此外,我们还将讨论Material Design的应用以及其对用户体验的影响。通过这些技术的综合运用,你将能够为用户提供更流畅、响应更快的使用体验。
|
6月前
|
存储 缓存 算法
深入理解操作系统内存管理:分页系统的优势与挑战构建高效Android应用:探究Kotlin协程的优势与实践
【5月更文挑战第27天】 在现代计算机系统中,内存管理是操作系统的核心功能之一。分页系统作为一种内存管理技术,通过将物理内存划分为固定大小的单元——页面,为每个运行的程序提供独立的虚拟地址空间。这种机制不仅提高了内存的使用效率,还为多任务环境提供了必要的隔离性。然而,分页系统的实现也带来了一系列的挑战,包括页面置换算法的选择、内存抖动问题以及TLB(Translation Lookaside Buffer)的管理等。本文旨在探讨分页系统的原理、优势及其面临的挑战,并通过分析现有解决方案,提出可能的改进措施。
|
6月前
|
存储 人工智能 程序员
【重学C++】【内存】关于C++内存分区,你可能忽视的那些细节
【重学C++】【内存】关于C++内存分区,你可能忽视的那些细节
159 1
|
6月前
|
存储 缓存 算法
作者推荐 | 【底层服务/编程功底系列】「底层技术原理」史上最清晰的采用程序员的视角方式进行深入探索Linux零拷贝技术原理及实现
作者推荐 | 【底层服务/编程功底系列】「底层技术原理」史上最清晰的采用程序员的视角方式进行深入探索Linux零拷贝技术原理及实现
57 0
|
Linux C语言 C++
一文搞懂操作系统进程同步的几种机制(含现实案列)
一文搞懂操作系统进程同步的几种机制(含现实案列)
|
新零售 缓存 人工智能
响应速度不给力?那是你没有解锁正确缓存姿势! | 开发者必读(117期)
最炫的技术新知、最热门的大咖公开课、最有趣的开发者活动、最实用的工具干货,就在《开发者必读》!
1480 0