专栏首页搜狗测试iOS文件内存映射——MMAP

iOS文件内存映射——MMAP

前言

最近一段项目上总是出现一些因为文件没有及时保存而产生的问题,因此小编就在网上寻找到了这个文件存储方法mmap,这里为大家进行下简单的介绍。

简介

首先我们需要对iOS中各App的运行环境进行了解,进程即App运行的基本单位,进程之间相对独立。iOS系统中App运行的内存空间地址是虚拟空间地址,存储数据是在各自的沙盒。当我们在App中去读写沙盒中的文件时,我们会使用NSFileManager去查找文件,然后可以使用NSData去加载二进制数据。文件操作的更底层实现过程,是使用linux的read()、write()函数直接操作文件句柄(也叫文件描述符、fd)。

在操作系统层面,当App读取一个文件时,实际是有两步:先将文件从磁盘读取到物理内存,再从系统空间拷贝到用户空间(可以认为是复制到系统给App统一分配的内存)。

iOS系统使用页缓存机制,通过MMU(Memory Management Unit)将虚拟内存地址和物理地址进行映射,并且由于进程的地址空间和系统的地址空间不一样,所以还需要多一次拷贝。

而mmap将磁盘上文件的地址信息与进程用的虚拟逻辑地址进行映射,建立映射的过程与普通的内存读取不同:正常的是将文件拷贝到内存,mmap只是建立映射而不会将文件加载到内存中。App 只管往里面写数据,由 iOS 负责将内存回写到文件,不必担心 crash 导致数据丢失。

下面两个图分别显示进程读取磁盘文件的过程和使用mmap进行文件映射的过程。

图1

图2

实现代码

以官网的demo为例,其他的代码很简明直接,核心就在于mmap函数。

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

*outDataPtr = mmap(NULL,
                   size,
                   PROT_READ|PROT_WRITE,
                   MAP_FILE|MAP_SHARED,
                   fileDescriptor,
                   );

start:映射开始地址,设置NULL则让系统决定映射开始地址;

length:映射区域的长度,单位是Byte;

prot:映射内存的保护标志,主要是读写相关,是位运算标志;(记得与下面fd对应句柄打开的设置一致)

flags:映射类型,通常是文件和共享类型;

fd:文件句柄;

off_toffset:被映射对象的起点偏移;

读写的例子如下:

#import "ViewController.h"
#import <sys/mman.h>
#import <sys/stat.h>

int MapFile(const char * inPathName, void ** outDataPtr, size_t * outDataLength, size_t appendSize)
{
    int outError;
    int fileDescriptor;
    struct stat statInfo;

    // Return safe values on error.
    outError = ;
    *outDataPtr = NULL;
    *outDataLength = ;

    // Open the file.
    fileDescriptor = open( inPathName, O_RDWR,  );
    if( fileDescriptor <  )
    {
        outError = errno;
    }
    else
    {
        // We now know the file exists. Retrieve the file size.
        if( fstat( fileDescriptor, &statInfo ) !=  )
        {
            outError = errno;
        }
        else
        {
            ftruncate(fileDescriptor, statInfo.st_size + appendSize);
            fsync(fileDescriptor);
            *outDataPtr = mmap(NULL,
                               statInfo.st_size + appendSize,
                               PROT_READ|PROT_WRITE,
                               MAP_FILE|MAP_SHARED,
                               fileDescriptor,
                               );
            if( *outDataPtr == MAP_FAILED )
            {
                outError = errno;
            }
            else
            {
                // On success, return the size of the mapped file.
                *outDataLength = statInfo.st_size;
            }
        }

        // Now close the file. The kernel doesn’t use our file descriptor.
        close( fileDescriptor );
    }

    return outError;
}


void ProcessFile(const char * inPathName)
{
    size_t dataLength;
    void * dataPtr;
    char *appendStr = " append_key";
    int appendSize = (int)strlen(appendStr);
    if( MapFile(inPathName, &dataPtr, &dataLength, appendSize) == ) {
        dataPtr = dataPtr + dataLength;
        memcpy(dataPtr, appendStr, appendSize);
        // Unmap files
        munmap(dataPtr, appendSize + dataLength);
    }
}

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    NSString *path = [NSHomeDirectory() stringByAppendingPathComponent:@"test.data"];
    NSLog(@"path: %@", path);
    NSString *str = @"test str";
    [str writeToFile:path atomically:YES encoding:NSUTF8StringEncoding error:nil];

    ProcessFile(path.UTF8String);
    NSString *result = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
    NSLog(@"result:%@", result);
}

最后

mmap就是文件的内存映射,通常读取文件是将文件读取到内存,会占用真正的物理内存;而mmap是用进程的内存虚拟地址空间去映射实际的文件中,这个过程由操作系统处理。mmap不会为文件分配物理内存,而是相当于将内存地址指向文件的磁盘地址,后续对这些内存进行的读写操作,会由操作系统同步到磁盘上的文件。这种操作也节省了很多内存占用,极大的提升了进程的性能。

搜狗测试微信号:Qa_xiaoming

搜狗测试QQ粉丝群:459645679

本文分享自微信公众号 - 搜狗测试(SogouQA),作者:小K

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-08-07

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • BLOCK介绍及常见问题

    这段时间小编在整理开发代码问题时发现开发同学在使用block时经常出现一些BUG,其中还有一些隐藏的很深的问题,这里小编就为大家介绍一下block的原理,简单用...

    用户5521279
  • 图表分析,Jmeter还有这种功能?

    在做压力测试或者性能测试的时候, 服务端的各项资源指标是我们非常关心的问题。一般性能测试需要监控的指标分为资源指标和系统指标。

    用户5521279
  • iOS内存优化心得

    当app经过一段儿时间的迭代,往往会出现一些性能问题,这时能够协助开发同学解决这些性能问题也成为我们测试同学的重要工作。凑巧最近一段时间小编就一直在协助开发同学...

    用户5521279
  • iOS的文件内存映射——mmap

    mmap在日常开发中偶尔会遇到的一个关键词,最常用到的场景是MMKV,其次用到的是日志打印。虽然都已经被封装好,但也需要了解下mmap的基本原理和过程。

    落影
  • POSIX文件操作(二)

    Oceanlong
  • Java文件映射(mmap)全接触

    前言 我们在平时的工作中大多都会需要处理像下面这样基于Key-Value的数据: ? 其中UID是数据唯一标识,FIELD[1]是属性值。以QQ用户的Sess...

    用户1263954
  • 数据驱动决策的13种思维

    “数据驱动决策”,为了不让这句话成为空话,请先装备以下13种思想武器,相信将来你一定能用上!

    华章科技
  • 数据驱动决策的13种思维

    “数据驱动决策”,为了不让这句话成为空话,请先装备以下13种思想武器,相信将来你一定能用上! 第一、信度与效度思维 这部分也许是全文最难理解的部分,但我觉得也...

    小莹莹
  • 干货 :数据驱动决策的13种思维

    “数据驱动决策”,为了不让这句话成为空话,请先装备以下13种思想武器,相信将来你一定能用上! 第一、信度与效度思维 这部分也许是全文最难理解的部分,但我觉得也...

    灯塔大数据
  • 【数据】数据驱动决策的13种思维

    小编邀请您,先思考: 1 如何让数据驱动决策? “数据驱动决策”,为了不让这句话成为空话,请先装备以下13种思想武器,相信将来你一定能用上! 1. 信度与效度...

    陆勤_数据人网

扫码关注云+社区

领取腾讯云代金券