启动期间 initramfs gzip 的 cpio 存档的寻址方案

启动期间 initramfs gzip 的 cpio 存档的寻址方案

Linux 内核 v2.6.21.7 的以下引导文件 (vmlinux64)(发行版:Cavium-Octeon for MIPS64):

ELF HEADER:
------------------------------------------
Magic: 0x7f 0x45 0x4c 0x46 ("ELF")
Class: 64-bit
Encoding: Big-Endian
ELF version: 1
OS ABI: System V
ABI Version: 0
Type: ET_EXEC
Machine: MIPS
Version: 1
Entry Point: 0xffffffff804b0000
Program Headers Offset: 0x40
Section Headers Offset: 0x572C70
Flags: 0x808b0001
ELF Header Size: 0x40
Program Header Entry Size: 0x38
Program Header Entries: 1
Section Header Entry Size: 0x40
Section Header Entries: 0x21
.shstrtab Index: 0x20

有这些段和部分:

_______________________________________________________________________________________________
PROGRAM HEADERS:
_______________________________________________________________________________________________
Index Type    Flags           SizeInMem  MemVirtAddress      FileOffs  SizeInFile
-----------------------------------------------------------------------------------------------
0    PT_LOAD  Write+Read+Exec 0x5AB200   0xffffffff80100000  0x4000    0x56EAC7                                           

_______________________________________________________________________________________________
SECTION HEADERS:
_______________________________________________________________________________________________
Index Name                   Type        Flags        MemVirtAddress      FileOffs  SizeInFile
-----------------------------------------------------------------------------------------------
0                            K_NULL                                  0x0       0x0       0x0
1   .text                    K_PROGBITS  Alloc+Exec   0xffffffff80100000    0x4000  0x30DFE8
2   __ex_table               K_PROGBITS  Alloc+       0xffffffff8040dff0  0x311FF0    0x5EA0
3   __dbe_table              K_PROGBITS  Alloc+       0xffffffff80413e90  0x317E90       0x0
4   .rodata                  K_PROGBITS  Alloc+       0xffffffff80414000  0x318000   0x48B68
5   .pci_fixup               K_PROGBITS  Alloc+       0xffffffff8045cb68  0x360B68     0xB20
7   __ksymtab                K_PROGBITS  Alloc+       0xffffffff8045d688  0x361688    0x8EA0
8    __ksymtab_gpl           K_PROGBITS  Alloc+       0xffffffff80466528  0x36A528    0x2580
17  __ksymtab_strings        K_PROGBITS  Alloc+       0xffffffff80468aa8  0x36CAA8    0xEBA8
18   __param                 K_PROGBITS  Alloc+       0xffffffff80477650  0x37B650     0x6E0
19  .data                    K_PROGBITS  Alloc+Write  0xffffffff80478000  0x37C000   0x2FD20
20  .data.cacheline_aligned  K_PROGBITS  Alloc+Write  0xffffffff804a8000  0x3AC000    0x7280
21  .init.text               K_PROGBITS  Alloc+Exec   0xffffffff804b0000  0x3B4000   0x31270
22  .init.data               K_PROGBITS  Alloc+Write  0xffffffff804e1270  0x3E5270    0x3708
23  .init.setup              K_PROGBITS  Alloc+Write  0xffffffff804e4980  0x3E8980     0x5B8
24  .initcall.init           K_PROGBITS  Alloc+Write  0xffffffff804e4f38  0x3E8F38     0x6D8
25  .con_initcall.init       K_PROGBITS  Alloc+Write  0xffffffff804e5610  0x3E9610      0x10
27  .exit.text               K_PROGBITS  Alloc+Exec   0xffffffff804e5620  0x3E9620    0x30C0
28  .init.ramfs              K_PROGBITS  Alloc+       0xffffffff804e9000  0x3ED000  0x185AC7
32  .shstrtab                K_STRTAB                                0x0  0x572AC7     0x1A7
6   .rio_route               K_PROGBITS  Write        0xffffffff8045d688  0x572AC7       0x0
9   __ksymtab_unused         K_PROGBITS  Write        0xffffffff80468aa8  0x572AC7       0x0
10  __ksymtab_unused_gpl     K_PROGBITS  Write        0xffffffff80468aa8  0x572AC7       0x0
11  __ksymtab_gpl_future     K_PROGBITS  Write        0xffffffff80468aa8  0x572AC7       0x0
12  __kcrctab                K_PROGBITS  Write        0xffffffff80468aa8  0x572AC7       0x0
13  __kcrctab_gpl            K_PROGBITS  Write        0xffffffff80468aa8  0x572AC7       0x0
14  __kcrctab_unused         K_PROGBITS  Write        0xffffffff80468aa8  0x572AC7       0x0
15  __kcrctab_unused_gpl     K_PROGBITS  Write        0xffffffff80468aa8  0x572AC7       0x0
16  __kcrctab_gpl_future     K_PROGBITS  Write        0xffffffff80468aa8  0x572AC7       0x0
26  .security_initcall.init  K_PROGBITS  Write        0xffffffff804e5620  0x572AC7       0x0
29  .sbss                    K_PROGBITS  Alloc+Write  0xffffffff8066f000  0x572AC7       0x0
30  .bss                     K_NOBITS    Alloc+Write  0xffffffff80670000  0x572AC7   0x3AEF0
31  .cvmx_shared_bss         K_NOBITS    Alloc+Write  0xffffffff806aaef0  0x572AC7     0x310  
_______________________________________________________________________________________________

请注意,此 ELF 文件具有嵌入的 1558kB init.ramfs 部分,其中包含操作系统必需的文件。该部分经过 gzip 压缩,包含一个包含 1805 个文件和目录的 cpio 存档。

根据: 内核.org维基百科,Linux 内核 cpio 提取器将这个 init.ramfs 部分解压到内存中的某个位置。

我的问题是:

  1. 什么决定了提取 cpio 归档内容的内存地址?
  2. 解压后,内核如何找到特定文件(例如 /sbin/init 文件)数据的内存地址?
  3. cpio 存档的内容是否被提取到某种文件系统中,以便内核稍后找到这些文件……或者这些文件的内存地址是否硬编码在内核代码中?

回复:问题 1:我不认为 .init.ramfs 部分可以被解压缩到 ELF 文件的部分标头中指定的 0xffffffff804e9000 内存地址,因为在下一部分之前只有 1560kB 的可用空间(“. sbss”)在内存中从 0xffffffff8066f000 开始,经过 ungzip 压缩的 cpio 存档占用 4035kB。

答案1

cpio 存档的内容是否被提取到某种文件系统中,以便内核稍后找到这些文件……或者这些文件的内存地址是否硬编码在内核代码中?

进入文件系统。使用的文件系统类型是内存文件系统, 或者临时文件系统。您提到的链接之一对此进行了详细解释。

https://github.com/torvalds/linux/blob/v4.17/Documentation/filesystems/ramfs-rootfs-initramfs.txt

ramfs 和 tmpfs 的工作原理非常相似;对于这个问题来说没有太大区别。 initramfs 使用的类型可能会有所不同,例如,不同内核版本之间(如果您需要知道,读这个)。在 initramfs 之外,一般规则是始终使用 tmpfs。 tmpfs 限制最大空间使用量,从而防止 RAM 耗尽和系统崩溃。


Rootfs 是 ramfs (或 tmpfs,如果启用的话) 的一个特殊实例,它始终存在于 2.6 系统中 [...]

什么是 initramfs?

所有 2.6 Linux 内核都包含一个 gzip 压缩的“cpio”格式存档,该存档在内核启动时被提取到 rootfs 中。


根据:Kernel.org 和 Wikipedia,Linux 内核 cpio 提取器将这个 init.ramfs 部分解压到内存中的某个位置 [...]

“ramfs-rootfs-initramfs.txt”的第一部分解释了这一点内存文件系统文件数据分配在页面缓存,与用于缓存物理文件系统中的文件数据的结构相同。 ramfs 文件页面也可以交换到交换设备,就像进程内存一样。

我可以告诉你页面缓存非常接近最低级别的分配器,即内核页面分配器。页面分配器在启动时获得所有可用的物理 RAM 区域;这些将排除初始内核部分。可用的物理 RAM 区域由引导加载程序/固件传递到内核。您应该在早期的一系列行中看到这些区域内核日志,如命令所示dmesg

稍后在内核日志中,您可以看到当页面分配器被移交给不再需要的 init mem 时的消息,包括释放的 init.ramfs 部分。 (IIRC 在页面分配器之前有一些单独的非常早期的分配器,但这并不是引导 IMO 中最令人兴奋的细节)。

2. 解压后,内核如何找到特定文件(例如/sbin/init 文件)的数据的内存地址?

页面缓存从内存中的索引节点链接,也称为虚拟节点。 vnodes通过内存中查找目录项缓存目录项= 缓存中的目录条目。

ramfs是什么?

Ramfs 是一个非常简单的文件系统,它将 Linux 的磁盘缓存机制(页面缓存和 dentry 缓存)导出为基于 RAM 的动态可调整大小的文件系统。

通常,所有文件都被 Linux 缓存在内存中。从后备存储(通常是安装文件系统的块设备)读取的数据页会保留下来,以备再次需要时使用,但会标记为干净(可释放),以防虚拟内存系统需要该内存用于其他用途。同样,写入文件的数据一旦写入后备存储,就会被标记为干净,但会出于缓存目的而保留,直到虚拟机重新分配内存。类似的机制(目录项缓存)极大地加快了对目录的访问速度。

对于 ramfs,没有后备存储。写入 ramfs 的文件照常分配目录项和页面缓存,但没有地方可以写入它们。这意味着这些页面永远不会被标记为干净,因此当虚拟机寻求回收内存时无法释放它们。


回复:问题 1:我认为 .init.ramfs 部分不能解压到

它可以被解压缩到 RAM 中任何地方的临时缓冲区,例如使用页面分配器。也就是说,我假设提取过程是流式的。也就是说,它可以使用类似的方法gzip -d | cpio --extract。当将文件从存档复制到 tmpfs 时,此方法避免需要缓冲区来保存整个未压缩的 cpio 存档。

相关内容