文件内存映射(mmap)之前看过很多文章提及到,但是都没有写iOS中具体的实现,只是都说对于大文件读写效率比较高等。所以作者就专门研究了以下mmap技术,并且实现了一下
mmap
文件映射是将文件的磁盘扇区映射到进程的虚拟内存空间的过程。一旦被映射,您的应用程序就会访问这个文件,就好像它完全驻留在内存中一样(不占用内存,使用的是虚拟内存)。当您从映射的文件指针读取数据时,将在适当的数据中的内核页面并将其返回给您的应用程序。
疑问
那大家就会想了,既然不消耗内存,那岂不是都用mmap就行了,这样多好啊,又不占内存。其实不然,并不是所有的场景都适合使用mmap的
适合的场景
您有一个很大的文件,其内容您想要随机访问一个或多个时间。
您有一个小文件,它的内容您想要立即读入内存并经常访问。这种技术最适合那些大小不超过几个虚拟内存页的文件。(页是地址空间的最小单位,虚拟页和物理页的大小是一样的,通常为4KB。)
您需要在内存中缓存文件的特定部分。文件映射消除了缓存数据的需要,这使得系统磁盘缓存中的其他数据空间更大。
当随机访问一个非常大的文件时,通常最好只映射文件的一小部分。映射大文件的问题是文件会消耗活动内存。如果文件足够大,系统可能会被迫将其他部分的内存分页以加载文件。将多个文件映射到内存中会使这个问题更加复杂。
不适合的场景
您希望从开始到结束的顺序从头到尾读取一个文件。
这个文件有几百兆字节或者更大。将大文件映射到内存中会快速地填充内存,并可能导致分页,这将抵消首先映射文件的好处。对于大型顺序读取操作,禁用磁盘缓存并将文件读入一个小内存缓冲区。
该文件大于可用的连续虚拟内存地址空间。对于64位应用程序来说,这不是什么问题,但是对于32位应用程序来说,这是一个问题。
该文件位于可移动驱动器上。
该文件位于网络驱动器上。
实现
这个代码实现的功能就是首先读取存储在我们沙盒的文件,然后在该文件的上继续写入数据(追加数据)
#import
"ViewController.h"
import
int
MapFile( char * inPathName,void
** outDataPtr,size_t * outDataLength );
void
ProcessFile( 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);
.mmap文件用MindManager软件可以打开。Mindjet MindManager是一个创造、管理和交流思想的通用标准,其可视化的绘图软件有着直观、友好的用户界面和丰富的功能,这将帮助您有序地进行组织您的思维、资源和项目进程。MindManager也是一。
}
}
/ MapFile
/ Exit: outDataPtra pointer to the mapped memory region
/ outDataLength size of the mapped memory region
手机如何打开mmap,/ return value an errno value on error (see sys/errno.h)
/ or zero for success
int
MapFile( char * inPathName,void
** outDataPtr,size_t * outDataLength )
{
int
outError;
int
fileDescriptor;
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
{
fsync(fileDescriptor);//刷新文件
*outDataPtr = mmap(NULL。
statInfo.st_size+4。
PROT_READ|PROT_WRITE。
MAP_FILE|MAP_SHARED。
fileDescriptor。
0);
将文件通过QQ或者其他方式发给另一台电脑,可以正常打开,然后将文件另存为一份新的mmap文件再发送到本机,即可打开文件。方案三、删除备注。之前有用户应该碰到过所谓参数错误的问题,是通过删除本地模板文件中的备注的方式。
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
.mmap文件可以用MindManager软件打开。使用MindManager软件打开.mmap文件的操作步骤如下:1、首先找到电脑文件夹中的.mmap文件,双击即可打开。2、在弹出的对话框中选项打开方式这个选项。3、然后再选择MindManager软件打开。。
outError;
}
@interface
ViewController ()
1、直接在手机桌面上,点击软件进入,如下图所示。2、需要选择Files跳转,如下图所示。3、如果没问题,就找到相关mmap文件并确定打开,如下图所示。4、这样一来会查到图示的结果,即可实现要求了,如下图所示。
@property (weak,nonatomic) IBOutlet UITextView *mTV;
@end
@implementation ViewController
- (void)viewDidLoad {
[super
viewDidLoad];
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);
self.mTV.text = result;
}
@end
最重要的就是2个函数:
mmap()
start:映射区的开始地址,设置为0时表示由系统决定映射区的起始地址。 length:映射区的长度。//长度单位是 以字节为单位,不足一内存页按一内存页处理 prot:期望的内存保护标志,不能与文件的打开模式冲突。是以下的某个值,可以通过or运算合理地组合在一起 PROT_EXEC //页内容可以被执行 PROT_READ //页内容可以被读取 PROT_WRITE //页可以被写入 PROT_NONE //页不可访问 flags:指定映射对象的类型,映射选项和映射页是否可以共享。它的值可以是一个或者多个以下位的组合体 fd:有效的文件描述词。一般是由open()函数返回,其值也可以设置为-1,此时需要指定flags参数中的MAP_ANON,表明进行的是匿名映射。 off_toffset:被映射对象内容的起点。
munmap()