操作系统基础:Linux内核启动过程

来源:互联网 发布:淘宝店铺图标大全 编辑:程序博客网 时间:2024/06/06 13:25
操作系统基础:Linux内核启动过程

林亮生 + 原创作品转载请注明出处 + 《Linux内核分析》MOOC课程

http://mooc.study.163.com/course/USTC-1000029000

1.gdb跟踪调试内核
qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img -s -S




此时进入调试模式,显示为一片黑屏。

2.gdb
(gdb) file linux-3.18.6/vmlinux  #将文件加载进来
(gdb) target remote:1234         #建立连接
(gdb) break start-kernel           #设置断点
(gdb) c                                     #Continue
(gdb) list                                  #查看代码
(gdb) rest_init
(gdb) c
(gdb) list


进入gdb调试界面


连接调试,打断点在start_kernel,并让程序执行


程序执行后会在start_kernel停下,这是停下后的界面


程序执行到rest_init停下的界面


程序执行结束界面

3.内核源码
start_kernel
asmlinkage __visible void __init start_kernel(void){char *command_line;char *after_dashes;set_task_stack_end_magic(&init_task);smp_setup_processor_id();debug_objects_early_init();/* * Set up the the initial canary ASAP: */boot_init_stack_canary();cgroup_init_early();local_irq_disable();early_boot_irqs_disabled = true;/* * Interrupts are still disabled. Do necessary setups, then * enable them */boot_cpu_init();page_address_init();pr_notice("%s", linux_banner);setup_arch(&command_line);mm_init_cpumask(&init_mm);setup_command_line(command_line);setup_nr_cpu_ids();setup_per_cpu_areas();boot_cpu_state_init();smp_prepare_boot_cpu();/* arch-specific boot-cpu hooks */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();/* * Set up the scheduler prior starting any interrupts (such as the * timer interrupt). Full topology setup happens at smp_init() * time - but meanwhile we still have a functioning scheduler. */sched_init();/* * 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();printk_nmi_init();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_INITRDif (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;}#endifpage_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_X86if (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();#endifthread_stack_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();}

比较关心的几个地方:
set_task_stack_end_magic(&init_task);
创建了0号进程

trap_init();
中断的初始化

mm_init();
内存管理的初始化

sched_init();
调度的初始化

rest_init();
其它的初始化

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);numa_default_policy();pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);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);
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;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.");}
其中
ret = run_init_process(ramdisk_execute_command);
而init_process就是Linux系统中的1号进程,也是第一个用户态进程;ramdisk_execute_command表明了根目录下的程序,找不到会继续往sbin,etc等目录下去寻找。

pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);
创建内核线程

cpu_startup_entry(CPUHP_ONLINE);
里面是一个while(1)的死循环,当系统没有进程需要执行时就调度到该idle进程。
0 0