2015年(109)
分类: LINUX
2015-01-23 16:17:24
【原创】Linux arm 启动 c语言部分详解第六讲(终结篇,1号进程)
written by leeming
1号进程(init进程)
static int init(void * unused)
{
lock_kernel();//这是1号进程的lock
child_reaper = current;//child_reaper是1号进程
/***********smp的一些操作*****************/
smp_prepare_cpus(max_cpus);
do_pre_smp_initcalls();
fixup_cpu_present_map();
smp_init();
sched_init_smp();
cpuset_init_smp();
/*********************************/
/*在initcalls之前安装文件系统,因为有些驱动可能会
访问,这部分主要针对initrd,在我们一般使用的系统
是不用用到的*/
/*第一种方式已经用过了,比如直接在mtdblock3上挂载
jffs2根文件系统。第二种方式要分为两种情况,一种是
image-initrd,一种是cpio-initrd。在host上普遍采用cpio-initrd了,但
是在嵌入式系统上还是采用image-initrd多。本来想探讨
一下如何使用cpio-initrd,没有结果,暂时放弃。第三
种方式实际上就是把fs作到kernel中,实际的映像就是
kernel+fs了。不过第三种方式对小系统来说合适,一
旦这个映像比较大,那么更多的时间会消耗在解压
缩上,反而得不偿失了。*/
populate_rootfs();
do_basic_setup();
{
/* drivers will send hotplug events */
init_workqueues();
//启动了用户态的khelper进程
usermodehelper_init();
driver_init();
#ifdef CONFIG_SYSCTL
sysctl_init();
#endif
//Do_initcalls()用来启动所有在__initcall_start和__initcall_end段的函
//数,而静态编译进内核的modules也会将其入口放置在这段区间里。
do_initcalls();
}
/*
* check if there is an early userspace init. If yes, let it do all
* the work
*/
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();
{
int is_floppy;
//我们没有使用devfs
mount_devfs();
if (root_delay) {
printk(KERN_INFO "Waiting %dsec before mounting root device...\n",
root_delay);
ssleep(root_delay);
}
//这是定义CONFIG_BLK_DEV_MD才会用到
md_run_setup();
if (saved_root_name[0]) {
root_device_name = saved_root_name;
//这一步很重要,解析了参数中的root选项,获得要挂载的设备号
ROOT_DEV = name_to_dev_t(root_device_name);
{
char s[32];
char *p;
dev_t res = 0;
int part;
#ifdef CONFIG_SYSFS
int mkdir_err = sys_mkdir("/sys", 0700);
if (sys_mount("sysfs", "/sys", "sysfs", 0, NULL) < 0)
goto out;
#endif
//如果bootargs不是以root=/dev/**开始,则进入循环
//我们也可以通过设备号,例root=31:03等价于/dev/mtdblock3
if (strncmp(name, "/dev/", 5) != 0) {
unsigned maj, min;
if (sscanf(name, "%u:%u", &maj, &min) == 2) {
res = MKDEV(maj, min);
if (maj != MAJOR(res) || min != MINOR(res))
goto fail;
} else {
res = new_decode_dev(simple_strtoul(name, &p, 16));
if (*p)
goto fail;
}
goto done;
}
name += 5;
res = Root_NFS;
if (strcmp(name, "nfs") == 0)
goto done;
res = Root_RAM0;
if (strcmp(name, "ram") == 0)
goto done;
//root参数的名字不能过长,即/dev/后面的个数不能超过31
if (strlen(name) > 31)
goto fail;
strcpy(s, name);
//将参数中的/换为!
for (p = s; *p; p++)
if (*p == '/')
*p = '!';
//除了nfs,ram外其他的root参数通过/sys/block/%s/dev找到设备号
res = try_name(s, 0);
if (res)
goto done;
while (p > s && isdigit(p[-1]))
p--;
if (p == s || !*p || *p == '0')
goto fail;
part = simple_strtoul(p, NULL, 10);
*p = '\0';
res = try_name(s, part);
if (res)
goto done;
if (p < s + 2 || !isdigit(p[-2]) || p[-1] != 'p')
goto fail;
p[-1] = '\0';
res = try_name(s, part);
done:
#ifdef CONFIG_SYSFS
sys_umount("/sys", 0);
out:
if (!mkdir_err)
sys_rmdir("/sys");
#endif
return res;
fail:
res = 0;
goto done;
}
if (strncmp(root_device_name, "/dev/", 5) == 0)
root_device_name += 5;
}
is_floppy = MAJOR(ROOT_DEV) == FLOPPY_MAJOR;
if (initrd_load())
goto out;
if (is_floppy && rd_doload && rd_load_disk(0))
ROOT_DEV = Root_RAM0;
//挂载设备
mount_root();
out:
umount_devfs("/dev");
sys_mount(".", "/", NULL, MS_MOVE, NULL);
sys_chroot(".");
security_sb_post_mountroot();
mount_devfs_fs ();
}
}
/*
* Ok, we have completed the initial bootup, and
* we're essentially up and running. Get rid of the
* initmem segments and start the user-mode stuff..
*/
//将以init标示的函数空间释放,也就是我们启动的时候
//经常会冒出来:Freeing init memory: 116K
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);
if (ramdisk_execute_command) {
run_init_process(ramdisk_execute_command);
printk(KERN_WARNING "Failed to execute %s\n",
ramdisk_execute_command);
}
/*
* We try each of these until one succeeds.
*
* The Bourne shell can be used instead of init if we are
* trying to recover a really broken machine.
*/
if (execute_command) {
run_init_process(execute_command);
printk(KERN_WARNING "Failed to execute %s. Attempting "
"defaults...\n", execute_command);
}
//一个个查找init进程
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.");
}