arm Linux系统启动之----reset_init,系统1号进程

先来看下一些基础概念
内核线程(thread)或叫守护进程(daemon),在操作系统中占据相当大的比例,当Linux操作系统启动以后,
尤其是Xwindow也启动以后,你可以用”ps”命令查看系统中的进程,这时会发现很多以”d”结尾的进程名,这些进程就是内核线程。

内核线程也可以叫内核任务,它们周期性地执行,例如,磁盘高速缓存的刷新,网络连接的维护,页面的换入换出等等。在Linux中,内核线程与普通进程有一些本质的区别,从以下几个方面可以看出二者之间的差异:
·      内核线程执行的是内核中的函数,而普通进程只有通过系统调用才能执行内核中的函数。
·      内核线程只运行在内核态,而普通进程既可以运行在用户态,也可以运行在内核态。
·     因为内核线程指只运行在内核态,因此,它只能使用大于PAGE_OFFSET(3G)的地址空间。另一方面,不管在用户态还是内核态,普通进程可以使用4GB的地址空间。

在系统start_kernel最后的一个函数,起来的是系统的1号线程kernel_init

[cpp]  view plain copy
  1. static noinline void __init_refok rest_init(void)  
  2.  __releases(kernel_lock)  
  3. {  
  4.  int pid;  
  5.   
  6. //对于RCU机制可以看看上篇文章中对rcu引用的两篇博文  
  7.  rcu_scheduler_starting();  
  8.  /* 
  9.   * We need to spawn init first so that it obtains pid 1, however 
  10.   * the init task will end up wanting to create kthreads, which, if 
  11.   * we schedule it before we create kthreadd, will OOPS. 
  12.   */  
  13.  kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);  
  14.  numa_default_policy();  
  15.  pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);  
  16.  rcu_read_lock();  
  17.  kthreadd_task = find_task_by_pid_ns(pid, &init_pid_ns);  
  18.  rcu_read_unlock();  
  19.  complete(&kthreadd_done);  
  20.  unlock_kernel();  
  21.   
  22.  /* 
  23.   * The boot idle thread must execute schedule() 
  24.   * at least once to get things moving: 
  25.   */  
  26.  init_idle_bootup_task(current);  
  27.  preempt_enable_no_resched();  
  28.  schedule();  
  29.  preempt_disable();  
  30.   
  31.  /* Call into cpu_idle with preempt disabled */  
  32.  cpu_idle();  
  33. }  

 

这里我们来分析下kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND)其中的kernel_init。这里是我们建立的init进程。
执行kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES)来启动内核线程kthreadd,它的工作是用来运行kthread_create_list全局链表中的kthread
然后创建idle线程来占用掉cpu空闲时的时间片。

现在我们先看看kernel_init

[cpp]  view plain copy
  1. static int __init kernel_init(void * unused)  
  2. {  
  3.  /* 
  4.   * Wait until kthreadd is all set-up. 
  5.   */  
  6.  wait_for_completion(&kthreadd_done);  
  7.  lock_kernel();  
  8.   
  9.  /* 
  10.   * init can allocate pages on any node 
  11.   */  
  12.  set_mems_allowed(node_states[N_HIGH_MEMORY]);  
  13.  /* 
  14.   * init can run on any cpu. 
  15.   */  
  16.  set_cpus_allowed_ptr(current, cpu_all_mask);  
  17.  /* 
  18.   * Tell the world that we're going to be the grim 
  19.   * reaper of innocent orphaned children. 
  20.   * 
  21.   * We don't want people to have to make incorrect 
  22.   * assumptions about where in the task array this 
  23.   * can be found. 
  24.   */  
  25.  init_pid_ns.child_reaper = current;  
  26.   
  27.  cad_pid = task_pid(current);  
  28.   
  29.  smp_prepare_cpus(setup_max_cpus);  
  30.   
  31.  do_pre_smp_initcalls();  
  32.  start_boot_trace();  
  33.   
  34.  smp_init();  
  35.  sched_init_smp();  
  36.   
  37.  do_basic_setup();  
  38.   
  39.  /* Open the /dev/console on the rootfs, this should never fail */  
  40.  if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)  
  41.   printk(KERN_WARNING "Warning: unable to open an initial console./n");  
  42.   
  43.  (void) sys_dup(0);  
  44.  (void) sys_dup(0);  
  45.  /* 
  46.   * check if there is an early userspace init.  If yes, let it do all 
  47.   * the work 
  48.   */  
  49.   
  50.  if (!ramdisk_execute_command)  
  51.   ramdisk_execute_command = "/init";  
  52.   
  53.  if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) {  
  54.   ramdisk_execute_command = NULL;  
  55.   prepare_namespace();  
  56.  }  
  57.   
  58.  /* 
  59.   * Ok, we have completed the initial bootup, and 
  60.   * we're essentially up and running. Get rid of the 
  61.   * initmem segments and start the user-mode stuff.. 
  62.   */  
  63.   
  64.  init_post();  
  65.  return 0;  
  66. }  


进入kernel_init则在等待kthreadd_done的释放,这里就可以看见在reset_init中的对于thread的描述。
lock_kernel则是系统的大内核锁,紧接着系统进行一系列的初始化。
do_basic_setup是我们重点关注的函数

[c-sharp]  view plain copy
  1. /* 
  2.  * Ok, the machine is now initialized. None of the devices 
  3.  * have been touched yet, but the CPU subsystem is up and 
  4.  * running, and memory and process management works. 
  5.  * 
  6.  * Now we can finally start doing some real work.. 
  7.  */  
  8. static void __init do_basic_setup(void)  
  9. {  
  10.  init_workqueues();  
  11.  cpuset_init_smp();  
  12.  usermodehelper_init();  
  13.  init_tmpfs();  
  14.  driver_init();  
  15.  init_irq_proc();  
  16.  do_ctors();  
  17.  do_initcalls();  
  18. }  

 

init_workqueues,初始化工作队列events,每个CPU一个,这里就是管理中断下半部的workqueue在这里初始化了
 
cpuset_init_smp因为没有配置CONFIG_CPUSETS,是个空函数。
 
usermodehelper_init,初始化工作队列khelper,每个CPU一个。
 
init_tmpfs,注册并安装tmpfs文件系统,它的file_system_type结构如下:

[cpp]  view plain copy
  1. static struct file_system_type tmpfs_fs_type = {  
  2.        .owner           = THIS_MODULE,  
  3.        .name             = "tmpfs",  
  4.        .get_sb           = shmem_get_sb,  
  5.        .kill_sb    = kill_litter_super,  
  6. };  

 
driver_init,建立设备驱动模型sysfs的kset、kobject和subsystem结构,并向其中注册cpu、内存和总线的驱动。
 
init_irq_proc,向/proc文件系统中增加子目录irq来显示中断描述符表中的所有元素,在
系统运行的时候我们可以通过cat /proc/interrupts来查看注册的中断和当前产生的中断数。
 
do_ctors?????
 
最关心的do_initcalls()下面详细分析

[cpp]  view plain copy
  1. static void __init do_initcalls(void)  
  2. {  
  3.  initcall_t *fn;  
  4.   
  5.  for (fn = __early_initcall_end; fn < __initcall_end; fn++)  
  6.   do_one_initcall(*fn);  
  7.   
  8.  /* Make sure there is no pending stuff from the initcall sequence */  
  9.  flush_scheduled_work();  
  10. }  

 

这里牵扯到内核中的init段。先看看module_init这个常用的接口函数(include/init.h)

[cpp]  view plain copy
  1. #define __define_initcall(level,fn,id) /  
  2.  static initcall_t __initcall_##fn##id __used /  
  3.  __attribute__((__section__(".initcall" level ".init"))) = fn  
  4.    
  5. #define __initcall(fn) device_initcall(fn)  
  6.   
  7. #define module_init(x) __initcall(x);  

 

可以看见上述宏定义,最终指向init段
对于__early_initcall_end的定义可以参考(include/asm-generic/vmlinux.lds.h)

[cpp]  view plain copy
  1. #define INITCALLS       /  
  2.  *(.initcallearly.init)      /  
  3.  VMLINUX_SYMBOL(__early_initcall_end) = .;   /  
  4.    *(.initcall0.init)      /  
  5.    *(.initcall0s.init)      /  
  6.    *(.initcall1.init)      /  
  7.    *(.initcall1s.init)      /  
  8.    *(.initcall2.init)      /  
  9.    *(.initcall2s.init)      /  
  10.    *(.initcall3.init)      /  
  11.    *(.initcall3s.init)      /  
  12.    *(.initcall4.init)      /  
  13.    *(.initcall4s.init)      /  
  14.    *(.initcall5.init)      /  
  15.    *(.initcall5s.init)      /  
  16.  *(.initcallrootfs.init)      /  
  17.    *(.initcall6.init)      /  
  18.    *(.initcall6s.init)      /  
  19.    *(.initcall7.init)      /  
  20.    *(.initcall7s.init)  

  
这里在看

[cpp]  view plain copy
  1. #define pure_initcall(fn)  __define_initcall("0",fn,0)  
  2.   
  3. #define core_initcall(fn)  __define_initcall("1",fn,1)  
  4. #define core_initcall_sync(fn)  __define_initcall("1s",fn,1s)  
  5. #define postcore_initcall(fn)  __define_initcall("2",fn,2)  
  6. #define postcore_initcall_sync(fn) __define_initcall("2s",fn,2s)  
  7. #define arch_initcall(fn)  __define_initcall("3",fn,3)  
  8. #define arch_initcall_sync(fn)  __define_initcall("3s",fn,3s)  
  9. #define subsys_initcall(fn)  __define_initcall("4",fn,4)  
  10. #define subsys_initcall_sync(fn) __define_initcall("4s",fn,4s)  
  11. #define fs_initcall(fn)   __define_initcall("5",fn,5)  
  12. #define fs_initcall_sync(fn)  __define_initcall("5s",fn,5s)  
  13. #define rootfs_initcall(fn)  __define_initcall("rootfs",fn,rootfs)  
  14. #define device_initcall(fn)  __define_initcall("6",fn,6)  
  15. #define device_initcall_sync(fn) __define_initcall("6s",fn,6s)  
  16. #define late_initcall(fn)  __define_initcall("7",fn,7)  
  17. #define late_initcall_sync(fn)  __define_initcall("7s",fn,7s)  

 

就应该明白在内核中的init的初始化的调用顺序了吧。
在这里系统里所有的initcall就会被调用了,这是为了避免把所有代码集合到一块的一个方法。但是同一级别的init顺序则是由编译的链接顺序所决定的。


好了,相关的初始化完成之后,最后在kernel_init执行的是

[cpp]  view plain copy
  1. /* 
  2.   * check if there is an early userspace init.  If yes, let it do all 
  3.   * the work 
  4.   */  
  5.   
  6.  if (!ramdisk_execute_command)  
  7.   ramdisk_execute_command = "/init";  
  8.   
  9.  if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) {  
  10.   ramdisk_execute_command = NULL;  
  11.   prepare_namespace();  
  12.  }  

 

这段代码检查是否有必要mount根文件系统,如果vmlinuz中带有initfamfs,而且其中已经有init,那么就不这么做了(我现在工作用的目标系统就是这样的,里面有个init),否则的话内核还要mount init所在的(也是所有用户态进程的最除根文件系统)根文件系统,挂在根文件系统和执行init是linux启动过程最后要做的事情

最后执行init_post

[cpp]  view plain copy
  1. static noinline int init_post(void)  
  2.  __releases(kernel_lock)  
  3. {  
  4.  /* need to finish all async __init code before freeing the memory */  
  5.  async_synchronize_full();  
  6.  imv_unref_core_init();  
  7.  free_initmem();  
  8.  unlock_kernel();  
  9.  mark_rodata_ro();  
  10.  system_state = SYSTEM_RUNNING;  
  11.  numa_default_policy();  
  12.   
  13.  log_boot("Kernel_init_done");  
  14.   
  15.  current->signal->flags |= SIGNAL_UNKILLABLE;  
  16.   
  17.  if (ramdisk_execute_command) {  
  18.   run_init_process(ramdisk_execute_command);  
  19.   printk(KERN_WARNING "Failed to execute %s/n",  
  20.     ramdisk_execute_command);  
  21.  }  
  22.   
  23.  /* 
  24.   * We try each of these until one succeeds. 
  25.   * 
  26.   * The Bourne shell can be used instead of init if we are 
  27.   * trying to recover a really broken machine. 
  28.   */  
  29.  if (execute_command) {  
  30.   run_init_process(execute_command);  
  31.   printk(KERN_WARNING "Failed to execute %s.  Attempting "  
  32.      "defaults.../n", execute_command);  
  33.  }  
  34.  run_init_process("/sbin/init");  
  35.  run_init_process("/etc/init");  
  36.  run_init_process("/bin/init");  
  37.  run_init_process("/bin/sh");  
  38.   
  39.  panic("No init found.  Try passing init= option to kernel. "  
  40.        "See Linux Documentation/init.txt for guidance.");  
  41. }  

 

在这个函数可以看到释放了__init空间,释放了大内核锁,开始启动下一个init咯。

log_boot("Kernel_init_done");,so,kernel init到了这里也就告一段落了。

从启动过程可以看到涉及到内存管理,文件管理,时钟管理,进程管理,中断,调试,还有相关的机制等等。

路漫漫呀。。。


转载地址:http://blog.csdn.net/skywalkzf/article/details/6415708

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值