Boot

引導期間 initramfs gzip’ed cpio 歸檔的定址方案

  • July 13, 2018

以下引導文件 (vmlinux64) 適用於 Linux 核心 v2.6.21.7(發行版: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 存檔。

根據: Kernel.orgWikipedia,Linux Kernel cpio 提取器將這個 init.ramfs 部分解壓縮到記憶體中的某個位置。

我的問題是:

  1. 是什麼決定了提取 cpio 存檔內容的記憶體地址?
  2. 提取後,核心如何找到特定文件的數據的記憶體地址,…例如 /sbin/init 文件?
  3. cpio 存檔的內容是否被提取到某種文件系統中,允許核心稍後找到這些文件……或者這些文件的記憶體地址是否在核心程式碼中硬編碼?

回复:問題 1:我不認為 .init.ramfs 部分可以解壓縮到 ELF 文件的部分標題中所述的 0xffffffff804e9000 記憶體地址,因為在下一部分之前只有 1560kB 可用空間(“. sbss") 從記憶體中的 0xffffffff8066f000 開始,而 ungzip 的 cpio 存檔佔用 4035kB。

cpio 存檔的內容是否被提取到某種文件系統中,允許核心稍後找到這些文件……或者這些文件的記憶體地址是否在核心程式碼中硬編碼?

進入文件系統。使用的文件系統類型是ramfstmpfs。在您提到的連結之一中對此進行了詳細說明。

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

ramfs 和 tmpfs 的工作方式非常相似;這個問題沒有太大區別。initramfs 使用哪種類型可能會有所不同,例如在核心版本之間(如果您需要知道,請閱讀此內容)。在 initramfs 之外,一般規則是始終使用 tmpfs。tmpfs 限制最大空間使用量,以防止記憶體不足和系統崩潰。


Rootfs 是 ramfs(或 tmpfs,如果已啟用)的特殊實例,它始終存在於 2.6 系統中

$$ … $$ 什麼是initramfs?

所有 2.6 Linux 核心都包含一個 gzip 壓縮的“cpio”格式存檔,當核心啟動時,它會被提取到 rootfs 中。


根據:Kernel.org 和 Wikipedia,Linux Kernel cpio 提取器將這個 init.ramfs 部分解壓在記憶體中的某個位置

$$ … $$

“ramfs-rootfs-initramfs.txt”的第一部分解釋了ramfs文件數據是在**頁面記憶體中分配的,這與用於記憶體來自物理文件系統的文件數據的結構相同。ramfs 文件頁面也可以換出到交換設備,就像程序記憶體一樣。

我可以告訴你,頁面記憶體非常接近最低級別的分配器,即核心頁面分配器。頁面分配器在啟動時被交給所有可用的物理 RAM 區域;這些將排除初始核心部分。可用的物理記憶體區域由引導載入程序/韌體傳遞給核心。如命令所示,您應該在核心日誌早期的一系列行中看到這些區域dmesg

稍後在核心日誌中,當頁面分配器被交給不再需要的 init mem 時,您可以看到該消息,包括已釋放的 init.ramfs 部分。(IIRC 在頁面分配器之前有一些單獨的非常早期的分配器,但這不是引導 IMO 中最令人興奮的細節)。

  1. 解壓後,核心如何找到特定文件的數據的記憶體地址,…例如 /sbin/init 文件?

頁面記憶體從記憶體中的 inode 連結,也就是vnodes通過記憶體中的dentry 記憶體查找vnode 。 dentry = 記憶體中的目錄條目。

什麼是ramfs?

Ramfs 是一個非常簡單的文件系統,它將 Linux 的磁碟記憶體機制(頁面記憶體和目錄記憶體)導出為基於 RAM 的可動態調整大小的文件系統。

通常所有文件都被 Linux 記憶體在記憶體中。從備份儲存(通常是掛載文件系統的塊設備)讀取的數據頁面會保留下來,以防再次需要它,但如果虛擬記憶體系統需要記憶體用於其他用途,則將其標記為乾淨(可釋放)。類似地,寫入文件的數據在寫入備份儲存後立即標記為乾淨,但出於記憶體目的而保留,直到 VM 重新分配記憶體。類似的機制(dentry 記憶體)極大地加快了對目錄的訪問。

使用 ramfs,沒有備份儲存。寫入 ramfs 的文件照常分配目錄和頁面記憶體,但無處可寫入。這意味著這些頁面永遠不會被標記為乾淨,因此當 VM 尋求回收記憶體時,它們不能被釋放。


回复:問題 1:我不認為 .init.ramfs 部分可以解壓縮到

它可以解壓縮到 RAM 中任何地方的臨時緩衝區,例如使用頁面分配器。也就是說,我假設提取過程是流式傳輸的。也就是說,它可以使用與gzip -d | cpio --extract. 在將文件從存檔複製到 tmpfs 時,這種方法避免了需要一個緩衝區來保存整個未壓縮的 cpio 存檔。

引用自:https://unix.stackexchange.com/questions/455003