“Kernel boot sequence”的版本间的差异
来自个人维基
free6d1823(讨论 | 贡献) |
free6d1823(讨论 | 贡献) |
||
第173行: | 第173行: | ||
int pid; | int pid; | ||
− | rcu_scheduler_starting(); | + | rcu_scheduler_starting(); //rcu_schedulr_active = 1 |
− | smpboot_thread_init(); | + | smpboot_thread_init();//if config SMP, register_cpu_notifier |
/* | /* | ||
* We need to spawn init first so that it obtains pid 1, however | * We need to spawn init first so that it obtains pid 1, however | ||
第180行: | 第180行: | ||
* we schedule it before we create kthreadd, will OOPS. | * we schedule it before we create kthreadd, will OOPS. | ||
*/ | */ | ||
− | kernel_thread(kernel_init, NULL, CLONE_FS); | + | kernel_thread(kernel_init, NULL, CLONE_FS); //第三步 |
numa_default_policy(); | numa_default_policy(); | ||
− | pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES); | + | pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);//kthreadd (kernel/kthread.c) |
rcu_read_lock(); | rcu_read_lock(); | ||
kthreadd_task = find_task_by_pid_ns(pid, &init_pid_ns); | kthreadd_task = find_task_by_pid_ns(pid, &init_pid_ns); |
2019年11月7日 (四) 12:02的最后版本
Kernel 4.4
第一步、函数start_kernel(): (init/main.c)
asmlinkage __visible void __init start_kernel(void) { set_task_stack_end_magic(&init_task);//STACK_END_MAGIC=0x57AC6E90 smp_setup_processor_id();//printf("Booting Linux on physical CPU 0x%") local_irq_disable(); //屏蔽CPU0上的中断 (include/linux/irqflags.h); early_boot_irqs_disabled = true; boot_cpu_init();// (init/main.c)激活current thread CPU which is CPU0; page_address_init(); // (mm/highmem.c)初始化内存子系统; pr_notice("%s", linux_banner);// (init/version.c)打印内核版本信息到终端; setup_arch(&command_line);//设置架构特有的子系统如内存、I/O、处理器、等等,其中command_line 是从U-Boot传来的参数列表 mm_init_cpumask(&init_mm); setup_command_line(command_line); setup_nr_cpu_ids(); setup_per_cpu_areas();//设置多处理器环境,这个函数确定单个CPU所需要的内存大小,并分配和初始化4个核分别所需要的内存,这样一来,每一个CPU有自己的区域放置数据;(init/main.c) smp_prepare_boot_cpu(); //允许current processor(CPU0)访问自己的初始化过的数据;(arch/arm/kernel/smp.c) build_all_zonelists(NULL, NULL); page_alloc_init(); pr_notice("Kernel command line: %s\n", boot_command_line); parse_early_param(); after_dashes = parse_args("Booting kernel", static_command_line, __start___param, __stop___param - __start___param, -1, -1, NULL, &unknown_bootoption); if (!IS_ERR_OR_NULL(after_dashes)) parse_args("Setting init args", after_dashes, NULL, 0, -1, -1, NULL, set_init_arg); jump_label_init(); /* * These use large bootmem allocations and must precede * kmem_cache_init() */ setup_log_buf(0); pidhash_init(); vfs_caches_init_early(); sort_main_extable(); trap_init(); mm_init(); sched_init();//(kernel/sched.c)设置Linux调度器; // 1)为每一个cpu相应的数据初始化一个运行队列; // 2)用函数init_idle(current, smp_processor_id())为cpu0 fork一个idle线程; /* * Disable preemption - early bootup scheduling is extremely * fragile until we cpu_idle() for the first time. */ preempt_disable(); if (WARN(!irqs_disabled(), "Interrupts were enabled *very* early, fixing it\n")) local_irq_disable(); idr_init_cache(); rcu_init(); /* trace_printk() and trace points may be used after this */ trace_init(); context_tracking_init(); radix_tree_init(); /* init some links before init_ISA_irqs() */ early_irq_init(); init_IRQ(); tick_init(); rcu_init_nohz(); init_timers(); hrtimers_init(); softirq_init(); timekeeping_init(); time_init(); sched_clock_postinit(); perf_event_init(); profile_init(); call_function_init(); WARN(!irqs_disabled(), "Interrupts were enabled early\n"); early_boot_irqs_disabled = false; local_irq_enable(); kmem_cache_init_late(); /* * HACK ALERT! This is early. We're enabling the console before * we've done PCI setups etc, and console_init() must be aware of * this. But we do want output early, in case something goes wrong. */ console_init(); if (panic_later) panic("Too many boot %s vars at `%s'", panic_later, panic_param); lockdep_info(); /* * Need to run this when irqs are enabled, because it wants * to self-test [hard/soft]-irqs on/off lock inversion bugs * too: */ locking_selftest(); #ifdef CONFIG_BLK_DEV_INITRD if (initrd_start && !initrd_below_start_ok && page_to_pfn(virt_to_page((void *)initrd_start)) < min_low_pfn) { pr_crit("initrd overwritten (0x%08lx < 0x%08lx) - disabling it.\n", page_to_pfn(virt_to_page((void *)initrd_start)), min_low_pfn); initrd_start = 0; } #endif page_ext_init(); debug_objects_mem_init(); kmemleak_init(); setup_per_cpu_pageset(); numa_policy_init(); if (late_time_init) late_time_init(); sched_clock_init(); calibrate_delay(); pidmap_init(); anon_vma_init(); acpi_early_init(); #ifdef CONFIG_X86 if (efi_enabled(EFI_RUNTIME_SERVICES)) efi_enter_virtual_mode(); #endif #ifdef CONFIG_X86_ESPFIX64 /* Should be run before the first non-init thread is created */ init_espfix_bsp(); #endif thread_info_cache_init(); cred_init(); fork_init(); proc_caches_init(); buffer_init(); key_init(); security_init(); dbg_late_init(); vfs_caches_init(); signals_init(); /* rootfs populating might need page-writeback */ page_writeback_init(); proc_root_init(); nsfs_init(); cpuset_init(); cgroup_init(); taskstats_init_early(); delayacct_init(); check_bugs(); acpi_subsystem_init(); sfi_init_late(); if (efi_enabled(EFI_RUNTIME_SERVICES)) { efi_late_init(); efi_free_boot_services(); } ftrace_init(); /* Do the rest non-__init'ed, we're now alive */ rest_init();//建立进程1;
第二步、函数rest_init(): (init/main.c)
static noinline void __init_refok rest_init(void) { int pid; rcu_scheduler_starting(); //rcu_schedulr_active = 1 smpboot_thread_init();//if config SMP, register_cpu_notifier /* * 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); //第三步 numa_default_policy(); pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);//kthreadd (kernel/kthread.c) 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_init(): (init/main.c) <进程1>
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_readonly(); 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; panic("Requested init %s failed (error %d).", execute_command, ret); } 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."); }
第四步、函数init_post() (init/main.c):
这里是我们切换到用户模式的地方,调用下面的序列, 优先择一调用: run_init_process("/sbin/init"); run_init_process("/etc/init"); run_init_process("/bin/init"); run_init_process("/bin/sh");
第五步、/sbin/init 进程执行,并在终端上显示很多信息,并且最后它把控制权交给终端,停留在激活状态。
Reference: https://blog.csdn.net/cs0301lm/article/details/41078599