- 运行简单的Linux系统
- 调试Linux内核启动过程
- 分析
- main.c源码(仅供参考)
- 具体函数分析
- start_kernel()函数
- init_task()函数
- rest_init()函数
- 总结与反思
这里的bzImage是vmLinux经过gzip压缩后的文件,b是大内核,vmLinux 是编译出来的最原始的内核ELF文件。initrd是"intial ramdisk"也就是内存根文件系统。
注意gdb调试内核时必须加两个参数,第一个参数是 -s,这个-s是在1234端口上创建一个gdb-server当打开另一个窗口时,用gdb把带有符号表的内核镜像加载进来,连接gdb server,设置断点跟踪内核。 第二个参数是-S是Cpu初试化前冻结起来。
可以看见冻结起来了,现在进行gdb调试,注意进行gdb调试需要打开另一个窗口,把内核加载进来建立连接。
# 在gdb界面中targe remote之前加载符号表 (gdb)file linux-3.18.6/vmlinux
```javascript # 建立gdb和gdbserver之间的连接,按c 让qemu上的Linux继续运行 (gdb)target remote:1234
# 断点的设置可以在target remote之前,也可以在之后 (gdb)break start_kernel
Linunx源码目录如下:
// An highlighted block var foo = 'bar';a) Arch目录:存放处理器相关的代码。下设子目录,分别对应具体的CPU,每个子目录有boot,mm,以及kernel三个子目录,分别对应系统引导以及存储管理,和系统调用 b) Include目录:内核所需要的大部分头文件目录。与平台无关的在include/linux子目录下,与平台相关的则放在include相应的子目录中。 c) fs目录:存放各种文件系统的实现代码。 d) init目录:init子目录包含核心的初始化代码(不是系统的引导代码)。其包含两个文件main.c和version.c,可以用来研究核心如何工作。 e) ipc目录:包含核心进程间的通信代码。 f) kernel目录:包含内核管理的核心代码。与硬件相关代码放在archmm目录下。 h) scripts目录:包含用于配置核心的脚本文件。 i) lib目录:包含了核心的库代码,与硬件相关的库代码被放在arch #define DEBUG #include具体函数分析 start_kernel()函数#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef CONFIG_X86_LOCAL_APIC #include #endif static int kernel_init(void *); extern void init_IRQ(void); extern void fork_init(unsigned long); extern void radix_tree_init(void); #ifndef CONFIG_DEBUG_RODATA static inline void mark_rodata_ro(void) { } #endif bool early_boot_irqs_disabled __read_mostly; enum system_states system_state __read_mostly; EXPORT_SYMBOL(system_state); #define MAX_INIT_ARGS CONFIG_INIT_ENV_ARG_LIMIT #define MAX_INIT_ENVS CONFIG_INIT_ENV_ARG_LIMIT extern void time_init(void); void (*__initdata late_time_init)(void); char __initdata boot_command_line[COMMAND_LINE_SIZE]; char *saved_command_line; static char *static_command_line; static char *initcall_command_line; static char *execute_command; static char *ramdisk_execute_command; bool static_key_initialized __read_mostly; EXPORT_SYMBOL_GPL(static_key_initialized); unsigned int reset_devices; EXPORT_SYMBOL(reset_devices); static int __init set_reset_devices(char *str) { reset_devices = 1; return 1; } __setup("reset_devices", set_reset_devices); static const char *argv_init[MAX_INIT_ARGS+2] = { "init", NULL, }; const char *envp_init[MAX_INIT_ENVS+2] = { "HOME=/", "TERM=linux", NULL, }; static const char *panic_later, *panic_param; extern const struct obs_kernel_param __setup_start[], __setup_end[]; static int __init obsolete_checksetup(char *line) { const struct obs_kernel_param *p; int had_early_param = 0; p = __setup_start; do { int n = strlen(p->str); if (parameqn(line, p->str, n)) { if (p->early) { if (line[n] == ' ' || line[n] == '=') had_early_param = 1; } else if (!p->setup_func) { pr_warn("Parameter %s is obsolete, ignoredn", p->str); return 1; } else if (p->setup_func(line + n)) return 1; } p++; } while (p < __setup_end); return had_early_param; } unsigned long loops_per_jiffy = (1<<12); EXPORT_SYMBOL(loops_per_jiffy); static int __init debug_kernel(char *str) { console_loglevel = CONSOLE_LOGLEVEL_DEBUG; return 0; } static int __init quiet_kernel(char *str) { console_loglevel = CONSOLE_LOGLEVEL_QUIET; return 0; } early_param("debug", debug_kernel); early_param("quiet", quiet_kernel); static int __init loglevel(char *str) { int newlevel; if (get_option(&str, &newlevel)) { console_loglevel = newlevel; return 0; } return -EINVAL; } early_param("loglevel", loglevel); static int __init repair_env_string(char *param, char *val, const char *unused) { if (val) { if (val == param+strlen(param)+1) val[-1] = '='; else if (val == param+strlen(param)+2) { val[-2] = '='; memmove(val-1, val, strlen(val)+1); val--; } else BUG(); } return 0; } static int __init set_init_arg(char *param, char *val, const char *unused) { unsigned int i; if (panic_later) return 0; repair_env_string(param, val, unused); for (i = 0; argv_init[i]; i++) { if (i == MAX_INIT_ARGS) { panic_later = "init"; panic_param = param; return 0; } } argv_init[i] = param; return 0; } static int __init unknown_bootoption(char *param, char *val, const char *unused) { repair_env_string(param, val, unused); if (obsolete_checksetup(param)) return 0; if (strchr(param, '.') && (!val || strchr(param, '.') < val)) return 0; if (panic_later) return 0; if (val) { unsigned int i; for (i = 0; envp_init[i]; i++) { if (i == MAX_INIT_ENVS) { panic_later = "env"; panic_param = param; } if (!strncmp(param, envp_init[i], val - param)) break; } envp_init[i] = param; } else { unsigned int i; for (i = 0; argv_init[i]; i++) { if (i == MAX_INIT_ARGS) { panic_later = "init"; panic_param = param; } } argv_init[i] = param; } return 0; } static int __init init_setup(char *str) { unsigned int i; execute_command = str; for (i = 1; i < MAX_INIT_ARGS; i++) argv_init[i] = NULL; return 1; } __setup("init=", init_setup); static int __init rdinit_setup(char *str) { unsigned int i; ramdisk_execute_command = str; for (i = 1; i < MAX_INIT_ARGS; i++) argv_init[i] = NULL; return 1; } __setup("rdinit=", rdinit_setup); #ifndef CONFIG_SMP static const unsigned int setup_max_cpus = NR_CPUS; #ifdef CONFIG_X86_LOCAL_APIC static void __init smp_init(void) { APIC_init_uniprocessor(); } #else #define smp_init() do { } while (0) #endif static inline void setup_nr_cpu_ids(void) { } static inline void smp_prepare_cpus(unsigned int maxcpus) { } #endif static void __init setup_command_line(char *command_line) { saved_command_line = memblock_virt_alloc(strlen(boot_command_line) + 1, 0); initcall_command_line = memblock_virt_alloc(strlen(boot_command_line) + 1, 0); static_command_line = memblock_virt_alloc(strlen(command_line) + 1, 0); strcpy(saved_command_line, boot_command_line); strcpy(static_command_line, command_line); } static __initdata DECLARE_COMPLETION(kthreadd_done); static noinline void __init_refok rest_init(void) { int pid; rcu_scheduler_starting(); 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); init_idle_bootup_task(current); schedule_preempt_disabled(); cpu_startup_entry(CPUHP_ONLINE); } static int __init do_early_param(char *param, char *val, const char *unused) { const struct obs_kernel_param *p; for (p = __setup_start; p < __setup_end; p++) { if ((p->early && parameq(param, p->str)) || (strcmp(param, "console") == 0 && strcmp(p->str, "earlycon") == 0) ) { if (p->setup_func(val) != 0) pr_warn("Malformed early option '%s'n", param); } } return 0; } void __init parse_early_options(char *cmdline) { parse_args("early options", cmdline, NULL, 0, 0, 0, do_early_param); } void __init parse_early_param(void) { static int done __initdata; static char tmp_cmdline[COMMAND_LINE_SIZE] __initdata; if (done) return; strlcpy(tmp_cmdline, boot_command_line, COMMAND_LINE_SIZE); parse_early_options(tmp_cmdline); done = 1; } static void __init boot_cpu_init(void) { int cpu = smp_processor_id(); set_cpu_online(cpu, true); set_cpu_active(cpu, true); set_cpu_present(cpu, true); set_cpu_possible(cpu, true); } void __init __weak smp_setup_processor_id(void) { } # if THREAD_SIZE >= PAGE_SIZE void __init __weak thread_info_cache_init(void) { } #endif static void __init mm_init(void) { page_cgroup_init_flatmem(); mem_init(); kmem_cache_init(); percpu_init_late(); pgtable_init(); vmalloc_init(); } asmlinkage __visible void __init start_kernel(void) { char *command_line; char *after_dashes; lockdep_init(); set_task_stack_end_magic(&init_task); smp_setup_processor_id(); debug_objects_early_init(); boot_init_stack_canary(); cgroup_init_early(); local_irq_disable(); early_boot_irqs_disabled = true; 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(); smp_prepare_boot_cpu(); build_all_zonelists(NULL, NULL); page_alloc_init(); pr_notice("Kernel command line: %sn", boot_command_line); parse_early_param(); after_dashes = parse_args("Booting kernel", static_command_line, __start___param, __stop___param - __start___param, -1, -1, &unknown_bootoption); if (!IS_ERR_OR_NULL(after_dashes)) parse_args("Setting init args", after_dashes, NULL, 0, -1, -1, set_init_arg); jump_label_init(); setup_log_buf(0); pidhash_init(); vfs_caches_init_early(); sort_main_extable(); trap_init(); mm_init(); sched_init(); preempt_disable(); if (WARN(!irqs_disabled(), "Interrupts were enabled *very* early, fixing itn")) local_irq_disable(); idr_init_cache(); rcu_init(); context_tracking_init(); radix_tree_init(); 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 earlyn"); early_boot_irqs_disabled = false; local_irq_enable(); kmem_cache_init_late(); console_init(); if (panic_later) panic("Too many boot %s vars at `%s'", panic_later, panic_param); lockdep_info(); 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_cgroup_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 init_espfix_bsp(); #endif thread_info_cache_init(); cred_init(); fork_init(totalram_pages); proc_caches_init(); buffer_init(); key_init(); security_init(); dbg_late_init(); vfs_caches_init(totalram_pages); signals_init(); page_writeback_init(); proc_root_init(); cgroup_init(); cpuset_init(); taskstats_init_early(); delayacct_init(); check_bugs(); sfi_init_late(); if (efi_enabled(EFI_RUNTIME_SERVICES)) { efi_late_init(); efi_free_boot_services(); } ftrace_init(); rest_init(); } static void __init do_ctors(void) { #ifdef CONFIG_CONSTRUCTORS ctor_fn_t *fn = (ctor_fn_t *) __ctors_start; for (; fn < (ctor_fn_t *) __ctors_end; fn++) (*fn)(); #endif } bool initcall_debug; core_param(initcall_debug, initcall_debug, bool, 0644); #ifdef CONFIG_KALLSYMS struct blacklist_entry { struct list_head next; char *buf; }; static __initdata_or_module LIST_HEAD(blacklisted_initcalls); static int __init initcall_blacklist(char *str) { char *str_entry; struct blacklist_entry *entry; do { str_entry = strsep(&str, ","); if (str_entry) { pr_debug("blacklisting initcall %sn", str_entry); entry = alloc_bootmem(sizeof(*entry)); entry->buf = alloc_bootmem(strlen(str_entry) + 1); strcpy(entry->buf, str_entry); list_add(&entry->next, &blacklisted_initcalls); } } while (str_entry); return 0; } static bool __init_or_module initcall_blacklisted(initcall_t fn) { struct list_head *tmp; struct blacklist_entry *entry; char *fn_name; fn_name = kasprintf(GFP_KERNEL, "%pf", fn); if (!fn_name) return false; list_for_each(tmp, &blacklisted_initcalls) { entry = list_entry(tmp, struct blacklist_entry, next); if (!strcmp(fn_name, entry->buf)) { pr_debug("initcall %s blacklistedn", fn_name); kfree(fn_name); return true; } } kfree(fn_name); return false; } #else static int __init initcall_blacklist(char *str) { pr_warn("initcall_blacklist requires CONFIG_KALLSYMSn"); return 0; } static bool __init_or_module initcall_blacklisted(initcall_t fn) { return false; } #endif __setup("initcall_blacklist=", initcall_blacklist); static int __init_or_module do_one_initcall_debug(initcall_t fn) { ktime_t calltime, delta, rettime; unsigned long long duration; int ret; printk(KERN_DEBUG "calling %pF @ %in", fn, task_pid_nr(current)); calltime = ktime_get(); ret = fn(); rettime = ktime_get(); delta = ktime_sub(rettime, calltime); duration = (unsigned long long) ktime_to_ns(delta) >> 10; printk(KERN_DEBUG "initcall %pF returned %d after %lld usecsn", fn, ret, duration); return ret; } int __init_or_module do_one_initcall(initcall_t fn) { int count = preempt_count(); int ret; char msgbuf[64]; if (initcall_blacklisted(fn)) return -EPERM; if (initcall_debug) ret = do_one_initcall_debug(fn); else ret = fn(); msgbuf[0] = 0; if (preempt_count() != count) { sprintf(msgbuf, "preemption imbalance "); preempt_count_set(count); } if (irqs_disabled()) { strlcat(msgbuf, "disabled interrupts ", sizeof(msgbuf)); local_irq_enable(); } WARN(msgbuf[0], "initcall %pF returned with %sn", fn, msgbuf); return ret; } extern initcall_t __initcall_start[]; extern initcall_t __initcall0_start[]; extern initcall_t __initcall1_start[]; extern initcall_t __initcall2_start[]; extern initcall_t __initcall3_start[]; extern initcall_t __initcall4_start[]; extern initcall_t __initcall5_start[]; extern initcall_t __initcall6_start[]; extern initcall_t __initcall7_start[]; extern initcall_t __initcall_end[]; static initcall_t *initcall_levels[] __initdata = { __initcall0_start, __initcall1_start, __initcall2_start, __initcall3_start, __initcall4_start, __initcall5_start, __initcall6_start, __initcall7_start, __initcall_end, }; static char *initcall_level_names[] __initdata = { "early", "core", "postcore", "arch", "subsys", "fs", "device", "late", }; static void __init do_initcall_level(int level) { initcall_t *fn; strcpy(initcall_command_line, saved_command_line); parse_args(initcall_level_names[level], initcall_command_line, __start___param, __stop___param - __start___param, level, level, &repair_env_string); for (fn = initcall_levels[level]; fn < initcall_levels[level+1]; fn++) do_one_initcall(*fn); } static void __init do_initcalls(void) { int level; for (level = 0; level < ARRAY_SIZE(initcall_levels) - 1; level++) do_initcall_level(level); } static void __init do_basic_setup(void) { cpuset_init_smp(); usermodehelper_init(); shmem_init(); driver_init(); init_irq_proc(); do_ctors(); usermodehelper_enable(); do_initcalls(); random_int_secret_init(); } static void __init do_pre_smp_initcalls(void) { initcall_t *fn; for (fn = __initcall_start; fn < __initcall0_start; fn++) do_one_initcall(*fn); } void __init load_default_modules(void) { load_default_elevator_module(); } 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); } 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; } static noinline void __init kernel_init_freeable(void); static int __ref kernel_init(void *unused) { int ret; kernel_init_freeable(); 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); } if (execute_command) { ret = run_init_process(execute_command); if (!ret) return 0; pr_err("Failed to execute %s (error %d). Attempting defaults...n", 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."); } static noinline void __init kernel_init_freeable(void) { wait_for_completion(&kthreadd_done); gfp_allowed_mask = __GFP_BITS_MASK; set_mems_allowed(node_states[N_MEMORY]); 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(); 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); 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(); } load_default_modules(); }
这里相当于Main函数,位置是main.c中的第500行,在这个函数被调用前,内核的代码是用汇编写,初始化硬件系统,为C代码的运行设置环境。里面涉及到了内核的主要模块
init_task()函数set_task_stack_end_magic(&init_task);
这个init_task就是0号进程,是task_struct类型,也是进程描述符,初始化是通过宏定义INIT_TASK
rest_init()函数在main.c中的393行
kernel_thread(kernel_init, NULL, CLONE_FS);
pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);
这里新建了两个内核线程,一个是kernel_init,一个是kthreadd。
需要注意的是:
对于kernel_thread()是fork一个新进程来执行kernel_init()函数 init_task是用宏进行初始化
kernel_thread执行了kthreadd,创建PID为2的线程,kthreadd是管理和调度其他内核线程kernel_thread。在这个kthreadd函数中有一个for循环,运行了kthread_create_list全局链表中维护的kthread,create_kthread函数是调用kernel_thread生成一个新的进程并加入链表,换句话说其实所有内核进程都是直接或者间接以kthreadd为父进程。
所以其实启动内核创建进程主要逻辑如下:
综上也就是start_kernel函数进行了大量初始化工作,主要是对硬件的初始化,而rest_init函数是对进程空间的初始化,然后内核启动。
之前从来没对内核进行分析过,所以对我而言算是全新的知识点,看C语言代码还是觉得有点难度,与我之前学过的C语言有所不同,主要分析了两个函数第一个是start_kenerl,第二个是rest_init()。使用init_task()创建了一个0号进程,进行了初始化,在进入rest_init()创建kernel_init()和kthreadd(),第一个是用户进程,第二个是内核线程。
接下来会多读内核源码,多对源码进行分析,多使用gdb调试,多设断点,深刻理解代码运行过程。



