Linux啟動分析之Initramfs
在前面已經分析了rootfs的掛載,解決了VFS架構下原始掛載點的問題,也提到了Initramfs檔案包的填充,這裡記下如何實現Initramfs填充
一、Initramfs概述
1.initrd
在早期的linux系統中,一般只有硬碟或者軟盤被用來作為linux根檔案系統的儲存裝置,因此也就很容易把這些裝置的驅動程式整合到核心中。但是現在的嵌入式系統中可能將根檔案系統儲存到各種儲存裝置上,包括scsi、sata,u-disk等等。因此把這些裝置的驅動程式碼全部編譯到核心中顯然就不是很方便。
為了解決這一矛盾,於是出現了基於ramdisk的initrd( bootloader initialized RAM disk )。Initrd是一個被壓縮過的小型根目錄,這個目錄中包含了啟動階段中必須的驅動模組,可執行檔案和啟動指令碼。當系統啟動的時候,bootloader會把initrd檔案讀到記憶體中,然後把initrd檔案在記憶體中的起始地址和大小傳遞給核心。核心在啟動初始化過程中會解壓縮initrd檔案,然後將解壓後的initrd掛載為根目錄,然後執行根目錄中的/linuxrc指令碼(cpio格式的initrd為/init,而image格式的initrd<也稱老式塊裝置的initrd或傳統的檔案映象格式的initrd>為/initrc),您就可以在這個指令碼中載入realfs(真實檔案系統)存放裝置的驅動程式以及在/dev目錄下建立必要的裝置節點。這樣,就可以mount真正的根目錄,並切換到這個根目錄中來。
2.Initramfs
在linux2.5中出現了initramfs,它的作用和initrd類似,只是和核心編譯成一個檔案(該initramfs是經過gzip壓縮後的cpio格式的資料檔案),該cpio格式的檔案被連結進了核心中特殊的資料段.init.ramfs上,其中全域性變數__initramfs_start和__initramfs_end分別指向這個資料段的起始地址和結束地址。核心啟動時會對.init.ramfs段中的資料進行解壓,然後使用它作為臨時的根檔案系統。
二、Initramfs載入kernel/init/main.c
static noinline void __init_refok rest_init(void) { ...... kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND); //核心執行緒0 ...... pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES); //核心執行緒 ...... init_idle_bootup_task(current); ...... schedule(); //kernel/kernel/sched.c 程序排程 ...... }
由核心程序0建立的核心執行緒裝載可執行程式init,成為一個普通程序——1號程序
static int __init kernel_init(void * unused) { ...... do_basic_setup(); //初始化裝置驅動,載入靜態核心模組;釋放ramdisk到rootfs ...... if (!ramdisk_execute_command) ramdisk_execute_command = "/init"; if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) { ramdisk_execute_command = NULL; prepare_namespace(); //載入磁碟檔案系統;也即磁碟的檔案系統掛載至rootfs的/root目錄,並重新裝置系統根檔案系統和根目錄 } init_post(); //啟動init程序 ...... }
核心靜態模組載入、驅動載入等
static void __init do_basic_setup(void)
{
cpuset_init_smp();
usermodehelper_init();
init_tmpfs();
driver_init(); //裝置驅動初始化 kernel/drivers/base/init.c
init_irq_proc();
do_ctors();
do_initcalls(); //載入核心模組
}
static void __init do_initcalls(void)
{
......
for (fn = __early_initcall_end; fn < __initcall_end; fn++)
do_one_initcall(*fn);
......
}
在do_initcalls中,是否支援ramdisk取決與kernel/init/Makefile;以下是釋放ramdisk至rootfs:
kernel/init/initramfs.c
rootfs_initcall(populate_rootfs);
static int __init populate_rootfs(void)
{
char *err = unpack_to_rootfs(__initramfs_start, __initramfs_size); //解壓Initramfs
......
if (initrd_start) { //uboot將啟動ramdisk檔案系統拷貝到了initrd_start處
......
err = unpack_to_rootfs((char *)initrd_start,
initrd_end - initrd_start);
......
fd = sys_open((const char __user __force *) "/initrd.image",
O_WRONLY|O_CREAT, 0700);
if (fd >= 0) {
sys_write(fd, (char *)initrd_start,
initrd_end - initrd_start);
sys_close(fd);
free_initrd();
}
......
}
return 0;
}
init程序啟動
kernel/init/main.c
static noinline int init_post(void)
{
......
if (ramdisk_execute_command) { //判斷initramfs是否有init可執行檔案
(void) sys_dup(0);
run_init_process(ramdisk_execute_command); //執行initramfs的init
printk(KERN_WARNING "Failed to execute %s\n",
ramdisk_execute_command);
}
......
}
static void run_init_process(const char *init_filename)
{
argv_init[0] = init_filename;
kernel_execve(init_filename, argv_init, envp_init); //核心空間呼叫使用者空間函式kernel_execve
}
三、Initramfs實現
使用initramfs的核心配置(使用initramfs做根檔案系統):
------------------------------------------------------
General setup --->
[*] Initial RAM filesystem and RAM disk (initramfs/initrd) support
(/rootfs_dir) Initramfs source file(s) //輸入根檔案系統的所在目錄,就和平時用的檔案系統一樣就可,用busybox生成的
這樣把initramfs編譯到核心中,會導致最後生成的uImage會比平時大許多,所以得修改uboot的讀取引數,不然uboot會出現校驗失敗
uboot/include/configs/mx6q_sabresd.h
#define CONFIG_EXTRA_ENV_SETTINGS \
"mmc read ${loadaddr} 0x800 0x5800; bootm\0" \ //把0x5800換成uImage的大小
"bootcmd=run bootcmd_mmc\0"
啟動log:
U-Boot 2009.08 (Dec 20 2016 - 11:10:25)
CPU: Freescale i.MX6 family TO1.5 at 792 MHz
Thermal sensor with ratio = 183
Temperature: 29 C, calibration data 0x5884df7d
mx6q pll1: 792MHz
mx6q pll2: 528MHz
mx6q pll3: 480MHz
mx6q pll8: 50MHz
ipg clock : 66000000Hz
ipg per clock : 66000000Hz
uart clock : 80000000Hz
cspi clock : 60000000Hz
ahb clock : 132000000Hz
axi clock : 264000000Hz
emi_slow clock: 132000000Hz
ddr clock : 528000000Hz
usdhc1 clock : 198000000Hz
usdhc2 clock : 198000000Hz
usdhc3 clock : 198000000Hz
usdhc4 clock : 198000000Hz
nfc clock : 24000000Hz
Board: i.MX6Q-SABRESD: unknown-board Board: 0x63015 [WDOG ]
Boot Device: MMC
I2C: ready
DRAM: 1 GB
MMC: FSL_USDHC: 0,FSL_USDHC: 1,FSL_USDHC: 2,FSL_USDHC: 3
*** Warning - bad CRC or MMC, using default environment
In: serial
Out: serial
Err: serial
i2c: I2C3 SDA is low, start i2c recovery...
I2C3 Recovery failed, I2C1 SDA still low!!!
Net: got MAC address from IIM: 00:00:00:00:00:00
FEC0 [PRIME]
Hit any key to stop autoboot: 0
mmc3(part 0) is current device
MMC read: dev # 3, block # 2048, count 22528 ... 22528 blocks read: OK
## Booting kernel from Legacy Image at 10800000 ...
Image Name: Linux-3.0.35-2666-gbdde708-g1a19
Image Type: ARM Linux Kernel Image (uncompressed)
Data Size: 5621116 Bytes = 5.4 MB
Load Address: 10008000
Entry Point: 10008000
Verifying Checksum ... OK
Loading Kernel Image ... OK
OK
Starting kernel ...
Uncompressing Linux... done, booting the kernel.
[ 0.000000] Linux version 3.0.35-2666-gbdde708-g1a194ba-dirty ([email protected]) (gcc version 4.6.2 20110630 (prerelease) (Freescale MAD -- Linaro 2011.07 -- Built6
[ 0.000000] CPU: ARMv7 Processor [412fc09a] revision 10 (ARMv7), cr=10c53c7d
[ 0.000000] CPU: VIPT nonaliasing data cache, VIPT aliasing instruction cache
[ 0.000000] Machine: Freescale i.MX 6Quad/DualLite/Solo Sabre-SD Board
[ 0.000000] Ignoring unrecognised tag 0x54410008
[ 0.000000] Memory policy: ECC disabled, Data cache writealloc
[ 0.000000] CPU identified as i.MX6Q, unknown revision
[ 0.000000] PERCPU: Embedded 7 pages/cpu @8c008000 s4928 r8192 d15552 u32768
[ 0.000000] Built 1 zonelists in Zone order, mobility grouping on. Total pages: 194560
[ 0.000000] Kernel command line: console=ttymxc0,115200 rdinit=/init ip=dhcp root=/dev/mmcblk0p1 rootwait
[ 0.000000] PID hash table entries: 4096 (order: 2, 16384 bytes)
[ 0.000000] Dentry cache hash table entries: 131072 (order: 7, 524288 bytes)
[ 0.000000] Inode-cache hash table entries: 65536 (order: 6, 262144 bytes)
[ 0.000000] Memory: 512MB 256MB = 768MB total
[ 0.000000] Memory: 761096k/761096k available, 287480k reserved, 0K highmem
[ 0.000000] Virtual kernel memory layout:
[ 0.000000] vector : 0xffff0000 - 0xffff1000 ( 4 kB)
[ 0.000000] fixmap : 0xfff00000 - 0xfffe0000 ( 896 kB)
[ 0.000000] DMA : 0xf4600000 - 0xffe00000 ( 184 MB)
[ 0.000000] vmalloc : 0xc0800000 - 0xf2000000 ( 792 MB)
[ 0.000000] lowmem : 0x80000000 - 0xc0000000 (1024 MB)
[ 0.000000] pkmap : 0x7fe00000 - 0x80000000 ( 2 MB)
[ 0.000000] modules : 0x7f000000 - 0x7fe00000 ( 14 MB)
[ 0.000000] .init : 0x80008000 - 0x80789000 (7684 kB)
[ 0.000000] .text : 0x80789000 - 0x80f4bc6c (7948 kB)
[ 0.000000] .data : 0x80f4c000 - 0x80f8ca20 ( 259 kB)
[ 0.000000] .bss : 0x80f8ca44 - 0x80fd01c0 ( 270 kB)
[ 0.000000] SLUB: Genslabs=13, HWalign=32, Order=0-3, MinObjects=0, CPUs=4, Nodes=1
[ 0.000000] Preemptible hierarchical RCU implementation.
[ 0.000000] NR_IRQS:624
[ 0.000000] MXC GPIO hardware
[ 0.000000] sched_clock: 32 bits at 3000kHz, resolution 333ns, wraps every 1431655ms
[ 0.000000] arm_max_freq=1GHz
[ 0.000000] MXC_Early serial console at MMIO 0x2020000 (options '115200')
[ 0.000000] bootconsole [ttymxc0] enabled
[ 0.000000] Console: colour dummy device 80x30
[ 0.224901] Calibrating delay loop... 1581.05 BogoMIPS (lpj=7905280)
[ 0.313230] pid_max: default: 32768 minimum: 301
[ 0.318163] Mount-cache hash table entries: 512
[ 0.323437] CPU: Testing write buffer coherency: ok
[ 0.328591] hw perfevents: enabled with ARMv7 Cortex-A9 PMU driver, 7 counters available
[ 0.429107] CPU1: Booted secondary processor
[ 0.509115] CPU2: Booted secondary processor
[ 0.589117] CPU3: Booted secondary processor
[ 0.628627] Brought up 4 CPUs
[ 0.644453] SMP: Total of 4 processors activated (6324.22 BogoMIPS).
[ 0.668056] print_constraints: dummy:
[ 0.675312] print_constraints: vddpu: 725 <--> 1300 mV at 700 mV fast normal
[ 0.682754] print_constraints: vddcore: 725 <--> 1300 mV at 1150 mV fast normal
[ 0.690473] print_constraints: vddsoc: 725 <--> 1300 mV at 1200 mV fast normal
[ 0.698097] print_constraints: vdd2p5: 2000 <--> 2775 mV at 2400 mV fast normal
[ 0.705815] print_constraints: vdd1p1: 800 <--> 1400 mV at 1100 mV fast normal
[ 0.713443] print_constraints: vdd3p0: 2625 <--> 3400 mV at 3000 mV fast normal
[ 0.738582] No AHCI save PWR: PDDQ disabled
[ 0.768496] hw-breakpoint: found 6 breakpoint and 1 watchpoint registers.
[ 0.775329] hw-breakpoint: 1 breakpoint(s) reserved for watchpoint single-step.
[ 0.782685] hw-breakpoint: maximum watchpoint size is 4 bytes.
[ 0.788577] L310 cache controller enabled
[ 0.792610] l2x0: 16 ways, CACHE_ID 0x410000c7, AUX_CTRL 0x02070000, Cache size: 1048576 B
[ 0.812161] bio: create slab <bio-0> at 0
[ 0.818995] mxs-dma mxs-dma-apbh: initialized
[ 0.823647] print_constraints: vmmc: 3300 mV
[ 0.829207] SCSI subsystem initialized
[ 0.848664] imx-ipuv3 imx-ipuv3.0: IPU DMFC NORMAL mode: 1(0~1), 5B(4,5), 5F(6,7)
[ 0.868661] imx-ipuv3 imx-ipuv3.1: IPU DMFC NORMAL mode: 1(0~1), 5B(4,5), 5F(6,7)
[ 0.876494] mxc_mipi_csi2 mxc_mipi_csi2: i.MX MIPI CSI2 driver probed
[ 0.882977] mxc_mipi_csi2 mxc_mipi_csi2: i.MX MIPI CSI2 dphy version is 0x3130302a
[ 0.890708] MIPI CSI2 driver module loaded
[ 0.894839] Switching to clocksource mxc_timer1
[ 0.948280] PMU: registered new PMU device of type 0
[ 0.953410] Static Power Management for Freescale i.MX6
[ 0.958653] wait mode is enabled for i.MX6
[ 0.962947] cpaddr = c0880000 suspend_iram_base=c0914000
[ 0.968360] PM driver module loaded
[ 0.972064] IMX usb wakeup probe
[ 1.000189] JFFS2 version 2.2. (NAND) �© 2001-2006 Red Hat, Inc.
[ 1.006911] msgmni has been set to 1486
[ 1.012487] alg: No test for stdrng (krng)
[ 1.016762] io scheduler noop registered
[ 1.020723] io scheduler deadline registered
[ 1.025092] io scheduler cfq registered (default)
[ 1.030536] mxc_mipi_dsi mxc_mipi_dsi: i.MX MIPI DSI driver probed
[ 1.036866] MIPI DSI driver module loaded
[ 1.041102] mxc_sdc_fb mxc_sdc_fb.0: register mxc display driver ldb
[ 1.047498] _regulator_get: get() with no identifier
[ 1.079520] imx-ipuv3 imx-ipuv3.0: IPU DMFC DP HIGH RESOLUTION: 1(0,1), 5B(2~5), 5F(6,7)
[ 1.133846] Console: switching to colour frame buffer device 128x37
[ 1.171846] mxc_sdc_fb mxc_sdc_fb.1: register mxc display driver ldb
[ 1.183836] mxc_sdc_fb mxc_sdc_fb.2: register mxc display driver lcd
[ 1.190251] mxc_sdc_fb mxc_sdc_fb.2: ipu0-di0 already in use
[ 1.195937] mxc_sdc_fb: probe of mxc_sdc_fb.2 failed with error -16
[ 1.202256] mxc_sdc_fb mxc_sdc_fb.3: register mxc display driver ldb
[ 1.208673] mxc_sdc_fb mxc_sdc_fb.3: ipu0-di1 already in use
[ 1.214371] mxc_sdc_fb: probe of mxc_sdc_fb.3 failed with error -16
[ 1.221403] imx-sdma imx-sdma: loaded firmware 1.1
[ 1.230668] imx-sdma imx-sdma: initialized
[ 1.368964] Serial: IMX driver
[ 1.372179] imx-uart.2: ttymxc2 at MMIO 0x21ec000 (irq = 60) is a IMX
[ 1.379036] imx-uart.0: ttymxc0 at MMIO 0x2020000 (irq = 58) is a IMX
[ 1.385540] console [ttymxc0] enabled, bootconsole disabled
[ 1.385540] console [ttymxc0] enabled, bootconsole disabled
[ 1.400637] GPMI NAND driver registered. (IMX)
[ 1.406120] snvs_rtc snvs_rtc.0: rtc core: registered snvs_rtc as rtc0
[ 1.412799] i2c /dev entries driver
[ 1.417082] Linux video capture interface: v2.00
[ 1.620091] TCH2825 probe
[ 1.623150] mxc_v4l2_output mxc_v4l2_output.0: V4L2 device registered as video16
[ 1.630840] mxc_v4l2_output mxc_v4l2_output.0: V4L2 device registered as video17
[ 1.638468] mxc_v4l2_output mxc_v4l2_output.0: V4L2 device registered as video18
[ 1.646370] imx2-wdt imx2-wdt.0: IMX2+ Watchdog Timer enabled. timeout=60s (nowayout=1)
[ 1.654641] sdhci: Secure Digital Host Controller Interface driver
[ 1.660842] sdhci: Copyright(c) Pierre Ossman
[ 1.665535] mmc0: SDHCI controller on platform [sdhci-esdhc-imx.3] using DMA
[ 1.672659] sdhci sdhci-esdhc-imx.1: no card-detect pin available!
[ 1.684683] mmc1: SDHCI controller on platform [sdhci-esdhc-imx.1] using DMA
[ 1.691800] sdhci sdhci-esdhc-imx.2: no write-protect pin available!
[ 1.708310] mmc2: SDHCI controller on platform [sdhci-esdhc-imx.2] using DMA
[ 1.715621] mxc_vdoa mxc_vdoa: i.MX Video Data Order Adapter(VDOA) driver probed
[ 1.729799] VPU initialized
[ 1.737679] mxc_asrc registered
[ 1.741041] Galcore version 4.6.9.6622
[ 1.773390] Thermal calibration data is 0x5884df7d
[ 1.778187] Thermal sensor with ratio = 183
[ 1.799459] Anatop Thermal registered as thermal_zone0
[ 1.804801] anatop_thermal_probe: default cooling device is cpufreq!
[ 1.812486] VFP support v0.3: implementor 41 architecture 3 part 30 variant 9 rev 4
[ 1.827631] Bus freq driver module loaded
[ 1.831660] Bus freq driver Enabled
[ 1.834609] mmc0: new high speed DDR MMC card at address 0001
[ 1.835091] mmcblk0: mmc0:0001 M32508 7.28 GiB
[ 1.835301] mmcblk0boot0: mmc0:0001 M32508 partition 1 4.00 MiB
[ 1.835512] mmcblk0boot1: mmc0:0001 M32508 partition 2 4.00 MiB
[ 1.836631] mmcblk0: p1
[ 1.859854] mxc_dvfs_core_probe
[ 1.862208] mmcblk0boot1: unknown partition table
[ 1.867978] DVFS driver module loaded
[ 1.870077] mmcblk0boot0: unknown partition table
[ 1.877974] snvs_rtc snvs_rtc.0: setting system clock to 1970-01-06 20:05:48 UTC (504348)
[ 1.888226] Freeing init memory: 7684K
starting pid 1073, tty '': '/etc/init.d/rcS'
mount: mounting none on /dev/pts failed: No such file or directory
mount: mounting tmpfs on /dev/shm failed: No such file or directory
Please press Enter to activate this console. [ 2.415952] mmc2: host does not support reading read-only switch. assuming write-enable.
[ 2.437709] mmc2: new high speed SDHC card at address aaaa
[ 2.443688] mmcblk1: mmc2:aaaa SS08G 7.40 GiB
[ 2.449549] mmcblk1: p1
starting pid 1077, tty '/dev/console': '-/bin/sh'
BusyBox v1.20.2 (2016-11-29 16:20:44 CST) built-in shell (ash)
Enter 'help' for a list of built-in commands.
-/bin/sh: can't access tty; job control turned off
[[email protected] /]#
優化核心後,開機三秒不到!!繼續優化中...
全劇終!