“平安的祝福 + 原创作品转载请注明出处 + 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000 ”
init process是Linux系统的第一个用户态进程,那自然没有父亲。它是由Linux内核直接启动的。该进程读取系统的初始化脚本并执行其他的相关的程序,最终完成系统启动的整个过程、
start_kernel()是内核的汇编与C语言的交接点,在该函数以前,内核的代码都是用汇编写的,完成一些最基本的初始化与环境设置工作,比如内核代码载入内存并解压缩(现在的内核一般都经过压缩),CPU 的最基本初始化,为C代码的运行设置环境(C代码的运行是有一定环境要求的,比如stack的设置等)。这里一个不太确切的比喻是start_kernel()就像是C代码中的main()。我们知道对应用程序员而言,main()是他的入口,但实际上程序的入口是被包在了C库中,在链接阶段,linker会把它链接入你的程序中。main()中的argc,argv 等都不是平白无故来的,而它的任务中有一项就是为main()准备运行环境。
在start_kernel()中Linux将完成整个系统的内核初始化。内核初始化的最后一步rest_init函数就是启动init进程这个所有进程的祖先。
{ … printk(KERN_NOTICE"%s", linux_banner); //输出linux版本信息 setup_arch(&command_line); //设置与初始化硬件体系相关的环境并调用 //pageint_inig() ->devicemaps_init(mdesc);初始化异常向量表,初始化zone memory maps,初始化结构体:MACHINE_START等 sched_init() //初始化调度器…先于中断开始前 printk(boot_command_line); //提取分析核心启动参数过程(从bootloader 中传递) parse_early_param(); parse_args trap_init(); //自陷入口函数初始化,针对此版本arm中直接return early_irq_init(); //中断初始化过程…. init_IRQ(); init_timers(); //初始化定时器,开启定时器软中断服务以及注册服务程序以及初始化各CPU中的tev_base等init_timers()->run_timer_softirq()->__run_timers().. timekeeping_init(); time_init(); //设置定时器及返回当前时间 console_init() //初步的初始化控制台,此控制台只能打印出一些简单//的启动信息… vmalloc_init(); // vfs_caches_init_early(); // mem_init(); //初始化内存并计算可用内存大小 kmem_cache_init(); // 初始化SLAB缓存分配器 calibrate_delay(); //延迟校准,jiffy,记录系统的定时器的节拍数,每变化一次代表了系统定时器2个连续节拍时间的间隔。 fork_init(num_physpages); //初始化max_threads,init_task参数为fork()提供参考 buffer_init(); //初始化块设备读写缓冲区 vfs_caches_init(num_physpages); //初始化虚拟文件系统 inode_init() ->files_init() ->mnt_init()... signals_init(); //初始化内核信号队列…. rest_init(); //最后实际进入reset_init()函数,包括所有剩下的硬件//驱动,线程初始化等过程…这也最终完成//start_kernel//的启动过程。 }
其中rest_init()函数如下:
static noinline void __init_refok rest_init(void) { int pid; rcu_scheduler_starting(); /* * We need to spawn init first so that it obtains pid 1, however * the init task will end up wanting to create kthreads, which, if * we schedule it before we create kthreadd, will OOPS. */ kernel_thread(kernel_init, NULL, CLONE_FS); //创建一个内核线程,实际上是一个内核进程。其中kernel_init只是一个函数 numa_default_policy(); pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);//CLONE_FS | CLONE_SIGHAND为do_fork产生线程时的标志,表示进程间的fs信息共享,信号处理和块信号共享, rcu_read_lock(); kthreadd_task = find_task_by_pid_ns(pid, &init_pid_ns); rcu_read_unlock(); complete(&kthreadd_done); /* * The boot idle thread must execute schedule() * at least once to get things moving: */ init_idle_bootup_task(current); schedule_preempt_disabled(); /* Call into cpu_idle with preempt disabled */ cpu_startup_entry(CPUHP_ONLINE); }
kernel_thread(kernel_init, NULL, CLONE_FS); //创建一个内核线程,实际上是一个内核进程,其中pid=1。其中kernel_init只是一个函数
static int __ref kernel_init(void *unused) { int ret; kernel_init_freeable(); /* need to finish all async __init code before freeing the memory */ async_synchronize_full(); free_initmem(); mark_rodata_ro(); system_state = SYSTEM_RUNNING; numa_default_policy(); flush_delayed_fput(); if (ramdisk_execute_command) { ret = run_init_process(ramdisk_execute_command); if (!ret) return 0; pr_err("Failed to execute %s (error %d)\n", ramdisk_execute_command, ret); } /* * 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) { ret = run_init_process(execute_command); if (!ret) return 0; #ifndef CONFIG_INIT_FALLBACK panic("Requested init %s failed (error %d).", execute_command, ret); #else pr_err("Failed to execute %s (error %d). Attempting defaults...\n", execute_command, ret); #endif } if (!try_to_run_init_process("/sbin/init") || !try_to_run_init_process("/etc/init") || !try_to_run_init_process("/bin/init") || !try_to_run_init_process("/bin/sh"))
return 0;
panic("No working init found. Try passing init= option to kernel. " "See Linux Documentation/init.txt for guidance.");
}
try_to_run_init_process()其参数就是要执行的可执行文件名,也就是这里的init process在磁盘上的文件。
static int try_to_run_init_process(const char *init_filename) { int ret; ret = run_init_process(init_filename); if (ret && ret != -ENOENT) { pr_err("Starting init: %s exists but couldn‘t execute it (error %d)\n", init_filename, ret); } return ret; }
//try_to_run_init_process()实际上调用run_init_process();
static int run_init_process(const char *init_filename) { argv_init[0] = init_filename; return do_execve(getname_kernel(init_filename), (const char __user *const __user *)argv_init, (const char __user *const __user *)envp_init); }
//run_init_process()实际上调用do_execve();
int do_execve(struct filename *filename, const char __user *const __user *__argv, const char __user *const __user *__envp) { struct user_arg_ptr argv = { .ptr.native = __argv }; struct user_arg_ptr envp = { .ptr.native = __envp }; return do_execveat_common(AT_FDCWD, filename, argv, envp, 0); }
//do_execve()实际上调用do_execveat_common(),执行sched_exec(),然后返回一个值
static int do_execveat_common(int fd, struct filename *filename, struct user_arg_ptr argv, struct user_arg_ptr envp, int flags) { char *pathbuf = NULL; struct linux_binprm *bprm; struct file *file; struct files_struct *displaced; int retval; if (IS_ERR(filename)) return PTR_ERR(filename); /********
*********
*******
/ sched_exec(); /********
*********
*******
/
return reval; }
在执行完成rest_init()之后,此时init_task的任务基本上已经完全结束了,它将 沦落为一个idle task。事实上在更早前的start kernel()函数中的sched_init(),在sched_init()函数中, 通过init_idle(current, smp_processor_id())函数的调用就已经把init_task初始化成了一个idle task,init_idle函数的第一个参数current就是&init_task,在init_idle中将会把init_task加入到 cpu的运行队列中,这样当运行队列中没有别的就绪进程时,init_task(也就是idle task)将会被调用,它的核心是一个while(1)循环,在循环中它将会调用schedule函数以便在运行队列中有新进程加入时切换到该新进程上。
void __init sched_init(void) { /* * * */ /* * Make us the idle thread. Technically, schedule() should not be * called from this thread, however somewhere below it might be, * but because we are the idle thread, we just pick up running again * when this runqueue becomes "idle". */ init_idle(current, smp_processor_id()); /* * * */ scheduler_running = 1; }
断点的设置
运行到start_kernel处的情况
运行到sched_init()处的图象
运行到rest_init()处的图象
参考文献:
http://blog.csdn.net/yyplc/article/details/7030983
http://blog.csdn.net/hardy_2009/article/details/7383815
Linux---从start_kernel到init进程启动
原文:http://www.cnblogs.com/pingandezhufu/p/4356648.html