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 部分解压到内存中的某个位置。
我的问题是:
- 什么决定了提取 cpio 归档内容的内存地址?
- 解压后,内核如何找到特定文件(例如 /sbin/init 文件)数据的内存地址?
- 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 存档。