Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1071442
  • 博文数量: 277
  • 博客积分: 8313
  • 博客等级: 中将
  • 技术积分: 2976
  • 用 户 组: 普通用户
  • 注册时间: 2010-04-22 11:25
文章分类

全部博文(277)

文章存档

2013年(17)

2012年(66)

2011年(104)

2010年(90)

我的朋友

分类: LINUX

2011-05-17 14:59:59


//创建一个虚拟文件系统的vfsmount结构缓存。每个挂载的文件系统都有一个

struct vfsmoun结构

       mnt_cache = kmem_cache_create("mnt_cache", sizeof(struct vfsmount),

                     0, SLAB_HWCACHE_ALIGN | SLAB_PANIC, NULL);

//创建文件系统挂载嘻哈表

       mount_hashtable = (struct list_head *)__get_free_page(GFP_ATOMIC);

 

       if (!mount_hashtable)

              panic("Failed to allocate mount hash table\n");

 

       printk("Mount-cache hash table entries: %lu\n", HASH_SIZE);

//初始化嘻哈表

       for (u = 0; u < HASH_SIZE; u++)

              INIT_LIST_HEAD(&mount_hashtable[u]);

 

       err = sysfs_init();//创建一个sysfs虚拟文件系统,并挂载为根文件系统。如果系统不指定sysfs,则此函数为空

       if (err)

              printk(KERN_WARNING "%s: sysfs_init error: %d\n",

                     __func__, err);

       fs_kobj = kobject_create_and_add("fs", NULL);//创建一个对象文件,加到文件系统中

       if (!fs_kobj)

              printk(KERN_WARNING "%s: kobj create error\n", __func__);

       init_rootfs();//注册一个rootfs文件系统

       init_mount_tree();//将上面创建的rootfs文件系统挂载为根文件系统。这只是个虚拟的文件系统,就好比只是创建了一个/目录。最后,这个函数会为系统最开始的进程( init_task 进程)准备他的进程数据块中的namespace 域,主要目的是将 do_kern_mount() 函数中建立的 mnt  dentry 信息记录在了 init_task 进程的进程数据块中,这样任何以后从 init_task 进程 fork 出来的进程也都先天地继承了这一信息。

//下面是进行radix树初始化。这个是linux2.6引入的为各种页面操作的重要结构之一struct  radix_tree_node。这种数据结构将指针与一个long型键值关联起来,提供高效快速查找。具体不再分析

       radix_tree_init();

       signals_init();//创建并初始化信号队列

       /* rootfs populating might need page-writeback */

       page_writeback_init();//CPU在内存中开辟高速缓存,CPU直接访问高速缓存提以高速度。当cpu更新了高速缓存的数据后,需要定期将高速缓存的数据写回到存储介质中,比如磁盘和flash等。这个函数初始化写回的周期

#ifdef CONFIG_PROC_FS

       proc_root_init();//如果配置了proc文件系统,则需初始化并加载proc文件系统。在根目录的proc文件夹就是proc文件系统,这个文件系统是ram类型的,记录系统的临时数据,系统关机后不会写回到flash

#endif

       cgroup_init();//没有配置cgroup,此函数为空

       cpuset_init();//CPU,此函数为空

       taskstats_init_early();//进程状态初始化,实际上就是分配了一个存储线程状态的高速缓存

       delayacct_init();//空函数

 

       check_bugs();//空函数

 

       acpi_early_init();//空函数

rest_init();//start_kernel启动的最后一个函数,进入这个函数,完成剩余启动的初始化

}

 

// rest_init()大致解释如下:

static void noinline __init_refok rest_init(void)

       __releases(kernel_lock)

{

       int pid;

//创建内核线程。Kernel_thread运用系统调用do_fork()产生新的子线程,子线程就调用传入的调用函数执行之,此处函数就是kernel_init. kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);

       numa_default_policy();//空函数

       pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);

       kthreadd_task = find_task_by_pid_ns(pid, &init_pid_ns);

       unlock_kernel();

 

       /*

        * The boot idle thread must execute schedule()

        * at least once to get things moving:

        */

       init_idle_bootup_task(current);

       preempt_enable_no_resched();

       schedule();

       preempt_disable();

 

       /* Call into cpu_idle with preempt disabled */

       cpu_idle();

}

// kernel_init通过调用do_basic_setup完成编译阶段注册的设备驱动程序初始化。

//这个函数又调用了一个很重要的初始化函数Do_initcalls()。它用来启动所有在__initcall_start__initcall_end段的函数,而静态编译进内核的modules也会将其入口放置在这段区间里。和根文件系统相关的初始化函数都会由rootfs_initcall()所引用。rootfs_initcall(populate_rootfs);

也就是说会在系统初始化的时候会调用populate_rootfs进行初始化。代码如下:

 

static int __init populate_rootfs(void)

{

       char *err = unpack_to_rootfs(__initramfs_start,

                      __initramfs_end - __initramfs_start, 0);

       if (err)

              panic(err);

       if (initrd_start) {

#ifdef CONFIG_BLK_DEV_RAM

              int fd;

              printk(KERN_INFO "checking if image is initramfs...");

              err = unpack_to_rootfs((char *)initrd_start,

                     initrd_end - initrd_start, 1);

              if (!err) {

                     printk(" it is\n");

                     unpack_to_rootfs((char *)initrd_start,

                            initrd_end - initrd_start, 0);

                     free_initrd();

                     return 0;

              }

              printk("it isn't (%s); looks like an initrd\n", err);

              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

              printk(KERN_INFO "Unpacking initramfs...");

              err = unpack_to_rootfs((char *)initrd_start,

                     initrd_end - initrd_start, 0);

              if (err)

                     panic(err);

              printk(" done\n");

              free_initrd();

#endif

       }

       return 0;

}

//unpack_to_rootfs就是解压包,所解得包就是usr/initramfs_data.cpio.gz下的文件系统。然后将其释放至上面创建的rootfs。注意这个文件系统已经在编译的时候用build_in.O的方式一种是跟kernel融为一体了所在的段就是__initramfs_start__initramfs_end的区域。这种情况下,直接调用unpack_to_rootfs将其释放到根目录.如果不是属于这种形式的。也就是由内核参数指定的文件系统,即image-initrd文件系统。如果配制CONFIG_BLK_DEV_RAM才会支持image-initrd。否则全当成cpio-initrd的形式处理。

对于是cpio-initrd的情况。直接将其释放到根目录。对于是image-initrd的情况。在根目录下建立/initrd.image文件,然后将INITRD写到这个文件中,并释放INITRD占用的空间。。

接下来,就开始具体的挂载操作,在kernel_init函数中完成

 

static int __init kernel_init(void * unused)

{

       lock_kernel();

       /*

        * init can run on any cpu.

        */

       set_cpus_allowed_ptr(current, CPU_MASK_ALL_PTR);

       /*

        * Tell the world that we're going to be the grim

        * reaper of innocent orphaned children.

        *

        * We don't want people to have to make incorrect

        * assumptions about where in the task array this

        * can be found.

        */

       init_pid_ns.child_reaper = current;

 

       cad_pid = task_pid(current);

 

       smp_prepare_cpus(setup_max_cpus);

 

       do_pre_smp_initcalls();

 

       smp_init();

       sched_init_smp();

 

       cpuset_init_smp();

 

       do_basic_setup();

 

       if (!ramdisk_execute_command)

              ramdisk_execute_command = "/init";

//如果存在指定的INITRD命令行参数,则执行命令行参数指定的init文件,如果不存在,则制定执行的命令为根目录下的init文件。如果用户指定的文件系统存在,则调用prepare_namespace();进行文件系统挂载的预操作

       if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) {

              ramdisk_execute_command = NULL;

//prepare_namespace()中首先会轮训检测块设备,若检测到则创建一个设备节点,然后分配一个设备号。如果saved_root_name不为空,则说明内核有指定设备作为根文件系统,则通过mount_block_root挂载根文件系统,然后退出即可。

比如有时指定了内核参数root=/dev/ram,则直接从这个位置进行挂载。

如果没有指定的块设备作为根文件系统,而是指明了INITRD映像,则调用initrd_load 函数挂载initram文件系统。这个函数首先创建/dev/ram设备节点,然后把映像拷贝到这个设备文件中,接着调用handle_initrdINITRD进行处理。

prepare_namespace()执行最后调用mount_root();将指定的文件系统挂接到/root下,然后切换当前目录到root下。再者,还需调用sys_mount(".", "/", NULL, MS_MOVE, NULL);将当前目录挂接为/根目录。


              prepare_namespace();

       }


//创建一个虚拟文件系统的vfsmount结构缓存。每个挂载的文件系统都有一个

struct vfsmoun结构

       mnt_cache = kmem_cache_create("mnt_cache", sizeof(struct vfsmount),

                     0, SLAB_HWCACHE_ALIGN | SLAB_PANIC, NULL);

//创建文件系统挂载嘻哈表

       mount_hashtable = (struct list_head *)__get_free_page(GFP_ATOMIC);

 

       if (!mount_hashtable)

              panic("Failed to allocate mount hash table\n");

 

       printk("Mount-cache hash table entries: %lu\n", HASH_SIZE);

//初始化嘻哈表

       for (u = 0; u < HASH_SIZE; u++)

              INIT_LIST_HEAD(&mount_hashtable[u]);

 

       err = sysfs_init();//创建一个sysfs虚拟文件系统,并挂载为根文件系统。如果系统不指定sysfs,则此函数为空

       if (err)

              printk(KERN_WARNING "%s: sysfs_init error: %d\n",

                     __func__, err);

       fs_kobj = kobject_create_and_add("fs", NULL);//创建一个对象文件,加到文件系统中

       if (!fs_kobj)

              printk(KERN_WARNING "%s: kobj create error\n", __func__);

       init_rootfs();//注册一个rootfs文件系统

       init_mount_tree();//将上面创建的rootfs文件系统挂载为根文件系统。这只是个虚拟的文件系统,就好比只是创建了一个/目录。最后,这个函数会为系统最开始的进程( init_task 进程)准备他的进程数据块中的namespace 域,主要目的是将 do_kern_mount() 函数中建立的 mnt  dentry 信息记录在了 init_task 进程的进程数据块中,这样任何以后从 init_task 进程 fork 出来的进程也都先天地继承了这一信息。

//下面是进行radix树初始化。这个是linux2.6引入的为各种页面操作的重要结构之一struct  radix_tree_node。这种数据结构将指针与一个long型键值关联起来,提供高效快速查找。具体不再分析

       radix_tree_init();

       signals_init();//创建并初始化信号队列

       /* rootfs populating might need page-writeback */

       page_writeback_init();//CPU在内存中开辟高速缓存,CPU直接访问高速缓存提以高速度。当cpu更新了高速缓存的数据后,需要定期将高速缓存的数据写回到存储介质中,比如磁盘和flash等。这个函数初始化写回的周期

#ifdef CONFIG_PROC_FS

       proc_root_init();//如果配置了proc文件系统,则需初始化并加载proc文件系统。在根目录的proc文件夹就是proc文件系统,这个文件系统是ram类型的,记录系统的临时数据,系统关机后不会写回到flash

#endif

       cgroup_init();//没有配置cgroup,此函数为空

       cpuset_init();//CPU,此函数为空

       taskstats_init_early();//进程状态初始化,实际上就是分配了一个存储线程状态的高速缓存

       delayacct_init();//空函数

 

       check_bugs();//空函数

 

       acpi_early_init();//空函数

rest_init();//start_kernel启动的最后一个函数,进入这个函数,完成剩余启动的初始化

}

 

// rest_init()大致解释如下:

static void noinline __init_refok rest_init(void)

       __releases(kernel_lock)

{

       int pid;

//创建内核线程。Kernel_thread运用系统调用do_fork()产生新的子线程,子线程就调用传入的调用函数执行之,此处函数就是kernel_init. kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);

       numa_default_policy();//空函数

       pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);

       kthreadd_task = find_task_by_pid_ns(pid, &init_pid_ns);

       unlock_kernel();

 

       /*

        * The boot idle thread must execute schedule()

        * at least once to get things moving:

        */

       init_idle_bootup_task(current);

       preempt_enable_no_resched();

       schedule();

       preempt_disable();

 

       /* Call into cpu_idle with preempt disabled */

       cpu_idle();

}

// kernel_init通过调用do_basic_setup完成编译阶段注册的设备驱动程序初始化。

//这个函数又调用了一个很重要的初始化函数Do_initcalls()。它用来启动所有在__initcall_start__initcall_end段的函数,而静态编译进内核的modules也会将其入口放置在这段区间里。和根文件系统相关的初始化函数都会由rootfs_initcall()所引用。rootfs_initcall(populate_rootfs);

也就是说会在系统初始化的时候会调用populate_rootfs进行初始化。代码如下:

 

static int __init populate_rootfs(void)

{

       char *err = unpack_to_rootfs(__initramfs_start,

                      __initramfs_end - __initramfs_start, 0);

       if (err)

              panic(err);

       if (initrd_start) {

#ifdef CONFIG_BLK_DEV_RAM

              int fd;

              printk(KERN_INFO "checking if image is initramfs...");

              err = unpack_to_rootfs((char *)initrd_start,

                     initrd_end - initrd_start, 1);

              if (!err) {

                     printk(" it is\n");

                     unpack_to_rootfs((char *)initrd_start,

                            initrd_end - initrd_start, 0);

                     free_initrd();

                     return 0;

              }

              printk("it isn't (%s); looks like an initrd\n", err);

              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

              printk(KERN_INFO "Unpacking initramfs...");

              err = unpack_to_rootfs((char *)initrd_start,

                     initrd_end - initrd_start, 0);

              if (err)

                     panic(err);

              printk(" done\n");

              free_initrd();

#endif

       }

       return 0;

}

//unpack_to_rootfs就是解压包,所解得包就是usr/initramfs_data.cpio.gz下的文件系统。然后将其释放至上面创建的rootfs。注意这个文件系统已经在编译的时候用build_in.O的方式一种是跟kernel融为一体了所在的段就是__initramfs_start__initramfs_end的区域。这种情况下,直接调用unpack_to_rootfs将其释放到根目录.如果不是属于这种形式的。也就是由内核参数指定的文件系统,即image-initrd文件系统。如果配制CONFIG_BLK_DEV_RAM才会支持image-initrd。否则全当成cpio-initrd的形式处理。

对于是cpio-initrd的情况。直接将其释放到根目录。对于是image-initrd的情况。在根目录下建立/initrd.image文件,然后将INITRD写到这个文件中,并释放INITRD占用的空间。。

接下来,就开始具体的挂载操作,在kernel_init函数中完成

 

static int __init kernel_init(void * unused)

{

       lock_kernel();

       /*

        * init can run on any cpu.

        */

       set_cpus_allowed_ptr(current, CPU_MASK_ALL_PTR);

       /*

        * Tell the world that we're going to be the grim

        * reaper of innocent orphaned children.

        *

        * We don't want people to have to make incorrect

        * assumptions about where in the task array this

        * can be found.

        */

       init_pid_ns.child_reaper = current;

 

       cad_pid = task_pid(current);

 

       smp_prepare_cpus(setup_max_cpus);

 

       do_pre_smp_initcalls();

 

       smp_init();

       sched_init_smp();

 

       cpuset_init_smp();

 

       do_basic_setup();

 

       if (!ramdisk_execute_command)

              ramdisk_execute_command = "/init";

//如果存在指定的INITRD命令行参数,则执行命令行参数指定的init文件,如果不存在,则制定执行的命令为根目录下的init文件。如果用户指定的文件系统存在,则调用prepare_namespace();进行文件系统挂载的预操作

       if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) {

              ramdisk_execute_command = NULL;

//prepare_namespace()中首先会轮训检测块设备,若检测到则创建一个设备节点,然后分配一个设备号。如果saved_root_name不为空,则说明内核有指定设备作为根文件系统,则通过mount_block_root挂载根文件系统,然后退出即可。

比如有时指定了内核参数root=/dev/ram,则直接从这个位置进行挂载。

如果没有指定的块设备作为根文件系统,而是指明了INITRD映像,则调用initrd_load 函数挂载initram文件系统。这个函数首先创建/dev/ram设备节点,然后把映像拷贝到这个设备文件中,接着调用handle_initrdINITRD进行处理。

prepare_namespace()执行最后调用mount_root();将指定的文件系统挂接到/root下,然后切换当前目录到root下。再者,还需调用sys_mount(".", "/", NULL, MS_MOVE, NULL);将当前目录挂接为/根目录。

 

 

              prepare_namespace();

       }

       init_post();

       return 0;

}

 

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);//创建一个设备节点,设备号是Root_RAM0,因此这个节点对应是/dev/ramINITRD

       /* mount initrd on rootfs' /root */

//将此设备挂接到/root

       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);//记录old目录的文件描述符

       /* move initrd over / and chdir/chroot in initrd root */

       sys_chdir("/root");//切换至root目录,刚才已经挂载了/dev/root.old

       sys_mount(".", "/", NULL, MS_MOVE, NULL);//当前目录挂载为根文件系统,也就是/dev/root.old变成现在的根文件系统

       sys_chroot(".");//切换到当前文件系统的根目录

       current->flags |= PF_FREEZER_SKIP;

//创建一个进程,运行目前文件系统下的/linuxrc文件

       pid = kernel_thread(do_linuxrc, "/linuxrc", SIGCHLD);

       if (pid > 0)

              while (pid != sys_wait4(-1, NULL, 0, NULL))

                     yield();

 

       current->flags &= ~PF_FREEZER_SKIP;

 

       /* move initrd to rootfs' /old */

       sys_fchdir(old_fd);

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

//处理完上面的文件,则initrd处理完之后,重新chroot进入rootfs

       /* switch root and cwd back to / of rootfs */

       sys_fchdir(root_fd);

       sys_chroot(".");

       sys_close(old_fd);

       sys_close(root_fd);

//如果real_root_dev linuxrc中重新设成Root_RAM0,则initrd就是最终的realfs了,改变当前目录到initrd中,不作后续处理直接返回。

       if (new_decode_dev(real_root_dev) == Root_RAM0) {

              sys_chdir("/old");

              return;

       }

//否则需要重新挂载上面linuxRC文件执行时指定的根文件系统

       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");

       }

}

static int noinline init_post(void)

{

       free_initmem();

       unlock_kernel();

       mark_rodata_ro();

       system_state = SYSTEM_RUNNING;

       numa_default_policy();

 

       if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)

              printk(KERN_WARNING "Warning: unable to open an initial console.\n");

 

       (void) sys_dup(0);

       (void) sys_dup(0);

 

       current->signal->flags |= SIGNAL_UNKILLABLE;

//刚才上面已经初始化了ramdisk_execute_command,此处可直接运行之。

如果不存在则运行下面程序,如果都不存在,则退出。

下面的/sbin/init就是上述挂载的根文件系统下的文件。

       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("No init found.  Try passing init= option to kernel.");

}

//当没有找到init程序后,则退出,进行进程调度,进入cpu_idle()进程

阅读(656) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~