文章目录
- 3.2_5 内存映射文件
- (一)传统的文件访问方式
- (二)内存映射文件(Memory-Mapped Files)
- 总结
3.2_5 内存映射文件
(一)传统的文件访问方式
磁盘的存储是以块为单位的,如图,每一块的大小是1KB,那相应的,这个文件就会被拆分成若干个大小相等的块、每个块是1KB,刚好可以一一对应地放到磁盘里。
如上图,有一个
葵花宝典.txt
文件,大小是3KB。
这些块有可能被离散地放到磁盘的各个角落。
如果一个进程想要访问这个文件的数据,应该怎么做呢?
传统的方法是这样的:首先,每个进程拥有自己的虚拟地址空间,如果这个进程想要访问这个文件中的数据。它首先要使用open系统调用,来指明打开这个文件。接下来要使用seek系统调用,来指明它想要读取这个文件的哪一部分数据,操作系统会用一个读写指针来记录这个位置。接下来,进程可以使用read系统调用,来指明从当前读写指针的位置往后想要读入多少的数据。
比如,此时该进程从磁盘读取了该文件的
2
部分数据,读入内存,就可以进行访问、修改。如果对这块数据进行了修改,进程想要保存这部分数据,那么还需要使用write系统调用,来把这部分数据给写回磁盘。
传统的文件访问方式:
open系统调用——打开文件;
seek系统调用——将读写指针移到某个位置;
read系统调用——从读写指针所指位置读入若干数据(从磁盘读入内存);
write系统调用——将内存中的指定数据写回磁盘(根据读写指针确定要写回什么位置)。
在了解了传统的文件访问方式后,感觉很麻烦。那有没有什么方法可以让文件的访问变得更简单呢?——就有了内存映射文件。
(二)内存映射文件(Memory-Mapped Files)
内存映射文件——操作系统向上层程序员提供的功能(系统调用)
1.方便程序员访问文件数据;
2.方便多个进程共享同一个文件。
如果一个系统支持内存映射文件,那么一个程序员要访问一个文件,就会变得更简单。
内存映射文件的访问方式:
open系统调用——打开文件;
mmap系统调用——将文件映射到进程的虚拟地址空间。
这个系统调用会给程序员返回一个指针,这个指针指向刚才映射的这片区域的起始地址。
接下来,就可以用访问内存的方式去访问这些文件数据了。
什么意思呢?
我们都写过C语言程序。如果给你一个起始地址、一个指针,那么你就可以用这个指针再加上地址偏移量,去访问这个起始地址后面的某些区域。
所以,只要mmap系统调用,返回了这片区域的起始地址,那么就可以利用指针去访问到这个文件当中的任何一块数据。
需要注意的是,使用mmap系统调用之后,只是建立了文件数据和内存之间的一个映射关系,但并没有把文件数据给直接读入内存。——这就相当于一个缺页的状态。
接下来的逻辑是这样的。假设此时想访问2
部分的文件数据了。由于它只是一个映射关系,而真正的数据并没有调入内存,因此会引发一个缺页,操作系统这时会把磁盘中的文件数据调入内存。
对于第3
块数据也是同理,如果你想要访问它时,操作系统便会自动地把相应数据帮你调入到内存中。
也就是说,作为程序员,我们不需要再调用read去读入数据了。读入数据的过程,是由操作系统自动帮我们完成的。
那么,数据既然读入内存,我们的进程就可以对其进行访问、修改。比如我修改了第2
块数据。
最后,如果进程不再需要使用这个文件,那么系统可以使用close系统调用来关闭这个文件。当它关闭文件之后,操作系统会自动地把文件当中被修改的数据给写回磁盘。
可以看到,采用内存映射文件之后,程序员对文件数据的访问就方便多了。它只需要知道这个文件在内存当中的起始地址。接下来,按照访问内存的方式去访问这个文件当中的数据就可以了。至于文件数据的读入、写出,都是由操作系统自动来完成的。
相比之下,传统文件访问方式,需要程序员自己去调用read、write系统调用,才可以读入、写出文件的数据。
内存映射文件的访问方式
1.open系统调用——打开文件;
2.mmap系统调用——将文件映射到进程的虚拟地址空间;
3.close系统调用——关闭文件。
说明:
①以访问内存的方式访问文件数据;
②文件数据的读入、写出由操作系统自动完成;
③进程关闭文件时,操作系统自动将文件被修改的数据写回磁盘。
至此,我们已经理解了“内存映射文件”的第一个作用——方便程序员访问文件数据。
接下来,我们再介绍它的第二个作用——方便多个进程共享同一个文件,是如何实现的。
至此我们已经知道,对于一个文件葵花宝典.txt
,它可以被进程1
映射到自己的进程虚拟地址空间里。
那么同样地,进程2
也可以把这个文件映射到自己的虚拟地址空间里。
此时,两个进程的虚拟地址空间是相互独立的,但是操作系统会把这两块虚拟地址空间映射到相同的物理内存上。
这不难实现,操作系统只需修改进程页表中对应的信息,将这些进程的页面映射到对应的页框上即可。——就能实现多个进程共享同一份文件数据。
在这种情况下,当一个进程修改了文件的数据之后,另一个进程立马也可以看到这块文件数据的改变。
如图,
进程1
对第2
块数据作了修改,那么会导致物理内存中的第2
块数据的改变,从而也会导致进程2
对第2
块数据的访问结果发生改变。
在物理内存中,一个文件对应同一份数据,当一个进程修改文件数据时,另一个进程可以立马“看到”。
总结
I/O效率的优化由操作系统负责,比如可以使用“预读入”、“缓写出”等策略。