2015年(19)
分类: LINUX
2015-04-28 15:15:09
原文地址:Linux系统启动函数start_kernel探秘 作者:automaton
赵建清+原创作品转载请注明出处+《Linux内核分析》MOOC课程
asmlinkage __visible void __init start_kernel(void)
{
...
// lockdep_init()是个宏,定义在kernel/fork.c中第388行,如下:
// # define lockdep_init() do { } while (0)
// 可见没做什么事,留作未来扩展之用。
lockdep_init();
// init_task的类型为task_struct.task_struct包含了一个进程相关的所有信息,
// task_struct就是进程描述符(process descriptor)。
// init_task就是内核中第一个进程,也就是idle进程或0号进程的process descriptor,
// init_task由INIT_TASK宏完成初始化,
// set_task_stack_end_magic函数定义在kernel/fork.c中第297行到303行,
// 用于设置进程栈增长的终点。进程描述符和紧挨着的线程描述符thread_info,通常占据内核
// 分配的8K空间,并占据两个连续的内存页框,堆栈从这8K的高地址开始增长,在thread_info
// 结构外设置一个魔数,避免栈数据覆盖了thread_info结构。
set_task_stack_end_magic(&init_task);
// 以下3个函数定义为空,不做分析
smp_setup_processor_id();
debug_objects_early_init();
boot_init_stack_canary();
// 函数体为return 0,不做分析
cgroup_init_early();
// 关闭当前CPU中断
local_irq_disable();
early_boot_irqs_disabled = true;
// 在多CPU机器上选择CPU
boot_cpu_init();
// 定义在mm/highmem.c第479行,用于高端内存初始化
page_address_init();
// 打印linux版本信息
pr_notice("%s", linux_banner);
// setup_arch是个特定与体系结构的函数,主要关注内存管理各个方面的初始化。
// 在IA-32系统上,首先记录下内核在物理和虚拟内存中的位置。
// 调用setup_memory_map初始化bootmem分配器;
// 调用parse_early_param对命令行参数进行部分解释,处理与内存管理设置相关的参数;
setup_arch(&command_line);
mm_init_cpumask(&init_mm);
// 对command_line备份与保存
setup_command_line(command_line);
// 以下3个函数在IA-32定义为空
setup_nr_cpu_ids();
setup_per_cpu_areas();
smp_prepare_boot_cpu();
// 设置内存的相关节点和其中的内存域数据结构
build_all_zonelists(NULL, NULL);
// 初始化page allocation相关数据结构
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();
// 使用bootmem分配一个启动信息的缓冲区
setup_log_buf(0);
// 使用bootmem分配并初始化PID散列表
pidhash_init();
// 前期VFS缓存初始化
vfs_caches_init_early();
// 对内核异常表进行排序
sort_main_extable();
// 对内核陷阱异常经行初始化
trap_init();
// 初始化内核内存分配器,启动信息中的内存信息来自此函数中的mem_init函数
mm_init();
// 初始化调度器的数据结构,并创建运行队列
sched_init();
// 禁用抢占和中断,早期启动时期,调度是极其脆弱的
preempt_disable();
if (WARN(!irqs_disabled(),
"Interrupts were enabled *very* early, fixing it\n"))
local_irq_disable();
// 为IDR机制分配缓存
idr_init_cache();
// 内核RCU机制初始化
rcu_init();
context_tracking_init();
// 内核radix树算法初始化
radix_tree_init();
// 前期外部中断描述符初始化
early_irq_init();
// 架构相关中断初始化
init_IRQ();
// 初始化内核时钟
tick_init();
// 函数定义为空
rcu_init_nohz();
// 以下5个函数是软中断和内核时钟机制初始化
init_timers();
hrtimers_init();
softirq_init();
timekeeping_init();
time_init();
sched_clock_postinit();
perf_event_init();
// profile子系统初始化,内核的性能调试工具
profile_init();
call_function_init();
WARN(!irqs_disabled(), "Interrupts were enabled early\n");
// 开启总中断
early_boot_irqs_disabled = false;
local_irq_enable();
// slab分配器后期初始化
kmem_cache_init_late();
// 初始化控制台
console_init();
if (panic_later)
panic("Too many boot %s vars at `%s'", panic_later,
panic_param);
// 打印lockdep调试模块信息
lockdep_info();
// 开启总中断后,用于测试lock inversion bugs
locking_selftest();
...
// 以下3个函数定义为空
page_cgroup_init();
debug_objects_mem_init();
kmemleak_init();
// 设置每个CPU的页组并初始化
setup_per_cpu_pageset();
// 分一致性内存访问(NUMA)初始化
numa_policy_init();
if (late_time_init)
late_time_init();
// 初始化调度时钟
sched_clock_init();
calibrate_delay();
// PID分配映射初始化
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();
// 页回写机制初始化
page_writeback_init();
// proc文件系统初始化
proc_root_init();
// 定义为空
cgroup_init();
// CPUSET初始化
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();
}
使用gdb在实验楼环境下对内核进行跟踪,在start_kernel()函数中一共加了5个断点,分别是在stark_kernel()
函数、set_task_stack_end_magic()函数、page_address_init()函数、sched_init()函数和rest_init()函数:
使用continue命令运行内核,在第1个断点start_kernel处停止:
继续运行,在第2个断点set_task_stack_end_magic()函数处停止,使用step命令进入函数内部,准备
查看一下idle进程栈空间尽头的地址stackend,发现内核编译过程已经优化过,无法查看地址:
到达最后一个断点rest_init()函数:
idle进程调用schedule()函数激活其他进程: