iOS文件內(nèi)存映射詳解(mmap)

文件內(nèi)存映射(mmap)之前看過(guò)很多文章提及到碳胳,但是都沒(méi)有寫(xiě)iOS中具體的實(shí)現(xiàn)废境,只是都說(shuō)對(duì)于大文件讀寫(xiě)效率比較高等榛泛。所以作者就專門(mén)研究了以下mmap技術(shù),并且實(shí)現(xiàn)了一下

mmap

文件映射是將文件的磁盤(pán)扇區(qū)映射到進(jìn)程的虛擬內(nèi)存空間的過(guò)程蓖墅。一旦被映射库倘,您的應(yīng)用程序就會(huì)訪問(wèn)這個(gè)文件临扮,就好像它完全駐留在內(nèi)存中一樣(不占用內(nèi)存,使用的是虛擬內(nèi)存)教翩。當(dāng)您從映射的文件指針讀取數(shù)據(jù)時(shí)杆勇,將在適當(dāng)?shù)臄?shù)據(jù)中的內(nèi)核頁(yè)面并將其返回給您的應(yīng)用程序。

疑問(wèn)

那大家就會(huì)想了饱亿,既然不消耗內(nèi)存蚜退,那豈不是都用mmap就行了,這樣多好啊彪笼,又不占內(nèi)存钻注。其實(shí)不然,并不是所有的場(chǎng)景都適合使用mmap的

適合的場(chǎng)景

您有一個(gè)很大的文件配猫,其內(nèi)容您想要隨機(jī)訪問(wèn)一個(gè)或多個(gè)時(shí)間幅恋。

您有一個(gè)小文件,它的內(nèi)容您想要立即讀入內(nèi)存并經(jīng)常訪問(wèn)泵肄。這種技術(shù)最適合那些大小不超過(guò)幾個(gè)虛擬內(nèi)存頁(yè)的文件捆交。(頁(yè)是地址空間的最小單位,虛擬頁(yè)和物理頁(yè)的大小是一樣的腐巢,通常為4KB品追。)

您需要在內(nèi)存中緩存文件的特定部分。文件映射消除了緩存數(shù)據(jù)的需要冯丙,這使得系統(tǒng)磁盤(pán)緩存中的其他數(shù)據(jù)空間更大肉瓦。

當(dāng)隨機(jī)訪問(wèn)一個(gè)非常大的文件時(shí),通常最好只映射文件的一小部分银还。映射大文件的問(wèn)題是文件會(huì)消耗活動(dòng)內(nèi)存风宁。如果文件足夠大洁墙,系統(tǒng)可能會(huì)被迫將其他部分的內(nèi)存分頁(yè)以加載文件蛹疯。將多個(gè)文件映射到內(nèi)存中會(huì)使這個(gè)問(wèn)題更加復(fù)雜。

不適合的場(chǎng)景

您希望從開(kāi)始到結(jié)束的順序從頭到尾讀取一個(gè)文件热监。

這個(gè)文件有幾百兆字節(jié)或者更大捺弦。將大文件映射到內(nèi)存中會(huì)快速地填充內(nèi)存,并可能導(dǎo)致分頁(yè)孝扛,這將抵消首先映射文件的好處列吼。對(duì)于大型順序讀取操作,禁用磁盤(pán)緩存并將文件讀入一個(gè)小內(nèi)存緩沖區(qū)苦始。

該文件大于可用的連續(xù)虛擬內(nèi)存地址空間寞钥。對(duì)于64位應(yīng)用程序來(lái)說(shuō),這不是什么問(wèn)題陌选,但是對(duì)于32位應(yīng)用程序來(lái)說(shuō)理郑,這是一個(gè)問(wèn)題蹄溉。

該文件位于可移動(dòng)驅(qū)動(dòng)器上。

該文件位于網(wǎng)絡(luò)驅(qū)動(dòng)器上您炉。

實(shí)現(xiàn)

這個(gè)代碼實(shí)現(xiàn)的功能就是首先讀取存儲(chǔ)在我們沙盒的文件柒爵,然后在該文件的上繼續(xù)寫(xiě)入數(shù)據(jù)(追加數(shù)據(jù))

#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個(gè)函數(shù):

mmap()

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

start:映射區(qū)的開(kāi)始地址,設(shè)置為0時(shí)表示由系統(tǒng)決定映射區(qū)的起始地址赚爵。 length:映射區(qū)的長(zhǎng)度棉胀。//長(zhǎng)度單位是 以字節(jié)為單位,不足一內(nèi)存頁(yè)按一內(nèi)存頁(yè)處理 prot:期望的內(nèi)存保護(hù)標(biāo)志冀膝,不能與文件的打開(kāi)模式?jīng)_突唁奢。是以下的某個(gè)值,可以通過(guò)or運(yùn)算合理地組合在一起 PROT_EXEC //頁(yè)內(nèi)容可以被執(zhí)行 PROT_READ //頁(yè)內(nèi)容可以被讀取 PROT_WRITE //頁(yè)可以被寫(xiě)入 PROT_NONE //頁(yè)不可訪問(wèn) flags:指定映射對(duì)象的類型窝剖,映射選項(xiàng)和映射頁(yè)是否可以共享驮瞧。它的值可以是一個(gè)或者多個(gè)以下位的組合體 fd:有效的文件描述詞。一般是由open()函數(shù)返回枯芬,其值也可以設(shè)置為-1论笔,此時(shí)需要指定flags參數(shù)中的MAP_ANON,表明進(jìn)行的是匿名映射。 off_toffset:被映射對(duì)象內(nèi)容的起點(diǎn)千所。

這里的參數(shù)我們要重點(diǎn)關(guān)注3個(gè)length狂魔、prot、flags淫痰。 length代表了我們可以操作的內(nèi)存大凶羁; prot代表我們對(duì)文件的操作權(quán)限待错。這里傳入了讀寫(xiě)權(quán)限籽孙,而且注意要與open()保持一致,所以open()函數(shù)傳入了O_RDWR可讀寫(xiě)權(quán)限火俄;犯建。 flags要寫(xiě)MAP_FILE|MAP_SHARED,我一開(kāi)始只寫(xiě)了MAP_FILE,能讀,但是不能寫(xiě)瓜客。

munmap()

int munmap(void* start,size_t length);

這里對(duì)原來(lái)文件追加寫(xiě)入數(shù)據(jù)要注意一點(diǎn)适瓦,讀取原來(lái)文件之后,我們只有原來(lái)文件大小的可寫(xiě)區(qū)域谱仪。例如以上例子原文件中是AAA玻熙,這時(shí)我們要寫(xiě)入CCCC,做覆蓋寫(xiě)入的話我們只能寫(xiě)入CCC疯攒。所以要要對(duì)文件進(jìn)行追加寫(xiě)入的話嗦随,必須提前增加文件的大小即調(diào)用ftruncate()和sync(),增加了4位了敬尺,最終才能使CCCC順利寫(xiě)入

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末枚尼,一起剝皮案震驚了整個(gè)濱河市肌毅,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌姑原,老刑警劉巖悬而,帶你破解...
    沈念sama閱讀 219,490評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異锭汛,居然都是意外死亡笨奠,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)唤殴,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)般婆,“玉大人,你說(shuō)我怎么就攤上這事朵逝∥蹬郏” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,830評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵配名,是天一觀的道長(zhǎng)啤咽。 經(jīng)常有香客問(wèn)我,道長(zhǎng)渠脉,這世上最難降的妖魔是什么宇整? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,957評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮芋膘,結(jié)果婚禮上鳞青,老公的妹妹穿的比我還像新娘。我一直安慰自己为朋,他們只是感情好臂拓,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,974評(píng)論 6 393
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著习寸,像睡著了一般胶惰。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上融涣,一...
    開(kāi)封第一講書(shū)人閱讀 51,754評(píng)論 1 307
  • 那天童番,我揣著相機(jī)與錄音精钮,去河邊找鬼威鹿。 笑死,一個(gè)胖子當(dāng)著我的面吹牛轨香,可吹牛的內(nèi)容都是我干的忽你。 我是一名探鬼主播,決...
    沈念sama閱讀 40,464評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼臂容,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼科雳!你這毒婦竟也來(lái)了根蟹?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,357評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤糟秘,失蹤者是張志新(化名)和其女友劉穎简逮,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體尿赚,經(jīng)...
    沈念sama閱讀 45,847評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡散庶,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,995評(píng)論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了凌净。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片悲龟。...
    茶點(diǎn)故事閱讀 40,137評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖冰寻,靈堂內(nèi)的尸體忽然破棺而出须教,到底是詐尸還是另有隱情,我是刑警寧澤斩芭,帶...
    沈念sama閱讀 35,819評(píng)論 5 346
  • 正文 年R本政府宣布轻腺,位于F島的核電站,受9級(jí)特大地震影響划乖,放射性物質(zhì)發(fā)生泄漏约计。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,482評(píng)論 3 331
  • 文/蒙蒙 一迁筛、第九天 我趴在偏房一處隱蔽的房頂上張望煤蚌。 院中可真熱鬧,春花似錦细卧、人聲如沸尉桩。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,023評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)蜘犁。三九已至,卻和暖如春止邮,著一層夾襖步出監(jiān)牢的瞬間这橙,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,149評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工导披, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留屈扎,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,409評(píng)論 3 373
  • 正文 我出身青樓撩匕,卻偏偏與公主長(zhǎng)得像鹰晨,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,086評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容