vim usr/initramfs_data.S
SECTIONS
{
.init.ramfs : { *(.data) } // 包含所有data
}
.section .init.ramfs,"a"
.incbin "usr/initramfs_data.cpio.gz" // data為usr/initramfs_data.cpio.gz文件內(nèi)容
vim arch/arm/kernel/vmlinux.lds.S
#ifdef CONFIG_BLK_DEV_INITRD
. = ALIGN(32);
__initramfs_start = .;
usr/built-in.o(.init.ramfs)
__initramfs_end = .;
#endif
vim init/initramfs.c
rootfs_initcall(populate_rootfs);
static int __init populate_rootfs(void)
{
// 先unpack built-in編譯進(jìn)內(nèi)核的以__initramfs_start地址開始的fs數(shù)據(jù)
char *err = unpack_to_rootfs(__initramfs_start,
__initramfs_end - __initramfs_start, 0);
// 再unpack bootloader傳過來的以initrd_start地址開始的fs數(shù)據(jù)
err = unpack_to_rootfs((char *)initrd_start,
initrd_end - initrd_start, 0);
}
生成initramfs.gz加載文件
scripts/gen_initramfs_list.sh -o /vobs/gliethttp/initramfs.gz /vobs/nfs/
如果-d后面不加任何參數(shù)那么將顯示默認(rèn)的initramfs中包含的文件
scripts/gen_initramfs_list.sh -d
顯示initramfs.cpio中的包含的文件
gunzip /vobs/gliethttp/initramfs.gz
cpio -i -t < /vobs/gliethttp/initramfs
或者直接顯示initramfs.gz中包含的文件
gunzip -c /vobs/gliethttp/initramfs.gz | cpio -i -t
arch/arm/kernel/setup.c|730| paging_init(mdesc)
start_kernel
==> setup_arch(&command_line)
==> paging_init
==> bootmem_init
==> bootmem_reserve_initrd(node) 會(huì)將phys_initrd_start后phys_initrd_size大小空間暫時(shí)保留,不被buddy回收管理[luther.gliethttp]
然后initrd_start = __phys_to_virt(phys_initrd_start)存放虛擬地址
start_kernel
==> vfs_caches_init
==> mnt_init
==> init_rootfs
位于fs/ramfs/inode.c中,調(diào)用register_filesystem(&rootfs_fs_type);注冊rootfs
接下來
init_mount_tree()函數(shù)將調(diào)用mnt = do_kern_mount("rootfs", 0, "rootfs", NULL);將rootfs掛在到/根目錄下
然后使用如下兩個(gè)函數(shù),將后邊建立線程的根目錄指向rootfs,
set_fs_pwd(current->fs, ns->root, ns->root->mnt_root);
set_fs_root(current->fs, ns->root, ns->root->mnt_root);
init/do_mounts.c|153| __setup("root=", root_dev_setup);
比如cmdline傳入?yún)?shù)"root=/dev/ram0"
static int __init root_dev_setup(char *line)
{
strlcpy(saved_root_name, line, sizeof(saved_root_name));
return 1;
}
__setup("root=", root_dev_setup);
在函數(shù)中將引用到saved_root_name
arch/arm/kernel/vmlinux.lds.S|46| *(.init.setup)
arch/x86/kernel/vmlinux_32.lds.S|131| *(.init.setup)
__setup_start = .;
*(.init.setup)
__setup_end = .;
1. obsolete_checksetup
2. do_early_param
start_kernel
==> parse_args("Booting kernel", static_command_line, __start___param,
__stop___param - __start___param,
&unknown_bootoption); // 解析built-in的靜態(tài)cmdline和靜態(tài)param.
==> unknown_bootoption
==> obsolete_checksetup
arch/arm/kernel/vmlinux.lds
__start___param = .;
*(__param) //為kernel buit in的內(nèi)容,我的kernel沒有定義該存儲(chǔ)區(qū)段
__stop___param = .;
start_kernel
==> parse_early_param
==*>parse_early_options
==**> parse_args("early options", cmdline, NULL, 0, do_early_param) // 使用do_early_param函數(shù)解析參數(shù)
==> do_early_param
2.6.30.4內(nèi)核cmdline常用命令行參數(shù)與相應(yīng)處理函數(shù)static int __init do_early_param(char *param, char *val)
{
struct obs_kernel_param *p;
for (p = __setup_start; p < __setup_end; p++) {
if ((p->early && strcmp(param, p->str) == 0) ||
(strcmp(param, "console") == 0 &&
strcmp(p->str, "earlycon") == 0)
) {
if (p->setup_func(val) != 0)
printk(KERN_WARNING
"Malformed early option '%s'\n", param);
}
}
/* We accept everything at this stage. */
return 0;
}
static int __init parse_tag_initrd(const struct tag *tag)
{
printk(KERN_WARNING "ATAG_INITRD is deprecated; "
"please update your bootloader.\n");
phys_initrd_start = __virt_to_phys(tag->u.initrd.start); // 如果傳入正確值,那么kernel就啟動(dòng)不起來
phys_initrd_size = tag->u.initrd.size;
return 0;
}
__tagtable(ATAG_INITRD, parse_tag_initrd);
start_kernel
==> rest_init
==> kernel_init
static int __init kernel_init(void * unused)
{
lock_kernel();
set_cpus_allowed_ptr(current, cpu_all_mask);
init_pid_ns.child_reaper = current;
cad_pid = task_pid(current);
smp_prepare_cpus(setup_max_cpus);
do_pre_smp_initcalls();
start_boot_trace();
smp_init();
sched_init_smp();
do_basic_setup(); // 遍歷module_init編譯進(jìn)內(nèi)核的驅(qū)動(dòng)初始化
// 其中就包括我們最關(guān)心的initramfs釋放操作[luther.gliethttp]
// populate_rootfs()會(huì)調(diào)用下面這個(gè)函數(shù)將initrd的內(nèi)容釋放到rootfs文件系統(tǒng)下unpack_to_rootfs
#if 0
{
struct obs_kernel_param *p;
int i = 0;
for (p = __setup_start; p < __setup_end; p++) {
printk("[gliethttp _setup %03d] %s\n", i++, p->str); // 可以打印所有built-in的參數(shù)處理函數(shù)[luther.gliethttp]
}
}
#endif
if (!ramdisk_execute_command) // 先檢查ramdisk_execute_command是否在rootfs目錄下,即/根目錄下存在,
// 如果存在,那么不用執(zhí)行prepare_namespace()[luther.gliethttp]
ramdisk_execute_command = "/init";
if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) {
ramdisk_execute_command = NULL;
prepare_namespace(); // 如果沒有ramfs,那么嘗試__setup("root=", root_dev_setup);用戶傳入的cmdline
// 檢查saved_root_name是否可掛載,如果可以那么sys_mount(".", "/", NULL, MS_MOVE, NULL);
// 然后將主目錄改變過去sys_chroot(".");.
}
init_post(); // 在init_post中,如果ramdisk_execute_command存在,那么啟動(dòng)用戶空間的
// 指定的elf用戶程序run_init_process(ramdisk_execute_command);[luther.gliethttp]
// sys_open((const char __user *) "/dev/console", O_RDWR, 0);
return 0;
}
==> do_basic_setup
==*> do_initcalls調(diào)用所有arch/arm/kernel/vmlinux.lds.S中定義的INITCALLS模塊,比如:使用module_init內(nèi)建到zImage中的所有驅(qū)動(dòng)模塊
#define pure_initcall(fn) __define_initcall("0",fn,0)
#define core_initcall(fn) __define_initcall("1",fn,1)
#define core_initcall_sync(fn) __define_initcall("1s",fn,1s)
#define postcore_initcall(fn) __define_initcall("2",fn,2)
#define postcore_initcall_sync(fn) __define_initcall("2s",fn,2s)
#define arch_initcall(fn) __define_initcall("3",fn,3)
#define arch_initcall_sync(fn) __define_initcall("3s",fn,3s)
#define subsys_initcall(fn) __define_initcall("4",fn,4)
#define subsys_initcall_sync(fn) __define_initcall("4s",fn,4s)
#define fs_initcall(fn) __define_initcall("5",fn,5)
#define fs_initcall_sync(fn) __define_initcall("5s",fn,5s)
#define rootfs_initcall(fn) __define_initcall("rootfs",fn,rootfs)
#define device_initcall(fn) __define_initcall("6",fn,6)
#define device_initcall_sync(fn) __define_initcall("6s",fn,6s)
#define late_initcall(fn) __define_initcall("7",fn,7)
#define late_initcall_sync(fn) __define_initcall("7s",fn,7s)
上面我們關(guān)心的是rootfs_initcall定義,
在init/initramfs.c中具體實(shí)現(xiàn)如下:
rootfs_initcall(populate_rootfs);
populate_rootfs()會(huì)調(diào)用下面這個(gè)函數(shù)將initrd的內(nèi)容釋放到rootfs文件系統(tǒng)下
unpack_to_rootfs((char *)initrd_start,initrd_end - initrd_start, 0);
這樣initrd的ramdisk的內(nèi)容全部釋放到了rootfs文件系統(tǒng)中,所以ramdisk下的所有目錄結(jié)構(gòu)將全部被克隆到rootfs文件系統(tǒng)下
==> prepare_namespace
void __init prepare_namespace(void)
{
if (saved_root_name[0]) { // 如果有__setup("root=", root_dev_setup);用戶傳入的cmdline,那么去出來看看
root_device_name = saved_root_name;
if (!strncmp(root_device_name, "mtd", 3) ||
!strncmp(root_device_name, "ubi", 3)) {
mount_block_root(root_device_name, root_mountflags);
goto out;
}
ROOT_DEV = name_to_dev_t(root_device_name);
if (strncmp(root_device_name, "/dev/", 5) == 0)
root_device_name += 5;
}
if (initrd_load())
goto out;
/* wait for any asynchronous scanning to complete */
if ((ROOT_DEV == 0) && root_wait) {
printk(KERN_INFO "Waiting for root device %s...\n",
saved_root_name);
while (driver_probe_done() != 0 ||
(ROOT_DEV = name_to_dev_t(saved_root_name)) == 0)
msleep(100);
async_synchronize_full();
}
is_floppy = MAJOR(ROOT_DEV) == FLOPPY_MAJOR;
if (is_floppy && rd_doload && rd_load_disk(0))
ROOT_DEV = Root_RAM0;
mount_root();
out:
sys_mount(".", "/", NULL, MS_MOVE, NULL);
sys_chroot(".");
}