谁能告诉我操作系统的哪个进程将ELF(可执行和链接格式)文件加载到RAM中?
答案1
用户通常会遇到三种类型的 ELF 文件:.o 文件、常规可执行文件和共享库。虽然所有这些文件都有不同的用途,但它们的内部结构文件非常相似。
所有不同 ELF 文件类型(以及 a.out 和许多其他可执行文件格式)中的一个通用概念是节的概念。节是相似类型信息的集合。每个部分代表文件的一部分。例如,可执行代码始终放置在称为。文本;用户初始化的所有数据变量都放置在称为。数据;未初始化的数据被放置在称为.bss。
实际上,人们可以设计一种可执行文件格式,其中所有内容都混杂在一起(例如操作系统)。但是将可执行文件分成多个部分具有重要的优点。例如,一旦将可执行文件的可执行部分加载到内存中,这些内存位置就不需要更改。在现代机器架构上,内存管理器可以将部分内存标记为只读,这样任何修改只读内存位置的尝试都会导致程序死亡并转储核心。因此,我们不仅可以说我们不希望特定的内存位置发生更改,还可以指定任何修改只读内存位置的尝试都是致命错误,表明应用程序中存在错误。话虽这么说,通常您不能单独设置内存的每个字节的只读状态,而是可以单独设置称为页面的内存区域的保护。在 i386 架构上,页面大小为 4096 字节,因此您可以指示地址 0-4095 是只读的,而字节 4096 及以上是可写的。
鉴于我们希望可执行文件的所有可执行部分都位于只读内存中,而所有可修改的内存位置(例如变量)都位于可写内存中,因此将可执行文件的所有可执行部分分组到一个节中是最有效的内存(.text 部分),并将所有可修改的数据区域一起放入另一个内存区域(以下称为.data 部分)。
进一步区分用户已初始化的数据变量和用户未初始化的数据变量。如果用户没有指定变量的初始值,则没有必要浪费可执行文件中的空间来存储该值。因此,已初始化的变量被分组到 .data 部分,未初始化的变量被分组到 .bss 部分,这是特殊的,因为它不占用文件中的空间 - 它只告诉未初始化的变量需要多少空间。
当您要求内核加载并运行可执行文件时,它首先查看映像标头以获取有关如何加载映像的线索。它在可执行文件中找到 .text 部分,将其加载到内存的适当部分,并将这些页面标记为只读。然后,它在可执行文件中找到 .data 部分,并将其加载到用户的地址空间中,这次是在读写内存中。最后,它从映像头中找到 .bss 部分的位置和大小,并将适当的内存页面添加到用户的地址空间。即使用户没有指定 .bss 中变量的初始值,按照惯例,内核也会将所有这些内存初始化为零。
所以你看,实际上是内核发出将可执行文件加载到内存中的命令。作为任何此类调用的结果的文本部分被加载到只读存储器中,并且数据部分被加载到读写存储器中。
答案2
这取决于操作系统。
例如,在 GNU Hurd 上,可执行文件由执行服务器。
在更典型的单片操作系统上,这是通过以下方式完成的:
内核将可执行文件和动态链接器映射到内存中;
动态链接器映射内存中的共享对象。
Linux 内核本身存储为 ELF 文件:该文件由引导加载程序(例如 GRUB)加载。