我对 Linux mmap 文件到主内存的方式感兴趣(在我的上下文中它用于执行,但我猜 mmap 过程对于写入和读取也是相同的)以及它使用的大小。
所以我知道 Linux 使用通常 4kB 页面大小的分页(在内核中的哪里可以找到这个大小?)。但这对于分配的内存到底意味着什么:假设您有一个大小为几千字节的二进制文件,假设为 5812B,然后执行它。内核中会发生什么:它是否分配 2*4kB,然后将 5812B 复制到该空间中,在第 2 页中浪费了 >3KB 的主内存?
如果有人知道内核源代码中定义页面大小的文件,那就太好了。
我想我的第二个问题也很简单:我假设 5812B 作为文件大小。这个大小是直接从索引节点获取的吗?
答案1
可执行文件的大小和内存中的大小之间没有直接关系。以下是执行二进制文件时发生的情况的快速概述:
- 内核解析该文件并将其分成几个部分。有些部分直接加载到内存中的单独页面中。有些部分根本没有加载(例如调试符号)。
- 如果可执行文件是动态链接的,则内核调用动态加载器,加载所需的共享库并根据需要执行链接编辑。
- 程序开始执行其代码,通常会请求更多内存来存储数据。
有关可执行文件格式、链接和可执行文件加载的更多信息,您可以阅读链接器和加载器作者:约翰·R·莱文。
在 5kB 的可执行文件中,除了标头之外,很可能所有内容都是需要加载到内存中的代码或数据。可执行代码至少有一页,也许有两页,然后至少有一页用于堆栈,可能一页或用于堆(其他数据),加上共享库使用的内存。
在 Linux 下,您可以检查可执行文件的内存映射cat /proc/$pid/maps
。该格式记录在proc(5)
手册页;也可以看看了解 Linux /proc/id/maps。
答案2
是的:您最终会得到两个 4k 页面。数据是按需加载的,因此如果没有任何内容引用第二页,它将永远不会加载到内存中。
include/asm-i386/param.h:#define EXEC_PAGESIZE 4096
include/asm-i386/elf.h:#define ELF_EXEC_PAGESIZE 4096
不要改变这些值并期望任何事情都能起作用。
是的,文件大小存储在ext2/3的inode中。
答案3
对于定义部分,在 Intel 架构的 2.6.38~ish 内核上:
arch/x86/include/asm/page_types.h:
/* PAGE_SHIFT决定页面大小 */
#定义 PAGE_SHIFT 12
#define PAGE_SIZE (_AC(1,UL) << PAGE_SHIFT)