国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開APP
userphoto
未登錄

開通VIP,暢享免費電子書等14項超值服

開通VIP
根文件系統(tǒng)掛載
1) 在RamDisk為initramfs時
start_kernel()->vfs_caches_init()->mnt_init()->init_rootfs()->init_mount_tree()注冊了類型為rootfs的fs

然后:start_kernel 最后 rest_init->kernel_init
kernel_init->do_basic_setup->do_initcalls 調(diào)用 rootfs_initcall 注冊過的函數(shù)
rootfs_initcall(populate_rootfs);
populate_rootfs解壓initramfs到rootfs, initramfs必須包含init文件,否則還將掛在其他的文件系統(tǒng)

然后kernel_init檢查init是否存在,如果有就不會注冊其他的根文件系統(tǒng),
if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0)
會導致 prepare_namespace 不被調(diào)用。
否則還會嘗試掛在其他的根文件系統(tǒng)。


2) 在RamDisk (initrd) 非initramfs時
kernel_init->prepare_namespace->initrd_load->rd_load_image(加載)->(identify_ramdisk_image)
 
 如果ROOT_DEV != Root_RAM0
 則handle_initrd通過linuxrc啟動用戶態(tài)
 該種情況可由initrd=0xXXXXX 但 root=/dev/mtdblock2矛盾導致。
 
 如果ROOT_DEV == Root_RAM0
 rd_load_image加載后,也是通過mount_root加載Root_RAM0根文件系統(tǒng)
 
3) 沒有RamDisk 例如使用mtd
 kernel_init->prepare_namespace->mount_root(加載)

#################################################################
#################################################################

順手摘一篇寫得比較好的initrd分析文章
原文:
http://liaowb1234.blog.163.com/blog/static/771555472010025114231594/

Initrd 流程分析(2.6 cpio rootfs)

以 Linux-2.6.25 的 kernel 為例,分析一下 Linux 啟動過程中 initrd 的流程。

1. 先從 Makefile說起

下面是內(nèi)核代碼中 init/Makefile 文件的一段內(nèi)容:
obj-y  := main.o version.o mounts.o

ifneq($(CONFIG_BLK_DEV_INITRD),y)
obj-y +=noinitramfs.o
else
obj-$(CONFIG_BLK_DEV_INITRD)+= initramfs.o
endif

obj-$(CONFIG_GENERIC_CALIBRATE_DELAY)+= calibrate.o

mounts-y :=do_mounts.o
mounts-$(CONFIG_BLK_DEV_RAM)   +=do_mounts_rd.o
mounts-$(CONFIG_BLK_DEV_INITRD)+= do_mounts_initrd.o
mounts-$(CONFIG_BLK_DEV_MD)    += do_mounts_md.o

內(nèi)核中和 initrd 相關(guān)的代碼主要放在 init 目錄下,包括 main.c,noinitramfs.c ,initramfs.c,do_mounts.c ,do_mounts_initrd.c ,do_mounts_rd.c 和 do_mounts_md.c。
從 Makefile 中可以看出,noinitramfs.c 是在內(nèi)核不支持 initrd 的情況下被編譯進內(nèi)核,而initramfs.c 正好相反,它處理(cpio包類型的)的 initrd 。do_mounts.c主要是負責掛載根文件系統(tǒng)的,所以總是被編譯。do_mounts_initrd.c 負責調(diào)用掛載和處理(ramdisk類型的)的initrd 。do_mounts_rd.c 是具體實現(xiàn)如何掛載(ramdisk類型的)的 initrd 。do_mount_md.c處理和 RAID 有關(guān)的一些情況。

2. cpio 包類型的 initrd 的處理


內(nèi)核在初始化啟動的時候會先注冊一個叫作 rootfs 的文件系統(tǒng),然后通過 rootfs_initcall來生成其中的內(nèi)容。根據(jù)內(nèi)核是否支持 initrd 和 ramdisk ,rootfs 的生成方法和內(nèi)容都會有所不同。當內(nèi)核不支持initrd 時,rootfs_initcall 調(diào)用 noinitramfs.c 中的 default_rootfs()函數(shù)。default_rootfs() 主要往 rootfs 中生成兩個目錄 /dev 和 /root 以及一個設(shè)備文件/dev/console 。下面是default_rootfs() 精簡過的流程:
static int __init default_rootfs(void)
{
        sys_mkdir("/dev", 0755);
        sys_mknod((const char __user *) "/dev/console",
                 S_IFCHR | S_IRUSR | S_IWUSR,
                 new_encode_dev(MKDEV(5, 1)));
        sys_mkdir("/root", 0700);
        return 0;
}
rootfs_initcall(default_rootfs);

在調(diào)用rootfs_initcall()之前已經(jīng)通過下面的調(diào)用過程建立了rootfs文件系統(tǒng):

kernel_entry()->vfs_caches_init()->mnt_init()->init_rootfs()->init_mount_tree()->...

似乎在rootfs的init文件必須位于根目錄下,即/init,否則系統(tǒng)會嘗試去mount其它的文件系統(tǒng),例如ram0,mtdblock等。

當內(nèi)核支持 initrd 時,rootfs_initcall 調(diào)用 initramfs.c 中的populate_rootfs() 函數(shù)來填充 rootfs。下面先看一下 populate_rootfs()精簡過的主要流程:
static int __init populate_rootfs(void)
{
        
        char *err = unpack_to_rootfs(__initramfs_start, __initramfs_end -__initramfs_start, 0);
        if (initrd_start){   
#ifdefCONFIG_BLK_DEV_RAM   
                 err = unpack_to_rootfs((char *)initrd_start, initrd_end -initrd_start,1);   
                 if (!err) {
                          unpack_to_rootfs((char *)initrd_start, initrd_end - initrd_start,0);   
                          free_initrd();   
                          return 0;
                 }
                 
                 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();   
                 }
#else
                 
                 err = unpack_to_rootfs((char *)initrd_start, initrd_end -initrd_start, 0);
                 if (err)
                          panic(err);
                 free_initrd();      
#endif
        }
        return 0;
}

rootfs_initcall(populate_rootfs);

關(guān)于 initramfs (也就是內(nèi)核中自帶的 cpio 包)需要解釋的是:如果內(nèi)核支持 initrd,但并沒有配置CONFIG_INITRAMFS_SOURCE 選項的話,內(nèi)核在編譯的時候會自動生成一個最小的 cpio 包附在內(nèi)核中。這個內(nèi)核自帶的cpio 包的內(nèi)容與由 default_rootfs() 生成的一樣。具體可參見編譯后的內(nèi)核源碼樹中的usr/initramfs_data.cpio.gz 文件。
至此,內(nèi)核對(cpio包格式的)initrd 的處理流程就結(jié)束了。如果在 populate_rootfs() 中成功地unpack_to_rootfs() 的話,之后內(nèi)核就不會再對 initrd 作任何操作,也不會去掛載根文件系統(tǒng),所有的工作都留給cpio 包(也就是rootfs)中的 /init 去完成了。關(guān)于這一點,在后面分析 main.c 中的流程中會看到。

3. 如果沒有使用 initrd

下面先考慮一下內(nèi)核在沒有使用 initrd 的情況下掛載根文件系統(tǒng)的流程。這又分內(nèi)核沒有編譯支持 initrd 或 內(nèi)核支持initrd 但系統(tǒng)引導時沒有提供 initrd文件兩種情況。但這兩種情況其結(jié)果其實是一樣的,根據(jù)前面的分析,兩種情況間的差別只是在生成 rootfs 的方式不一樣,一個是通過default_rootfs() ,另一個是調(diào)用 unpack_to_rootfs() ,而且其產(chǎn)生內(nèi)容是一樣的(只要沒有配置CONFIG_INITRAMFS_SOURCE)。
如果沒有使用到 cpio 類型的 initrd,內(nèi)核會執(zhí)行 prepare_namespace()函數(shù)(關(guān)于這個函數(shù)在內(nèi)核啟動過程中的位置,后面會有講到)。prepare_namespace() 在 do_mounts.c中定義,它主要負責掛載根文件系統(tǒng)和 ramdisk 類型的 initrd (如果需要的話)。下面看一下它精簡過的大致流程:
void __init prepare_namespace(void)
{
        if (saved_root_name[0]){   
                 root_device_name = saved_root_name;
                 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;

        
        mount_root();
out:
        sys_mount(".", "/", NULL, MS_MOVE, NULL);
        sys_chroot(".");
}

變量 saved_root_name 的值是內(nèi)核啟動的參數(shù) "root=" 后的值,這個是由 __setup()宏提取的。

下面看一下 mount_root() 函數(shù)精簡過的流程:
void __init mount_root(void)
{
#ifdef CONFIG_BLOCK
        create_dev("/dev/root",ROOT_DEV);    
        mount_block_root("/dev/root", root_mountflags);
#endif
}

這里在 rootfs 中新建了一個 /dev/root 設(shè)備文件,這個設(shè)備文件一般就是指內(nèi)核啟動參數(shù)指定的包含根文件系統(tǒng)的設(shè)備。在rootfs 中,這個設(shè)備文件被命名為 /dev/root 。

再往下看 mount_block_root() 的精簡流程:
void __init mount_block_root(char *name, intflags)
{
        get_fs_names(fs_names);      
retry:
        for (p = fs_names; *p; p += strlen(p)+1){   
                 int err = do_mount_root(name, p, flags, root_mount_data);
                 switch (err) {
                 ... ...
                 }

                 
                 ... ...
        }
        ... ...
}

所以在這個函數(shù)中,最主要的是調(diào)用了 do_mount_root() 來掛載根文件系統(tǒng)。

最后來看一下 do_mount_root() 函數(shù)的實現(xiàn):
static int __init do_mount_root(char *name,char *fs, int flags, void *data)
{
        int err = sys_mount(name, "/root", fs, flags, data);
        if (err)
                 return err;

        sys_chdir("/root");
        ROOT_DEV =current->fs->pwd.mnt->mnt_sb->s_dev;
        ... ...   
        return 0;
}

do_mount_root() 嘗試把參數(shù) name 指定的設(shè)備文件掛載到 /root 目錄中去,并 cd到新的根文件系統(tǒng)的根目錄中去。

至此,如果一切順利的話,我們已經(jīng)成功地把包含根文件系統(tǒng)的設(shè)備掛載到了 rootfs 中的 /root 目錄上去了。我們的調(diào)用流程是prepare_namespace() -->mount_root() --> mount_block_root()--> do_mount_root() 。

回到 prepare_namespace() 中去,在順利 mount_root() 完之后,還有兩步要做:
out:
        sys_mount(".", "/", NULL, MS_MOVE,NULL);   
        sys_chroot(".");
}

由于之前已經(jīng)切換到了新的根文件系統(tǒng)的根目錄中去,所以這兩步的作用是用新的根文件系統(tǒng)的根目錄替換 rootfs ,使其成為 LinuxVFS 的根目錄。

至此,我們已經(jīng)走完了 prepare_namespace() 在不使用 initrd情況下的流程,根文件系統(tǒng)設(shè)備也已經(jīng)掛載上了,且替代了 rootfs 成為了 VFS 的根,剩下的任務就留給了根文件系統(tǒng)中的/sbin/init 程序了。

4. ramdisk 類型的 initrd的處理

從前面的分析可以看出,不管內(nèi)核是否使用了 initrd ,只要進入了 prepare_namespace() , 都會去調(diào)用initrd_load() 函數(shù)。下面看一下 initrd_load() 流程:
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;
}

變量 mount_initrd 是是否要加載 initrd 的標志,默認為1,當內(nèi)核啟動參數(shù)中包含 noinitrd字符串時,mount_initrd 會被設(shè)為0 。接著為了把 initrd 釋放到內(nèi)存盤中,先需要創(chuàng)建設(shè)備文件,然后通過rd_load_image 把之前保存的 /initrd.image 加載到內(nèi)存盤中。之后判斷如果內(nèi)核啟動參數(shù)中指定的最終的根文件系統(tǒng)不是內(nèi)存盤的話,那就先要執(zhí)行 initrd 中的linuxrc;如果最終的根文件系統(tǒng)就是剛加載到內(nèi)存盤的 initrd的話,那就先不處理它,留到之后當真正的根文件系統(tǒng)處理。
需要補充的是,只要沒有用到 cpio 類型的 initrd,那內(nèi)核都會執(zhí)行到rd_load_image("/initrd.image"),無論是否真的提供了 initrd 。如果沒有提供 initrd,那/initrd.image 自然不會存在,rd_load_image() 也會提早結(jié)束。另外 /dev/ram 這個設(shè)備節(jié)點文件在rd_load_image() 中用完之后總會被刪除(但相應的內(nèi)存盤中的內(nèi)容還在)。

下面看一下 handle_initrd() 的主要流程:
static void __init handle_initrd(void)
{
       int error;
       int pid;

        real_root_dev = new_encode_dev(ROOT_DEV);

        
       create_dev("/dev/root.old", Root_RAM0);
        mount_block_root("/dev/root.old", root_mountflags &~MS_RDONLY);

        
        sys_mkdir("/old", 0700);
        root_fd = sys_open("/", 0, 0);
        old_fd = sys_open("/old", 0, 0);


        
        sys_chdir("/root");
        sys_mount(".", "/", NULL, MS_MOVE, NULL);
        sys_chroot(".");

        
        pid = kernel_thread(do_linuxrc, "/linuxrc", SIGCHLD);
        if (pid > 0)
                 while (pid != sys_wait4(-1, NULL, 0, NULL))
                 yield();

        
        sys_fchdir(old_fd);
        sys_mount("/", ".", NULL, MS_MOVE, NULL);

        
        sys_fchdir(root_fd);
        sys_chroot(".");

        sys_close(old_fd);
        sys_close(root_fd);

        
        if (new_decode_dev(real_root_dev) == Root_RAM0) {
                sys_chdir("/old");
                 return;
       }

        
        ROOT_DEV = new_decode_dev(real_root_dev);

        
        mount_root();

        printk(KERN_NOTICE "Trying to move old root to /initrd ...");
        
        error = sys_mount("/old", "/root/initrd", NULL, MS_MOVE,NULL);
        if (!error)
                 printk("okay\n");
        else {
                 int fd = sys_open("/dev/root.old", O_RDWR, 0);
                 if (error == -ENOENT)
                          printk("/initrd does not exist. Ignored.\n");
                 else
                          printk("failed\n");

                 
                 printk(KERN_NOTICE "Unmounting old root\n");
                 sys_umount("/old", MNT_DETACH);
                 printk(KERN_NOTICE "Trying to free ramdisk memory ... ");
                 if (fd < 0) {
                          error = fd;
                 } else {
                          error = sys_ioctl(fd, BLKFLSBUF, 0);
                          sys_close(fd);
                 }
                 printk(!error ? "okay\n" : "failed\n");
         }
}

在這個函數(shù)中,用到了一個 real_root_dev 變量,它是一個 int 型的全局變量,它的值從變量 ROOT_DEV轉(zhuǎn)換過來。變量 real_root_dev 是和文件 /proc/sys/kernel/real-root-dev 相關(guān)聯(lián)的(參見kernel/sysctl.c 第405行左右),所以如果在執(zhí)行 initrd 中的 /linuxrc 時改了/proc/sys/kernel/real-root-dev 的話,就相當于又重新指定了真正的根文件系統(tǒng)。之所以要新弄一個real_root_dev 變量,使因為 procfs 不支持改 kdev_t 型的 ROOT_DEV 變量。
另外,在 rootfs 中會建有兩個設(shè)備文件節(jié)點:/dev/root 是真正的根文件系統(tǒng)的設(shè)備節(jié)點,其設(shè)備號是 ROOT_DEV的值;/dev/root.old 是 ramdisk 型的 initrd 的設(shè)備節(jié)點,其設(shè)備號總是 Root_RAM0 。


下面看一下 do_linuxrc() 的實現(xiàn):
static int __init do_linuxrc(void *shell)
{
         static char *argv[] = { "linuxrc", NULL, };
         extern char * envp_init[];

         sys_close(old_fd);sys_close(root_fd);
         sys_close(0);sys_close(1);sys_close(2);
         sys_setsid();
         (void) sys_open("/dev/console",O_RDWR,0);
         (void) sys_dup(0);
         (void) sys_dup(0);
         return kernel_execve(shell, argv, envp_init);
}

這樣我們就走完了整個 ramdisk 類型的 initrd 的處理流程了,如果執(zhí)行了其中的 /linuxrc文件的話,當我們退回到 prepare_namespace() 函數(shù)時就會直接跳到 out:標簽處,剩下還有兩行代碼要執(zhí)行:
out:
sys_mount(".", "/", NULL, MS_MOVE, NULL);
sys_chroot(".");
}

這兩步前一節(jié)已經(jīng)分析過了,就是把當前目錄替換 rootfs ,使其成為 Linux VFS的根目錄。由于之前無論是用內(nèi)存盤還是指定的新設(shè)備作為我們的真正的根文件系統(tǒng),我們都會 cd 進去,所以這里就很好理解了。

 

另外,補充一點的是,通過比較 cpio 包和 ramdisk 類型的initrd,我們可以發(fā)現(xiàn) cpio 包里的東西是直接解壓到rootfs 中的,成為 rootfs 的一部分,而 ramdisk 的 initrd 是以 ramdisk形式存在的,需要額外掛載到 rootfs 上才能使用,兩者有很大的區(qū)別。

5. 總的流程

最后我們看一下內(nèi)核啟動過程中和 initrd 有關(guān)的一個總的流程順序。
內(nèi)核初始化從 start_kernel() 函數(shù)開始,start_kernel() 最后會調(diào) rest_init() 函數(shù)。在rest_init() 函數(shù)中,第一步就會通過調(diào)用 kernel_thread(kernel_init, ...)生成一個內(nèi)核線程來執(zhí)行 kernel_init() 函數(shù)。注意,這個 kernel_init() 線程就是后來的 init進程(1號進程)的前身,而原來的 rest_init() 函數(shù)在最后調(diào)用 cpu_idle() 后就變成 swap進程(0號進程)了。


接著,我們從 kernel_init() 函數(shù)線程繼續(xù)研究,下面列出了 kernel_init() 函數(shù)中和 initrd相關(guān)的的主要流程:
static int __init kernel_init(void *unused)
{
   ... ...
   do_basic_setup();

   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();
   }

   init_post();
   return 0;
}

在 kernel_init() 中會調(diào)用 do_basic_setup(),而 do_basic_setup() 又會調(diào)用do_initcalls(),所以 cpio 包類型的 initrd (如果有的話)就會在此時被填充到 rootfs中去。接下來初始化 ramdisk_execute_command 變量,這個變量表示在 cpio包中要被執(zhí)行的第一個程序,可通過在內(nèi)核啟動參數(shù)中給 rdinit= 賦值來指定。接下來檢查在 rootfs 中是否存在變量ramdisk_execute_command 所指的文件。如果有,就說明 cpio 包類型的 initrd成功加載了,那就不需要內(nèi)核再調(diào)用 prepare_namespace() 來掛載根文件系統(tǒng)了,這些都留給 cpio 包里的ramdisk_execute_command 所指的程序去完成了;如果沒有,就說明內(nèi)核并沒有成功用上 cpio 包類型的initrd,還需要調(diào)用 prepare_namespace()來繼續(xù)準備加載根文件系統(tǒng),并清空變量ramdisk_execute_command。無論怎樣,內(nèi)核都會繼續(xù)執(zhí)行init_post()。


init_post() 是內(nèi)核初始化的終點了:把 kernel_init() 內(nèi)核線程 execve 成用戶態(tài)的 init進程。下面是它的主要流程:
static int noinline init_post(void)
{
    ......
    if(sys_open((const char __user *) "/dev/console", O_RDWR, 0)< 0)
      printk(KERN_WARNING "Warning: unable to open an initialconsole.\n");

    (void)sys_dup(0);
    (void)sys_dup(0);

   
    if(ramdisk_execute_command) {
      run_init_process(ramdisk_execute_command);
      printk(KERN_WARNING "Failed to execute %s\n",ramdisk_execute_command);
    }

   
    if(execute_command) {
      run_init_process(execute_command);
      printk(KERN_WARNING "Failed to execute %s. Attempting "
               "defaults...\n", execute_command);
    }
   run_init_process("/sbin/init");
   run_init_process("/etc/init");
   run_init_process("/bin/init");
   run_init_process("/bin/sh");

    panic("Noinit found. Try passing init= option to kernel.");
}

本站僅提供存儲服務,所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊舉報。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
Linux--根文件系統(tǒng)的掛載過程分析
Linux INITRAMFS 與 INITRD
解析linux根文件系統(tǒng)的掛載過程
linux2.6內(nèi)核initrd機制解析
各種文件系統(tǒng)
看看initramfs加載的完整流程
更多類似文章 >>
生活服務
分享 收藏 導長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點擊這里聯(lián)系客服!

聯(lián)系客服