天天看點

linux kernel_init

前言:

核心在啟動使用者空間程式時會建立兩個線程,kthread() 和 kernel_init()線程,在前一篇介紹了kthread()線程 ,本文不在贅述,這裡主要是對kernel_init()線程建立init=1号程序,并完成驅動子產品的注冊(這裡的驅動子產品是編譯進核心的子產品)。

1. kthreadd()

路徑:linux-3.10.x\init\main.c

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 | CLONE_SIGHAND); //init=1 号程序的建立
  numa_default_policy();
  pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES); //核心程序kthread的建立,用來管理核心層線程
  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);
}      

2. kernel_init()

 路徑:linux-3.10.x\init\main.c

功能:init=1号程序建立、核心驅動子產品注冊、啟動預設控制台/dev/console

static int __ref kernel_init(void *unused)
{
  //kernel_init_freeable内部使用了等待完成量“kthreadd_done”,通過核心啟動的封包我們得知,該線程雖然先建立
  //但卻是在“kthreadd”線程建立完才執行的,原因是kthreadd執行完後才喚醒完成量的,是以這裡會等待完成量!!!
  kernel_init_freeable();
  /* need to finish all async __init code before freeing the memory */
  async_synchronize_full();
  free_initmem(); //釋放init初始化資料段,見下面
  mark_rodata_ro(); //标記隻讀資料段為隻讀,arm平台不影響
  system_state = SYSTEM_RUNNING;//設定系統為運作狀态
  numa_default_policy(); //numa即非一緻性記憶體通路,設定預設政策即非一緻性記憶體通路,設定預設政策      
void free_initmem(void)
{
#ifdef CONFIG_HAVE_TCM
  extern char __tcm_start, __tcm_end;

  poison_init_mem(&__tcm_start, &__tcm_end - &__tcm_start);
  free_reserved_area(&__tcm_start, &__tcm_end, 0, "TCM link");
#endif

  poison_init_mem(__init_begin, __init_end - __init_begin); //釋放init段
  if (!machine_is_integrator() && !machine_is_cintegrator())
    free_initmem_default(0);
}      
/*
 * Poison init memory with an undefined instruction (ARM) or a branch to an
 * undefined instruction (Thumb).
 */
static inline void poison_init_mem(void *s, size_t count)
{
  u32 *p = (u32 *)s;
  for (; count != 0; count -= 4)
    *p++ = 0xe7fddef0;
    /*
    核心初始化時把這些記憶體區域初始化為0xe7fddef0 (an undefined instruction (ARM) or 
    a branch to an undefined instruction (Thumb)),如果運作時函數非法通路到了這些區域,
    會觸發一個undef instruction的異常并列印相應的回調,進而輔助開發人員更快地解決相關
    問題。
    */
}      

3. kernel_init_freeable()

路徑:linux-3.10.x\init\main.c

功能:kernel_init_freeable主要功能是,等待核心線程建立完wait_for_completion(&kthreadd_done)、注冊核心驅動子產品do_basic_setup()、啟動預設控制台/dev/console

static noinline void __init kernel_init_freeable(void)
{
  /*
   * Wait until kthreadd is all set-up.
   */
  wait_for_completion(&kthreadd_done); //等待完成量,即等待kthreadd線程完成

  /* Now the scheduler is fully set up and can do blocking allocations */
  gfp_allowed_mask = __GFP_BITS_MASK;

  /*
   * init can allocate pages on any node
   */
  set_mems_allowed(node_states[N_MEMORY]);
  /*
   * init can run on any cpu.
   */
  set_cpus_allowed_ptr(current, cpu_all_mask);

  cad_pid = task_pid(current);

  smp_prepare_cpus(setup_max_cpus);

  do_pre_smp_initcalls();
  lockup_detector_init();

  smp_init();
  sched_init_smp();

  do_basic_setup();

  /* Open the /dev/console on the rootfs, this should never fail */
  if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)
    pr_err("Warning: unable to open an initial console.\n");

  (void) sys_dup(0);
  (void) sys_dup(0);
  /*
   * 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();
  }

  /*
   * 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..
   */

  /* rootfs is available now, try loading default modules */
  load_default_modules();
}      

4. do_basic_setup()

路徑:linux-3.10.x\init\main.c

功能:初始化cpuset子系統、建立khelper線程隊列、核心子產品驅動注冊、

static void __init do_basic_setup(void)
{
  cpuset_init_smp(); //初始化核心control group的cpuset子系統
  usermodehelper_init();//建立khelper單線程工作隊列,用于協助建立和運作使用者空間程式
  shmem_init(); //初始化共享記憶體
  driver_init(); //驅動子產品總線、檔案系統注冊,包括bus、devtmpfs、platform
  init_irq_proc(); //建立/proc/irq目錄, 并初始化系統中所有中斷對應的子目錄
  do_ctors(); //執行核心的構造函數
  usermodehelper_enable(); //使能usermodehelper
  do_initcalls(); //調用level 0到level 7的initcall函數,依次的level名稱是"early", "core", "postcore", "arch", "subsys", "fs", "device", “late”,需要注意的kernel在這塊的命名有些問題,early_initcall對應的level小于0,pure_initcall對應level才是0
  random_int_secret_init(); //初始化随機數生成池
}      
void __init driver_init(void)
{
  /* These are the core pieces */
  devtmpfs_init();
  devices_init();
  buses_init();
  classes_init();
  firmware_init();
  hypervisor_init();

  /* These are also core pieces, but must come after the
   * core core pieces.
   */
  platform_bus_init(); 
  cpu_dev_init();
  memory_dev_init();
}      

5. do_initcalls()路徑:linux-3.10.x\init\main.c

static void __init do_initcalls(void)
{
  int level;

  for (level = 0; level < ARRAY_SIZE(initcall_levels) - 1; level++)
    do_initcall_level(level);
}      

繼續閱讀