在嵌入式 Linux 系統啟動階段,控制台輸出關鍵錯誤日志:
RAMDISK: Couldn't find valid RAM disk image starting at 0.
UDF-fs: No partition found (1)
NILFS: Can't find nilfs on dev ram0.
(1,15):ocfs2_fill_super:1001 ERROR: superblock probe
VFS: Cannot open root device "ram0" or unknown-block(1,0)
Please append a correct "root=" boot option; here are the available partitions:
0800 8003520 sda driver: sd
0801 14048 sda1
0804 1 sda4
0805 393057 sda5
0806 102400 sda6
0807 7488690 sda7
Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(1,0)
Unable to load '/system/dump '.
Pid: 1, comm: swapper Not tainted 2.6.32.15-hermes-1 #23
Call Trace:
[<ffffffff8151f1d5>] panic+0x7a/0x12d
[<ffffffff818780ba>] mount_block_root+0x257/0x275
[<ffffffff8187812e>] mount_root+0x56/0x5a
[<ffffffff8187829d>] prepare_namespace+0x16b/0x198
[<ffffffff81877685>] kernel_init+0x178/0x188
[<ffffffff8102e8fa>] child_rip+0xa/0x20
[<ffffffff8187750d>] ? kernel_init+0x0/0x188
[<ffffffff8102e8f0>] ? child_rip+0x0/0x20
UTC time : 2005-1-1 0:14:52
核心報錯 RAMDISK: Couldn't find valid RAM disk image starting at 0. 表明內核無法在指定位置識別有效的 initrd 鏡像,導致根文件系統掛載失敗,最終觸發內核恐慌。
根據日志定位到 init/do_mounts.c 中的 identify_ramdisk_image() 函數,該函數負責讀取磁塊設備前幾個扇區,通過魔數匹配識別文件系統類型(minix、ext2、romfs、cramfs、squashfs 等)或壓縮格式(gzip、bzip2、lzma 等)。
關鍵代碼片段:
static int __init identify_ramdisk_image(int fd, int start_block, decompress_fn *decompressor) {
const int size = 512;
unsigned char *buf = kmalloc(size, GFP_KERNEL);
// ... 省略結構體定義 ...
sys_lseek(fd, start_block * BLOCK_SIZE, 0);
sys_read(fd, buf, size);
// 調試打印:輸出讀取到的原始數據
printk("start_block:%d\n", start_block);
for(i=0;iword0 == ROMSB_WORD0 && romfsb->word1 == ROMSB_WORD1) { /* romfs */ goto done; }
if (cramfsb->magic == CRAMFS_MAGIC) { /* cramfs */ goto done; }
if (le32_to_cpu(squashfsb->s_magic) == SQUASHFS_MAGIC) { /* squashfs */ goto done; }
sys_lseek(fd, (start_block+1) * BLOCK_SIZE, 0);
sys_read(fd, buf, size);
if (minixsb->s_magic == MINIX_SUPER_MAGIC || minixsb->s_magic == MINIX_SUPER_MAGIC2) { /* minix */ goto done; }
if (ext2sb->s_magic == cpu_to_le16(EXT2_SUPER_MAGIC)) { /* ext2 */ goto done; }
printk(KERN_NOTICE "RAMDISK: Couldn't find valid RAM disk image starting at %d.\n", start_block);
done:
sys_lseek(fd, start_block * BLOCK_SIZE, 0);
kfree(buf);
return nblocks;
}
所有分支均未匹配,說明讀取到的數據不包含任何已知文件系統或壓縮格式的魔數。通過調試打印發現 buf 全為 0xFF,這通常意味著內存區域未被正確寫入,處於上電初始狀態。
順藤摸瓜發現 initrd_load() (init/do_mounts_initrd.c) 嘗試加載 /initrd.image:
int __init initrd_load(void) {
if (mount_initrd) {
create_dev("/dev/ram", Root_RAM0);
if (rd_load_image("/initrd.image") && ROOT_DEV != Root_RAM0) {
sys_unlink("/initrd.image");
handle_initrd();
return 1;
}
}
sys_unlink("/initrd.image");
return 0;
}
但磁盤上明明是 initrd.img,這是怎麼回事?進一步追蹤到 init/initramfs.c 的 populate_rootfs():
static int __init populate_rootfs(void) {
// ... 嘗試解包內置 initramfs 失敗後 ...
fd = sys_open("/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();
}
}
原來內核將內存中 initrd_start 到 initrd_end 區間的數據直接寫入 /initrd.image。那麼 initrd_start 的數據來源是哪裡?
啟動日志顯示:
RAMDISK: 7fa36000 - 7ffff40e
Allocated new RAMDISK: 00100000 - 006c940e
Move RAMDISK from 000000007fa36000 - 000000007ffff40d to 00100000 - 006c940d
RAMDISK 初始物理地址 0x7fa36000 (約 2GB) 遠超 15MB 限制。LILO 文檔說明:傳統 BIOS 僅支持 24 位地址總線(16MB),LILO 默認將 initrd 加載在 16MB 以下。但當內核 + initrd 體積超過 14MB 時,LILO 會假設 BIOS 支持高內存,將 initrd 放入高地址。
關鍵警告信息:
Normally any initial ramdisk (initrd) loaded with a kernel is loaded as high in memory as possible, but never above 15Mb. This is due to a BIOS limitation on older systems. On newer systems, this option enables using memory above 15Mb (up to a kernel imposed limit, around 768Mb) for passing the initrd to the kernel.
實際上,舊款 BIOS 的 INT 13h 中斷無法訪問 16MB 以上內存,LILO 調用 BIOS 寫入高內存時失敗卻未報錯,導致目標內存區域保持 0xFF 初始值。
lilo.conf 中添加 large-memory = off 或調整內核/ initrd 體積使其總和 < 14MB。mem= 參數限制內核可見內存範圍,配合 initrd= 顯式指定位置。本案例揭示了引導加載器、BIOS 能力與內核內存管理之間微妙的兼容性問題。排查關鍵在於:不信任引導加載器的「成功」提示,必須驗證內存實際寫入內容。