ARM Linux启动流程-start_kernel阶段

来源:互联网 发布:windows 照片管理软件 编辑:程序博客网 时间:2024/06/04 23:00

start_kernel的主要功能

  start_kernel()函数是内核初始化C语言部分的主体。这个函数完成系统底层基本机制,包括处理器、存储管理系统、进程管理系统、中断机制、定时机制等的初始化工作。
  分析所在文件:init/main.c
  内核版本:linux-3.14.28

start_kernel流程介绍

1、void lockdep_init(void)
有些体系结构有自己的start_kernel也会调用lockdep_init,这里只会调用一次,来初始化hash表。这个hash表是个全局的锁链表,lock dependency哈希表。

2、void __init __weak smp_setup_processor_id(void)
指定当前cpu的逻辑号(判断是否是smp系统,如果是则从arm协处理器读取当前cpuid,否则为0),这个函数对应于对称多处理器的设置,当系统中只有一个cpu的情况,此函数为空,什么也不做。

3、void page_address_init(void)
当定义了CONFIG_HIGHMEM 宏,并且没有定义 WANT_PAGE_VIRTUAL 宏时,非空函数。其他情况为空函数。ARM9不支持高端地址(大于896M),一般的嵌入式产品也不会用高端地址,所以,在ARM体系结构下,此函数为空!

4、void __init setup_arch(char **cmdline_p)
这个setup_arch()函数是start_kernel阶段最重要的一个函数,每个体系都有自己的setup_arch()函数,是体系结构相关的,具体编译哪个体系的setup_arch()函数,由顶层Makefile中的ARCH变量决定。该函数的主要功能有,分析uboot传进来的参数(tags)、初始化内存结构、创建页表、开启mmu等。

5、static void __init setup_command_line(char *command_line)
保存未改变的comand_line到字符数组static_command_line[]中。保存 boot_command_line到字符数组saved_command_line[]中。

6、void __init setup_per_cpu_areas(void)
如果没有定义CONFIG_SMP宏,则这个函数为空函数。如果定义了CONFIG_SMP宏,则这个setup_per_cpu_areas()函数给每个CPU分配内存,并拷贝.data.percpu段的数据。为系统中的每个CPU的per_cpu变量申请空间。

7、void sched_init(void)
核心进程调度器初始化,调度器的初始化的优先级要高于任何中断的建立,并且初始化进程0,即idle进程,但是并没有设置idle进程的NEED_RESCHED标志,所以还会继续完成内核初始化剩下的事情。

8、void init_IRQ(void)
初始化IRQ中断和终端描述符。初始化系统中支持的最大可能的中断描述结构struct irqdesc变量数组irq_desc[NR_IRQS],把每个结构变量irq_desc[n]都初始化为预先定义好的坏中断描述结构变量bad_irq_desc,并初始化该中断的链表表头成员结构变量pend。

9、void __init vfs_caches_init(unsigned long mempages)
初始化VFS,创建一个rootfs,这是个虚拟的rootfs,即内存文件系统,后面还会指向真实的文件系统。

10、static noinline void __init_refok rest_init(void)
完成了基本的初始化工作后,最后调用了rest_init函数。这个函数创建了一个入口点是kernel_init()函数的内核线程,然后调用cpu_idle()函数进入空闲状态。新创建的内核线程是系统的1号任务(pid = 1),放入了调度队列中,而原先的初始化代码是系统的0号任务,是不在调度队列中的。

__define_initcall介绍

#define arch_initcall(fn)   __define_initcall("3",fn,3)#define __define_initcall(fn, id) \    static initcall_t __initcall_##fn##id __used \    __attribute__((__section__(".initcall" #id ".init"))) = fn

其中initcall_t 是个函数指针类型:typedef int (*initcall_t)(void);
而属性 attribute((section())) 则表示把对象放在一个这个由括号中的名称所指代的section中。

所以这个宏定义的含义是:
声明一个名称为_initcall##fn##id的函数指针(其中##表示替换连接);并将这个函数指针初始化(或赋值)为fn;
编译的时候需要把这个函数指针变量放置到名称为 “.initcall” level “.init”的section中(比如level=”1”,代表这个section的名称是 “.initcall1.init”)。level 值越小表示优先及更高。kernel中 level的范围是(0~7)。例子:

arch_initcall(customize_machine);等价于static initcall_t __initcall_customize_machine3 __used \    __attribute__((__section__(".initcall" 3 ".init"))) = customize_machine

通过顶层Makefile中的规则生成vmlinux是根据arch/arm/kernel/vmlinux.lds这个脚本链接生成的。arch/arm/kernel/vmlinux.lds是由arch/arm/kernel/vmlinux.lds.S生成的,其生成规则在scripts/Makefile.build里有定义。在arch/arm/kernel/vmlinux.lds里有定义如下段:

 __initcall_start = .;   *(.initcallearly.init) __early_initcall_end = .; *(.initcall0.init) *(.initcall0s.init) *(.initcall1.init) *(.initcall1s.init) *(.initcall2.init) *(.initcall2s.init) *(.initcall3.init) *(.initcall3s.init) *(.initcall4.init) *(.initcall4s.init) *(.initcall5.init) *(.initcall5s.init) *(.initcallrootfs.init) *(.initcall6.init) *(.initcall6s.init) *(.initcall7.init) *(.initcall7s.init)  __initcall_end = .;

do_initcalls函数里依次调用__initcall段里的各个初始化函数:start_kernel->rest_init->kernel_thread(kernel_init, …)->do_basic_setup->do_initcalls

0 0
原创粉丝点击