iOS js css html

mmap和MMKV

2022-10-09  本文已影响0人  林大鹏

一. mmap

1. 什么是mmap

mmap是一种内存映射文件的方法,即将一个文件或者其他对象映射到进程的地址空间,实现文件磁盘地址和进程虚拟地址空间中一段虚拟地址的一一映射关系。实现这样的映射关系后,进程就可以采用指针的方式读写操作这一段内存,而系统会自动回写脏页面到对应的磁盘上,即完成了对文件的操作而不必调用readwrite等系统调用函数。相反,内核空间对这段区域的修改也直接反应用户空间,从而可以实现不同进程间的文件共享。如下图所示:

image.png

由上图可以看出,进程的虚拟地址空间,由多个虚拟内存区域构成。虚拟内存区域是进程的虚拟地址空间中的一个同质区间,即具有同样特性的连续地址范围。上图中所示的text数据段(代码段),初始数据段, BSS数据段,堆,栈和内存映射,都是一个独立的虚拟内存区域。而为内存映射服务的地址空间处在堆栈之前的空余部分。

linux内核使用vm_area_struct结构来表示一个独立的虚拟内存区域,由于每个不同质的虚拟内存区域功能和内部机制都不同,因此一个进程使用多个vm_area_struct结构来分别表示不同类型的虚拟内存区域。各个vm_area_struct结构使用链表或者树形结构链接,方便进程快速访问,如下图所示:

image.png

vm_area_struct结构中包含区域起始和终止地址以及其他相关信息,同时也包含了一个vm_ops指针,其内部可引出所有针对这个区域可以使用的系统调用函数。这样,进程对某一个虚拟内存区域的任何操作需要用到的信息,都可以从vm_area_struct中获取。

mmap函数就是要创建一个新的vm_area_struct结构,并将其与文件的物理磁盘地址相连。

2. mmap内存映射原理

mmap内存映射的实现过程,总的来说可以分为三个阶段:

A. 进程启动映射过程,并在虚拟地址空间中为映射创建虚拟映射区域

void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);

B. 调用内核空间的系统调用函数mmap(不同于用户空间函数),实现文件物理地址和进程虚拟地址一一映射关系。

int mmap(struct file *filp, struct vm_area_struct *vma)

不同于用户空间库函数。

C. 进程发起对这片映射空间的访问,引发缺页异常,实现文件内容到物理内存(主存)的拷贝

注:前两个阶段仅在于创建虚拟区间并完成地址映射,但是并没有将任何文件数据的拷贝至主存。真正的文件读取是当进程发起读或写操作时。

注:修改过的脏页面并不会立即更新回文件中,而是有一段时间的延迟,可以调用msync()来强制同步, 这样所写的内容就能立即保存到文件里了。

3. 常规文件访问

常规文件操作为了提高读写效率和保护磁盘,使用了页缓存机制。这样造成读文件时需要先将文件页从磁盘拷贝到页缓存中,由于页缓存处在内核空间,不能被用户进程直接寻址,所以还需要将页缓存中数据页再次拷贝到内存对应的用户空间中。这样,通过了两次数据拷贝过程,才能完成进程对文件内容的获取任务。写操作也是一样,待写入的buffer在内核空间不能直接访问,必须要先拷贝至内核空间对应的主存,再写回磁盘中(延迟写回),也是需要两次数据拷贝。

4. mmap优点

5. mmap缺点

二. MMKV

MMKV是基于mmap内存映射的key-value组件,底层序列化/反序列化使用protobuf实现,性能高,稳定性强。

1. MMKV原理

A. 内存准备

通过mmap内存映射文件,提供一段可供随时写入的内存块,App只管往里面写数据,由iOS操作系统负责将内存写到文件,不必担心crash到时数据丢失。

B. 数据组织

数据序列化方面我们选用protobuf协议,pb在性能和空间占用效率表现优秀,考虑到我们要提供通用的KV组件,key可以限定是string字符串类型,value则多种多样(int, double, bool等),考虑将value通过protobuf协议序列化成统一的内存块buffer,然后就可以将这些KV对象序列化到内存中。

image.png

图片引用自: [微信团队分享:iOS版微信的高性能通用key-value组件技术实践]

C. 写入优化

标准的protobuf不提供增量更新能力,每次写入都必须全量写入。

考虑到主要使用场景是频繁的进行写入更新,我们需要有增量更新的能力:

D. 空间增长

使用append实现增量更新带来新的问题,就是不断append的话,文件大小会增长得不可控,例如同一个key不断更新的话,是可能耗尽几百M甚至上G空间,而事实上整改KV文件就这一个key,不到1k空间就存得下。这明显不可取。

我们需要在性能和空间中做个折中:

image.png

图片引用自: [微信团队分享:iOS版微信的高性能通用key-value组件技术实践]

E. 数据有效性

考虑到文件系统,操作系统都有一定的不稳定性,我们另外增加了crc校验,对无效数据进行甄别。

F. MMKV使用

MMKV提供一个全局实例,可以直接使用:

image.png

图片引用自: [微信团队分享:iOS版微信的高性能通用key-value组件技术实践]

可以看到MMKV在使用上还是比较简单的。如果不同业务需要区别存储,也可以单独创建自己的实例。

image.png

图片引用自: [微信团队分享:iOS版微信的高性能通用key-value组件技术实践]

G. 支持数据类型

支持以下 C 语语言基础类型:

bool、int32、int64、uint32、uint64、float、double

支持以下 ObjC 类型:

NSString、NSData、NSDate

三. 引用

[MMKV学习(一)mmap映射原理]

[认真分析mmap:是什么 为什么 怎么用]

[微信团队分享:iOS版微信的高性能通用key-value组件技术实践]

上一篇 下一篇

猜你喜欢

热点阅读