您的位置:首页 > 其它

start_kernel分析

2013-04-21 12:37 141 查看
在构架相关的汇编代码运行完之后,程序跳入了构架无关的内核C语言代码:init/main.c中的start_kernel函数,在这个函数中Linux内核开始真正进入初始化阶段。

下面就顺这代码逐个函数的解释,但是这里并不会过于深入每个函数,因为这样就会只见树木,不见森林。分析代码首先要从构架上宏观地理解,然后再去考虑细节问题

asmlinkage void __init start_kernel(void)

{

char * command_line;

extern struct kernel_param __start___param[], __stop___param[];

点击(此处)折叠或打开

声明结构标签为kernel_param 的2个变量__start___param[], __stop___param[];

这里关键是这两个变量的地址是如何确定的。

这两个变量为地址指针,指向内核启动参数处理相关结构体段在内存中的位置(虚拟地址)。

对于ARM平台,位于 kernel\include\asm-generic\vmlinux.lds.h

kernel_param结构体的定义是:(标签为kernel_param的结构体)

点击(此处)折叠或打开

struct kernel_param {

const char
*name;

unsigned int perm;

param_set_fn set;

param_get_fn get;

union {

void *arg;

const struct kparam_string
*str;

const struct kparam_array
*arr;

};

smp_setup_processor_id();

点击(此处)折叠或打开

这个函数是针对SMP处理器的,经查阅资料,其作用是获取当前CPU的的硬件ID。
如果不是多处理器构架,在其他文件中就不会定义这个函数,此时使用本文件定义的弱引用函数:
void __init __weak smp_setup_processor_id(void)
{
}

/*

*必须尽早运行这个程序, 作用是初始化

* lockdep 模块的hash表:

*/

lockdep_init();

点击(此处)折叠或打开

lockdep是一个内核调试模块,用来检查内核互斥机制(尤其是自旋锁)潜在的死锁问题。

由于自旋锁以查询方式等待,不释放处理器,比一般互斥机制更容易死锁,故引入lockdep检查以下几种可能的死锁情况:
1.同一个进程递归地加锁同一把锁;
2.一把锁既在中断(或中断下半部)使能的情况下执行过加锁操作, 又在中断(或中断下半部)里执行过加锁操作。这样该锁有可能在锁定时由于中断发生又试图在同一处理器上加锁;

3.加锁后导致依赖图产生成闭环,这是典型的死锁现象。

debug_objects_early_init();

点击(此处)折叠或打开

在启动早期初始化hash buckets 和链接静态的 pool objects对象到 poll 列表. 在这个调用完成后 object tracker 已经开始完全运作了.

/*

* Set up the the initial canary ASAP: 初始化栈canary值:

*/

boot_init_stack_canary();

点击(此处)折叠或打开

canary值的是用于防止栈溢出攻击的堆栈的保护字 。

参考资料: GCC 中的编译器堆栈保护技术

cgroup_init_early();

点击(此处)折叠或打开

cgroup: 它的全称为control group.即一组进程的行为控制.
该函数主要是做数据结构和其中链表的初始化
参考资料: Linux cgroup机制分析之框架分析

local_irq_disable();
//关闭系统总中断(底层调用汇编指令)

early_boot_irqs_off();
//设置系统中断的关闭标志(bool全局变量)

early_init_irq_lock_class();
/*

* Interrupts are still disabled. Do necessary setups, then

* enable them 中断依然被禁用。做必要的设置后,
再使能它

*/

lock_kernel();

tick_init();

点击(此处)折叠或打开

初始化内核时钟系统
-->clockevents_register_notifier(&tick_notifier) <kernel/time/tick-common.c>
往内核通知链中注册内核时钟时间的通知函数

参考资料:《Linux 时钟处理机制》 《Linux 时钟管理》

boot_cpu_init();

点击(此处)折叠或打开

激活当前CPU(在内核全局变量中将当前CPU的状态设为激活状态)
参考资料:《激活第一个CPU》

page_address_init(); //高端内存相关,未定义高端内存的话为空函数

printk(KERN_NOTICE "%s", linux_banner);

//打印内核版本信息,也就是平时我们在内核启动时在串口中看到的:Linux version 2.6.30.4mingjie (root@EmbedSky) (gcc version 4.3.3 (Sourcery G Lite 2009q1176) )

#2 Sun Oct 21 08:54:39 CST 2012

setup_arch(&command_line);

点击(此处)折叠或打开

内核构架相关初始化函数,可以说是非常重要的一个初始化步骤。其中包含了处理器相关参数的初始化、内核启动参数(tagged list)的获取和前期处理、内存子系统的早期的初始化(bootmem分配器)。

对于ARM构架来说,这个函数位于:arch/arm/kernel/setup.c
以后会详细分析这个函数。

mm_init_owner(&init_mm, &init_task);

点击(此处)折叠或打开

初始化代表内核本身内存使用的管理结构体init_mm。
ps:每一个任务都有一个mm_struct结构以管理内存空间,init_mm是内核的mm_struct,其中:
设置成员变量* mmap指向自己,意味着内核只有一个内存管理结构;
设置* pgd=swapper_pg_dir,swapper_pg_dir是内核的页目录(在arm体系结构有16k, 所以init_mm定义了整个kernel的内存空间)。

这些内容涉及到内存管理子系统,以后再仔细分析。

setup_command_line(command_line);

点击(此处)折叠或打开

对cmdline进行备份和保存:

/* 为处理的command line备份 (例如eg. 用于 /proc) */
char *saved_command_line;
/* 用于参数处理的command line */
static char *static_command_line;

setup_per_cpu_areas();

setup_nr_cpu_ids();

smp_prepare_boot_cpu(); /* arch-specific boot-cpu hooks */

点击(此处)折叠或打开

针对SMP处理器的内存初始化函数,如果不是SMP系统则都为空函数。 (arm为空)
他们的目的是给每个CPU分配内存,并拷贝.data.percpu段的数据。为系统中的每个CPU的per_cpu变量申请空间并为boot CPU设置一些数据。

在SMP系统中,在引导过程中使用的CPU称为boot CPU

/*

* 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();

build_all_zonelists();

page_alloc_init();

printk(KERN_NOTICE "Kernel command line: %s\n", boot_command_line);

parse_early_param();

parse_args("Booting kernel", static_command_line, __start___param,

__stop___param - __start___param,

&unknown_bootoption);

if (!irqs_disabled()) {

printk(KERN_WARNING "start_kernel(): bug: interrupts were "

"enabled *very* early, fixing it\n");

local_irq_disable();

}

sort_main_extable();

trap_init();

rcu_init();

/* init some links before init_ISA_irqs() */

early_irq_init();

init_IRQ();

pidhash_init();

init_timers();

hrtimers_init();

softirq_init();

timekeeping_init();

time_init();

sched_clock_init();

profile_init();

if (!irqs_disabled())

printk(KERN_CRIT "start_kernel(): bug: interrupts were "

"enabled early\n");

early_boot_irqs_on();

local_irq_enable();

/*

* 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(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) {

printk(KERN_CRIT "initrd overwritten (0xlx < 0xlx) - "

"disabling it.\n",

page_to_pfn(virt_to_page((void *)initrd_start)),

min_low_pfn);

initrd_start = 0;

}

#endif

vmalloc_init();

vfs_caches_init_early();

cpuset_init_early();

page_cgroup_init();

mem_init();

enable_debug_pagealloc();

cpu_hotplug_init();

kmem_cache_init();

kmemtrace_init();

debug_objects_mem_init();

idr_init_cache();

setup_per_cpu_pageset();

numa_policy_init();

if (late_time_init)

late_time_init();

calibrate_delay(); // 计算BogoMIPS值,他是衡量一个CPU性能的标志。

pidmap_init(); //PID分配映射初始化。

pgtable_cache_init();

prio_tree_init();

anon_vma_init(); // 匿名虚拟内存域( anonymous VMA)初始化

#ifdef CONFIG_X86

if (efi_enabled)

efi_enter_virtual_mode();

#endif

thread_info_cache_init();//获取thread_info缓存空间,大部分构架为空函数(包括ARM)

cred_init(); //任务信用系统初始化。详见:Documentation/credentials.txt

fork_init(num_physpages);//进程创建机制初始化。为内核"task_struct"分配空间,计算最大任务数。

proc_caches_init(); //初始化进程创建机制所需的其他数据结构,为其申请空间。

buffer_init();
//缓存系统初始化,创建缓存头空间,并检查其大小限时。

key_init(); //内核密钥管理系统初始化

security_init(); //内核安全框架初始化

vfs_caches_init(num_physpages);//虚拟文件系统(VFS)缓存初始化

radix_tree_init();

signals_init(); //信号管理系统初始化

/* rootfs populating might need page-writeback */

page_writeback_init(); //页写回机制初始化

#ifdef CONFIG_PROC_FS

proc_root_init(); //proc文件系统初始化

#endif

cgroup_init(); //control group正式初始化

cpuset_init();
//CPUSET初始化。 参考资料:《多核心計算環境—NUMA與CPUSET簡介》

taskstats_init_early();
//任务状态早期初始化函数:为结构体获取高速缓存,并初始化互斥机制。
delayacct_init();
//任务延迟初始化

check_bugs(); //检查CPU BUG的函数,通过软件规避BUG

acpi_early_init(); /* before LAPIC and SMP init 在 LAPIC
和 SMP 前初始化 */

ACPI早期初始化函数。 ACPI - Advanced Configuration and Power Interface高级配置及电源接口

ftrace_init();
//功能跟踪调试机制初始化,ftrace 是 function trace 的简称
/* Do the rest non-__init'ed, we're now alive 所剩下的非-__init的初始化, 内核现在已经启了 */

rest_init();
// 虽然从名字上来说是剩余的初始化。但是这个函数中的初始化包含了很多的内容

}

在看完上面的代码之后,你会发现内容很多。但是归纳起来,我认为需要注意的有以下几点:

内核启动参数的获取和处理

setup_arch(&command_line);函数

内存管理的初始化(从bootmem到slab)

rest_init();函数

其他的部分都是对内核各个组件的数据结构申请内存,并初始化。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: