start_kernel到init进程启动的过程
2015-03-22 23:26
381 查看
你 之前了解了线程切换如何运作,现在该考虑内核是如何启动的了:
http://www.shiyanlou.com/courses/195 借用了课程中的实验楼:
首先启动内核并在启动处冻结: qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img-s -S# 关于-s和-S选项的说明: -S freeze CPU
at startup (use ’c’ to start execution)-s shorthandfor -gdb tcp::1234 若不想使用1234端口,则可以使用-gdb tcp:xxxx来取代-s选项;接着代开另一个终端,执行如下操作:gdb(gdb)file
linux-3.18.6/vmlinux# 在gdb界面中targe remote之前加载符号表(gdb)target remote:1234#
建立gdb和gdbserver之间的连接,按c 让qemu上的Linux继续运行(gdb)break start_kernel# 断点的设置可以在target remote之前,也可以在之后。
好了,debug环境已经建立好,接着改调试内核了。gdb中输入c使程序执行到断点处,
看到内核中使用的hash表挺有意思,小分析一下:
其中的hlist_node中的pprev成员挺有意思,一般双联可能会存一个hlist_node的prev指针,这里使用pprev存储了一个二级指针,其实指向上级指针的next指针。这样做便屏蔽了首节点和其余节点的区别(首节点无需pprev,但导致了首节点的特殊性,如此操作,自需要造作指向自己的指针和其指向下一个节点的指针即可)。
这次作业比较失败,等理解深入后进一步补充吧~~
http://www.shiyanlou.com/courses/195 借用了课程中的实验楼:
首先启动内核并在启动处冻结: qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img-s -S# 关于-s和-S选项的说明: -S freeze CPU
at startup (use ’c’ to start execution)-s shorthandfor -gdb tcp::1234 若不想使用1234端口,则可以使用-gdb tcp:xxxx来取代-s选项;接着代开另一个终端,执行如下操作:gdb(gdb)file
linux-3.18.6/vmlinux# 在gdb界面中targe remote之前加载符号表(gdb)target remote:1234#
建立gdb和gdbserver之间的连接,按c 让qemu上的Linux继续运行(gdb)break start_kernel# 断点的设置可以在target remote之前,也可以在之后。
好了,debug环境已经建立好,接着改调试内核了。gdb中输入c使程序执行到断点处,
asmlinkage __visible void __init start_kernel(void) { char *command_line; char *after_dashes; /* * Need to run as early as possible, to initialize the * lockdep hash: */ lockdep_init(); //初始化了两个链表作为hash表的表头 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(); 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, &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(); /* * 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(); 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_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 /* Should be run before the first non-init thread is created */ 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(); /* rootfs populating might need page-writeback */ 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(); /* Do the rest non-__init'ed, we're now alive */ rest_init(); }
看到内核中使用的hash表挺有意思,小分析一下:
struct hlist_head { struct hlist_node *first; }; struct hlist_node { struct hlist_node *next, **pprev; };
其中的hlist_node中的pprev成员挺有意思,一般双联可能会存一个hlist_node的prev指针,这里使用pprev存储了一个二级指针,其实指向上级指针的next指针。这样做便屏蔽了首节点和其余节点的区别(首节点无需pprev,但导致了首节点的特殊性,如此操作,自需要造作指向自己的指针和其指向下一个节点的指针即可)。
这次作业比较失败,等理解深入后进一步补充吧~~
相关文章推荐
- start_kernel到init进程启动的过程
- (作业3)Linux内核的启动过程(从start_kernel到init进程启动)
- kernel 启动过程之四,start_kernel中的rest_init函数到init进程
- 跟踪分析Linux内核的启动过程(start_kernel到init进程启动)
- 分析Linux内核启动过程:从start_kernel到init
- kernel 启动过程之三, start_kernel()函数 概叙!init/main.c
- 学习Linux内核启动过程:从start_kernel到init
- 跟踪内核从start_kernel到init进程启动
- 分析Linux内核启动过程:从start_kernel到init
- kernel 启动过程之四,start_kernel中的rest_init函数到init进程
- kernel 启动过程之三, start_kernel()函数 概叙!init/main.c
- MOOC-Linux内核lab3 调试内核从start_kernel到init进程启动
- 从start_kernel到init进程启动 《Linux内核分析》笔记
- kernel 启动过程之三, start_kernel()函数 概叙!init/main.c
- Linux内核分析-使用gdb跟踪调试内核从start_kernel到init进程启动
- Linux系统启动分析-从start_kernel到init进程的启动
- 使用gdb跟踪调试内核从start_kernel到init进程启动
- 利用gdb分析从start_kernel到init启动的过程
- kernel 启动过程之三, start_kernel()函数 概叙!init/main.c
- Linux内核分析之三——使用gdb跟踪调试内核从start_kernel到init进程启动