iOS文件内存映射详解(mmap)

文件内存映射(mmap)之前看过很多文章提及到,但是都没有写iOS中具体的实现,只是都说对于大文件读写效率比较高等。所以作者就专门研究了以下mmap技术,并且实现了一下

mmap

文件映射是将文件的磁盘扇区映射到进程的虚拟内存空间的过程。一旦被映射,您的应用程序就会访问这个文件,就好像它完全驻留在内存中一样(不占用内存,使用的是虚拟内存)。当您从映射的文件指针读取数据时,将在适当的数据中的内核页面并将其返回给您的应用程序。

疑问

那大家就会想了,既然不消耗内存,那岂不是都用mmap就行了,这样多好啊,又不占内存。其实不然,并不是所有的场景都适合使用mmap的

适合的场景

您有一个很大的文件,其内容您想要随机访问一个或多个时间。

您有一个小文件,它的内容您想要立即读入内存并经常访问。这种技术最适合那些大小不超过几个虚拟内存页的文件。(页是地址空间的最小单位,虚拟页和物理页的大小是一样的,通常为4KB。)

您需要在内存中缓存文件的特定部分。文件映射消除了缓存数据的需要,这使得系统磁盘缓存中的其他数据空间更大。

当随机访问一个非常大的文件时,通常最好只映射文件的一小部分。映射大文件的问题是文件会消耗活动内存。如果文件足够大,系统可能会被迫将其他部分的内存分页以加载文件。将多个文件映射到内存中会使这个问题更加复杂。

不适合的场景

您希望从开始到结束的顺序从头到尾读取一个文件。

这个文件有几百兆字节或者更大。将大文件映射到内存中会快速地填充内存,并可能导致分页,这将抵消首先映射文件的好处。对于大型顺序读取操作,禁用磁盘缓存并将文件读入一个小内存缓冲区。

该文件大于可用的连续虚拟内存地址空间。对于64位应用程序来说,这不是什么问题,但是对于32位应用程序来说,这是一个问题。

该文件位于可移动驱动器上。

该文件位于网络驱动器上。

实现

这个代码实现的功能就是首先读取存储在我们沙盒的文件,然后在该文件的上继续写入数据(追加数据)

#import"ViewController.h"

#import#importintMapFile( char * inPathName, void** outDataPtr, size_t * outDataLength );

voidProcessFile( char * inPathName )

{

size_t dataLength;

void* dataPtr;

void*start;

if( MapFile( inPathName, &dataPtr, &dataLength ) == 0)

{

start = dataPtr;

dataPtr = dataPtr+3;

memcpy(dataPtr, "CCCC", 4);

// Unmap files:

munmap(start, 7);

}

}

// MapFile

// Exit:    outDataPtra    pointer to the mapped memory region

//          outDataLength  size of the mapped memory region

//          return value    an errno value on error (see sys/errno.h)

//                          or zero for success

//

intMapFile( char * inPathName, void** outDataPtr, size_t * outDataLength )

{

intoutError;

intfileDescriptor;

struct stat statInfo;

// Return safe values on error.

outError = 0;

*outDataPtr = NULL;

*outDataLength = 0;

// Open the file.

fileDescriptor = open( inPathName, O_RDWR, 0);

if( fileDescriptor < 0)

{

outError = errno;

}

else

{

// We now know the file exists. Retrieve the file size.

if( fstat( fileDescriptor, &statInfo ) != 0)

{

outError = errno;

}

else

{

ftruncate(fileDescriptor, statInfo.st_size+4);//增加文件大小

fsync(fileDescriptor);//刷新文件

*outDataPtr = mmap(NULL,

statInfo.st_size+4,

PROT_READ|PROT_WRITE,

MAP_FILE|MAP_SHARED,

fileDescriptor,

0);

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 );

}

returnoutError;

}

@interfaceViewController ()

@property (weak, nonatomic) IBOutlet UITextView *mTV;

@end

@implementation ViewController

- (void)viewDidLoad {

[superviewDidLoad];

NSString *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;

NSString *str = @"AAA";

NSError *error;

NSString *filePath = [NSString stringWithFormat:@"%@/text.txt",path];

[str writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:&error];

if(error) {

NSLog(@"%@",error);

}

ProcessFile(filePath.UTF8String);

NSString *result = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];

self.mTV.text = result;

}

@end

最重要的就是2个函数:

mmap()

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

start:映射区的开始地址,设置为0时表示由系统决定映射区的起始地址。 length:映射区的长度。//长度单位是 以字节为单位,不足一内存页按一内存页处理 prot:期望的内存保护标志,不能与文件的打开模式冲突。是以下的某个值,可以通过or运算合理地组合在一起 PROT_EXEC //页内容可以被执行 PROT_READ //页内容可以被读取 PROT_WRITE //页可以被写入 PROT_NONE //页不可访问 flags:指定映射对象的类型,映射选项和映射页是否可以共享。它的值可以是一个或者多个以下位的组合体 fd:有效的文件描述词。一般是由open()函数返回,其值也可以设置为-1,此时需要指定flags参数中的MAP_ANON,表明进行的是匿名映射。 off_toffset:被映射对象内容的起点。

这里的参数我们要重点关注3个length、prot、flags。 length代表了我们可以操作的内存大小; prot代表我们对文件的操作权限。这里传入了读写权限,而且注意要与open()保持一致,所以open()函数传入了O_RDWR可读写权限;。 flags要写MAP_FILE|MAP_SHARED,我一开始只写了MAP_FILE,能读,但是不能写。

munmap()

int munmap(void* start,size_t length);

这里对原来文件追加写入数据要注意一点,读取原来文件之后,我们只有原来文件大小的可写区域。例如以上例子原文件中是AAA,这时我们要写入CCCC,做覆盖写入的话我们只能写入CCC。所以要要对文件进行追加写入的话,必须提前增加文件的大小即调用ftruncate()和sync(),增加了4位了,最终才能使CCCC顺利写入

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,045评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,114评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,120评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,902评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,828评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,132评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,590评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,258评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,408评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,335评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,385评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,068评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,660评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,747评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,967评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,406评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,970评论 2 341

推荐阅读更多精彩内容