linux 根文件系统挂载分析(二)

第三部分解压initramfs文件系统中的内容到rootfs

01static int __init populate_rootfs(void)
02{
03 char *err = unpack_to_rootfs(__initramfs_start,
04    __initramfs_end - __initramfs_start);
05 if (err)
06  panic(err); /* Failed to decompress INTERNAL initramfs */
07 if (initrd_start) {
08#ifdef CONFIG_BLK_DEV_RAM
09  int fd;
10  printk(KERN_INFO "Trying to unpack rootfs image as initramfs...\n");
11  err = unpack_to_rootfs((char *)initrd_start,
12   initrd_end - initrd_start);
13  if (!err) {
14   free_initrd();
15   return 0;
16  } else {
17   clean_rootfs();
18   unpack_to_rootfs(__initramfs_start,
19     __initramfs_end - __initramfs_start);
20  }
21  printk(KERN_INFO "rootfs image is not initramfs (%s)"
22    "; looks like an initrd\n", err);
23  fd = sys_open("/initrd.image", O_WRONLY|O_CREAT, 0700);
24  if (fd >= 0) {
25   sys_write(fd, (char *)initrd_start,
26     initrd_end - initrd_start);
27   sys_close(fd);
28   free_initrd();
29  }
30#else
31  err = unpack_to_rootfs((char *)initrd_start,
32   initrd_end - initrd_start);
33  if (err)
34   printk(KERN_EMERG "Initramfs unpacking failed: %s\n", err);
35  free_initrd();
36#endif
37 }
38 return 0;
39}

第3行解压initramfs文件到rootfs文件系统中,第一个参数为开始位置,第二个参数为长度。
第7行initrd_start值为0,下面不执行

01static int __init kernel_init(void * unused)
02{
03 ............
04 do_basic_setup();
05 /*
06  * check if there is an early userspace init.  If yes, let it do all
07  * the work
08  */
09
10 if (!ramdisk_execute_command)
11  ramdisk_execute_command = "/init";
12
13 if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) {
14  ramdisk_execute_command = NULL;
15 
16 prepare_namespace();
17 }
18
19 /*
20  * Ok, we have completed the initial bootup, and
21  * we're essentially up and running. Get rid of the
22  * initmem segments and start the user-mode stuff..
23  */
24
25 init_post();
26 return 0;
27}

第4行初始化init段里面的函数
第10行判断ramdisk_execute_command的值,如果为空,就给它赋于/init,这个也是启动是第一个进程。
第13行如果这个init个程序,访问它,其实也就是initramfs里面解压出来有这个程序的话,就不用再执行下面的函数,用这个init进程可以挂载真正的根文件系统。
第16行为进程0准备命名空间
第25行进行真正根文件系统中的init进程运行
第四部分准备命名空间,挂载flash上的根文件系统

01void __init prepare_namespace(void)
02{
03 int is_floppy;
04 if (root_delay) {
05  printk(KERN_INFO "Waiting %dsec before mounting root device...\n",
06         root_delay);
07  ssleep(root_delay);
08 }
09
10 /*
11  * wait for the known devices to complete their probing
12  *
13  * Note: this is a potential source of long boot delays.
14  * For example, it is not atypical to wait 5 seconds here
15  * for the touchpad of a laptop to initialize.
16  */
17 wait_for_device_probe();
18
19 md_run_setup();
20
21 if (saved_root_name[0]) {
22  root_device_name = saved_root_name;
23  if (!strncmp(root_device_name, "mtd", 3) ||
24      !strncmp(root_device_name, "ubi", 3)) {
25   mount_block_root(root_device_name, root_mountflags);
26   goto out;
27  }
28  ROOT_DEV = name_to_dev_t(root_device_name);
29  if (strncmp(root_device_name, "/dev/", 5) == 0)
30   root_device_name += 5;
31}
32
33 if (initrd_load())
34   goto out;
35
36 /* wait for any asynchronous scanning to complete */
37 if ((ROOT_DEV == 0) && root_wait) {
38  printk(KERN_INFO "Waiting for root device %s...\n",
39   saved_root_name);
40  while (driver_probe_done() != 0 ||
41   (ROOT_DEV = name_to_dev_t(saved_root_name)) == 0)
42   msleep(100);
43  async_synchronize_full();
44 }
45
46 is_floppy = MAJOR(ROOT_DEV) == FLOPPY_MAJOR;
47
48 if (is_floppy && rd_doload && rd_load_disk(0))
49  ROOT_DEV = Root_RAM0;
50
51 mount_root();
52out:
53 sys_mount(".", "/", NULL, MS_MOVE, NULL);
54 sys_chroot(".");
55}

第21行检查saved_root_name是否为真,这里是有值的。
第22行把它赋给root_device_name,此时为/dev/mtdblock3
第23-27行不执行
第25行挂载根文件系统
第28行名字到设备号的转变,这个设备号是设备的设备号,下面会用到的。我这里是flash的第三个分区
第29行/dev/mtdblock3的前5个字符与/dev/比较,这里是相等的。
第30行root_device_name加5,所以此时root_device_name为mtdblock3.
第37-44行由于设备号不为0,所以这里面没有执行。
第53行移动根文件系统的根目录为/

01void __init mount_block_root(char *name, int flags)
02{
03 char *fs_names = __getname();
04 char *p;
05#ifdef CONFIG_BLOCK
06 char b[BDEVNAME_SIZE];
07#else
08 const char *b = name;
09#endif
10
11 get_fs_names(fs_names);
12retry:
13 for (p = fs_names; *p; p += strlen(p)+1) {
14  int err = do_mount_root(name, p, flags, root_mount_data);
15  switch (err) {
16   case 0:
17    goto out;
18   case -EACCES:
19    flags |= MS_RDONLY;
20    goto retry;
21   case -EINVAL:
22    continue;
23  }
24         /*
25   * Allow the user to distinguish between failed sys_open
26   * and bad superblock on root device.
27   * and give them a list of the available devices
28   */
29#ifdef CONFIG_BLOCK
30  __bdevname(ROOT_DEV, b);
31#endif
32  printk("VFS: Cannot open root device \"%s\" or %s\n",
33    root_device_name, b);
34  printk("Please append a correct \"root=\" boot option; here are the available partitions:\n");
35
36  printk_all_partitions();
37#ifdef CONFIG_DEBUG_BLOCK_EXT_DEVT
38  printk("DEBUG_BLOCK_EXT_DEVT is enabled, you need to specify "
39         "explicit textual name for \"root=\" boot option.\n");
40#endif
41  panic("VFS: Unable to mount root fs on %s", b);
42 }
43
44 printk("List of all partitions:\n");
45 printk_all_partitions();
46 printk("No filesystem could mount root, tried: ");
47 for (p = fs_names; *p; p += strlen(p)+1)
48  printk(" %s", p);
49 printk("\n");
50#ifdef CONFIG_BLOCK
51 __bdevname(ROOT_DEV, b);
52#endif
53 panic("VFS: Unable to mount root fs on %s", b);
54out:
55 putname(fs_names);
56}

第3行申请空间
第11行fs_name指向内核里面编译文件系统的第一个
第13行循环把这些文件系统挂到根目录下,我的内核里面分别有,ext3,ext3,vfat等,这里用到的是yaffs文件系统
第14行调用do_mount_root函数进行挂载
第15行返回的结果0
第17行增加对文件系统的引用

01static int __init do_mount_root(char *name, char *fs, int flags, void *data)
02{
03 int err = sys_mount(name, "/root", fs, flags, data);
04 if (err)
05  return err;
06
07 sys_chdir("/root");
08 ROOT_DEV = current->fs->pwd.mnt->mnt_sb->s_dev;
09 printk("VFS: Mounted root (%s filesystem)%s on device %u:%u.\n",
10        current->fs->pwd.mnt->mnt_sb->s_type->name,
11        current->fs->pwd.mnt->mnt_sb->s_flags & MS_RDONLY ?
12        " readonly" : "", MAJOR(ROOT_DEV), MINOR(ROOT_DEV));
13 return 0;
14}

第3行挂载文件系统,这里的name为/dev/root,fs为文件的类型,这里是yaffs2,文件类型当然还是ext3,ext2等。
第7行改变到/root目录下
第9行分别显示出挂载出根文件系统和主次设备号,这里是yaffs文件系统,主:次31:3

01void __init mount_root(void)
02{
03#ifdef CONFIG_ROOT_NFS
04 if (MAJOR(ROOT_DEV) == UNNAMED_MAJOR) {
05  if (mount_nfs_root())
06   return;
07
08  printk(KERN_ERR "VFS: Unable to mount root fs via NFS, trying floppy.\n");
09  ROOT_DEV = Root_FD0;
10 }
11#endif
12#ifdef CONFIG_BLK_DEV_FD
13 if (MAJOR(ROOT_DEV) == FLOPPY_MAJOR) {
14  /* rd_doload is 2 for a dual initrd/ramload setup */
15  if (rd_doload==2) {
16   if (rd_load_disk(1)) {
17    ROOT_DEV = Root_RAM1;
18    root_device_name = NULL;
19   }
20  } else
21   change_floppy("root floppy");
22 }
23#endif
24#ifdef CONFIG_BLOCK
25 create_dev("/dev/root", ROOT_DEV);
26 mount_block_root("/dev/root", root_mountflags);
27#endif
28}

这里只执行了CONFI_BLOCK宏开关
第25行创建设备结点,这里我在ubutu上测试了一下,/dev/root是的连接是hda1
第26行挂载根文件系统
第五部分运行真正根目录中的init程序

01static noinline int init_post(void)
02 __releases(kernel_lock)
03{
04 /* need to finish all async __init code before freeing the memory */
05 async_synchronize_full();
06 free_initmem();
07 unlock_kernel();
08 mark_rodata_ro();
09 system_state = SYSTEM_RUNNING;
10 numa_default_policy();
11
12 if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)
13  printk(KERN_WARNING "Warning: unable to open an initial console.\n");
14
15 (void) sys_dup(0);
16 (void) sys_dup(0);
17
18 current->signal->flags |= SIGNAL_UNKILLABLE;
19
20 if (ramdisk_execute_command) {
21  run_init_process(ramdisk_execute_command);
22  printk(KERN_WARNING "Failed to execute %s\n",
23    ramdisk_execute_command);
24 }
25
26 /*
27  * We try each of these until one succeeds.
28  *
29  * The Bourne shell can be used instead of init if we are
30  * trying to recover a really broken machine.
31  */
32 if (execute_command) {
33  run_init_process(execute_command);
34  printk(KERN_WARNING "Failed to execute %s.  Attempting "
35     "defaults...\n", execute_command);
36 }
37 run_init_process("/sbin/init");
38 run_init_process("/etc/init");
39 run_init_process("/bin/init");
40 run_init_process("/bin/sh");
41
42 panic("No init found.  Try passing init= option to kernel.");
43}

第12行打开/dev/console设备文件
第15-16行将文件描述符0复制给文件描述符1和2。
第20-24行ramdisk_execute_command变量中如果指定了要运行的程序,就开始运行这个程序,在u-boot的命令行参数中会指定rdinit=…,这个时候ramdisk_execute_command等于这个参数指定的程序。也可能/init程序存在,哪么ramdisk_execute_command就等于/init,或者为空。本程序没有指定,所以这里为空。
第37行执行/sbin/init程序,这个程序存在于根文件系统,如果存在,执行它,系统的控制权交给/sbin/init,不再返回init_post函数
简易的流程图


 
参考资料
initramfs 简介:一个新的 initial RAM disks 模型
ramfs,tmpfs,rootfs and initramfs

 

阅读更多
个人分类: linux 虚拟文件系统
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭