我正在阅读有关内存映射文件的消息,该消息称它比传统方法更快地打开文件或读取文件,例如开放系统调用和读取系统调用,而不给出如何打开或读取系统调用的描述的工作原理。
所以这是我的问题开放系统调用的工作原理?
据我所知,它会将文件加载到内存中,而使用映射文件只会将其地址保存在内存中,并在需要时将所请求的页面放入内存中。
我希望澄清我迄今为止的理解。
修改
我之前的理解几乎是错误的,因为正确的解释是指Pawel接受的答案。
答案 0 :(得分:6)
由于您没有提供任何细节,我假设您对类Unix系统的行为感兴趣。
实际上open()
系统调用只会创建一个文件描述符,然后mmap()
或read()
可以使用该文件描述符。
内存映射I / O和标准I / O都通过页面缓存在内部访问磁盘上的文件,这是一个缓存文件的缓冲区,以减少I / O操作的数量。
标准I / O方法(使用write()
和read()
)涉及执行系统调用,然后将数据从(或写入时)页面缓存复制到应用程序选择的缓冲区。除此之外,非顺序访问还需要另一个系统调用lseek()
。系统调用很昂贵,复制数据也是如此。
当文件通过内存映射时,通常将进程地址空间中的内存区域直接映射到页面缓存,以便可以执行已加载数据的所有读取和写入,而无需任何额外延迟(无系统调用,无数据复制) 。仅当应用程序尝试访问尚未加载的文件区域时,才会发生页面错误,并且内核从磁盘加载所需的数据(整页)。
修改强> 我看到我还要解释记忆paging。在大多数现代架构中,存在物理内存,它是真正的硬件和虚拟内存,它为进程创建地址空间。内核决定虚拟内存中的地址如何映射到物理内存中的地址。最小的单位是一个记忆页面(通常,但不总是4K)。它不必是1:1映射,例如,所有虚拟内存页面都可以映射到相同的物理地址。
在内存映射的应用程序地址空间和内核页面缓存的I / O部分映射到相同的物理内存区域,因此程序可以直接访问页面缓存。
答案 1 :(得分:2)
Pawel精心解释了如何执行读/写操作。让我解释原始问题:fopen(3)如何工作:
当用户空间进程遇到fopen(在libc或任何用户空间库中定义)时,它会将其转换为open(2)系统调用。首先,它接受来自fopen的参数,将它们与open()系统调用号一起写入特定于体系结构的寄存器。这个数字告诉内核系统调用用户空间程序要运行。加载这些寄存器后,用户空间进程会中断内核(通过softirq
,传统上是x86上的INT 80H)和阻塞。
内核验证提供的参数和访问权限等,然后返回错误或调用实际系统调用,在这种情况下为vfs_open()
。 vfs_open()
检查fd数组中的可用文件描述符并分配struct文件。访问文件的引用计数增加,fd返回给用户程序。这样就完成了open和大多数系统调用的工作。
open()
与read()
/ write()
一起,后跟close()
无疑是比缓冲区缓存中的内存映射文件更长的过程。
答案 2 :(得分:0)
有关Linux上的开放和阅读工作的清晰解释,您可以阅读this。代码片段来自内核的旧版本,但理论仍然存在。
您仍然需要使用open()系统调用来获取有效的文件描述符,您将传递给mmap()。至于为什么mmaped IO更快,这是因为没有(从)用户空间到(来自)内核空间缓冲区的数据副本,这是读写系统调用所发生的。