分类:
2010-10-13 10:03:51
//创建一个虚拟文件系统的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_initrd对INITRD进行处理。
在prepare_namespace()执行最后调用mount_root();将指定的文件系统挂接到/root下,然后切换当前目录到root下。再者,还需调用sys_mount(".",
"/", NULL, MS_MOVE, NULL);将当前目录挂接为/根目录。
prepare_namespace();
}