系统初始化
一、Makefile分析
从 src/ 目录下的Makefile可以看出,工程的结构基本与Linux系统保持一致:
在 init/ 目录中的Makefile主要内容如下:
obj-y := main.o version.o init_task.o test.o
在 arch/arm64/kernel/Makefile中可以找到:
head-y := head.o
从Makefile的分析可以看出,系统的初始化是在head.S和main.c这两个文件完成的,接下来我们来看看这两个文件。
二、head.S
head.S是一个汇编文件,内部的执行流程如下:
知道了具体步骤之后,我们再来看看每一步做了什么。
1、fake_start
这一步直接跳转到了real_start,具体意义不明。
ENTRY(fake_start)
b real_start
ENDPROC(fake_start)
2、real_start
在real_start中,首先将bootloader传递给内核的启动参数保存到了boot_params数组(该数组定义在main.c里)中;然后启动了MMU单元,最后跳转到了__prepare_jump_to_master。
ENTRY(real_start)
/**
* 将x0~x3保存到boot_params中
* 这三个参数是boot传递给内核的值
*/
adr_l x9, boot_params
stp x0, x1, [x9]
stp x2, x3, [x9, #16]
/**
* 对boot_params中32个字节(x0~x3)执行inval操作
*/
adr_l x0, boot_params
add x1, x0, #0x20 // 4 x 8 bytes
/**
* 在MMU未打开时,需要先调用此句
* 才能执行inval操作
*/
dmb sy
bl __inval_cache_range
adrp x11, VA_OFFSET
bl __create_temporary_page_tables /* x12=TTBR0, x13=TTBR1 */
/**
* 前面已经准备好页表。
* 准备调用CPU设置代码,打开MMU.
*/
bl __prepare_cpu_mmu
/**
* 打开MMU以后,直接跳转到这里
*/
ldr x14, =__prepare_jump_to_master
b __turn_on_cpu_mmu
ENDPROC(real_start)
3、__prepare_jump_to_master
这一步主要是为运行C语言程序准备环境,初始化堆栈之后直接跳转到了main.c中的start_master函数运行。
__prepare_jump_to_master:
adr_l x6, __bss_start
adr_l x7, __bss_stop
/**
* 清空BSS段的内容
*/
1: cmp x6, x7
b.hs 2f
str xzr, [x6], #8
b 1b
/**
* 准备启动线程的堆栈
* 并跳转到C函数入口处
*/
2:
adr_l sp, initial_sp, x4
str_l x9, device_tree_phys, x5
str_l x11, phys_addr_origin, x6
mov x29, #0
/**
* 是不是有点小激动??
*/
b start_master
ENDPROC(__prepare_jump_to_master)
三、main.c
程序运行到main.c文件,就是由大家熟悉的C语言进行编写了,我们先来看看程序流程:
从流程图来看main.c文件的内容好像非常少,但其实这个文件完成了主要的系统初始化工作。
1、start_master
在函数前端看到了熟悉的标记 __init,猜测这个标记的作用可能与Linux系统中的作用类似。
在函数中我们可以看到各种系统初始化的函数(init_xxx),这里就不展开讨论了,在后面具体模块的代码分析中我们再来讨论。我们可以看到 boot_state 全局变量表示当前系统的初始化状态,在状态变化为 KERN_PREPARE_RUN 之后,就进入到了 kick_rest 函数中。cpu_idle 函数作用类似于一个死循环,防止系统直接运行结束。
/**
* 主核初始化
*/
asmlinkage void __init start_master(void)
{
boot_state = BOOTING;
disable_irq();
/* 为主核设置其活动掩码 */
smp_mark_master();
/* 体系结构特定的初始化过程 */
start_arch();
init_memory_early();
init_vfs_early();
init_sched_early();
init_linear_mapping();
/**
* 初始化内存子系统
* 自此以后,可以调用内存分配API了^_^
*/
init_memory();
boot_state = KERN_MALLOC_READY;
init_pagecache();
init_virt_space();
init_radix_tree();
init_IRQ();
init_time();
init_timer();
init_sched();
init_console();
enable_irq();
boot_state = KERN_PREPARE_RUN;
kick_rest();
cpu_idle();
//不可能运行到这里来
BUG();
}
2、init_in_process
这个函数顾名思义,创建了一个新的进程继续系统初始化的操作。
static void kick_rest(void)
{
create_process(init_in_process,
NULL,
"init_in_process",
5
);
}
3、init_in_process
从调用的函数名可以看出这一步主要是为运行用户的应用程序做准备,同时打开了命令行终端设备,最后将系统的控制权交给用户。
/**
* 在进程上下文进行初始化工作。
* 在开中断的情况下运行,此时可以睡眠。
*/
static __maybe_unused int init_in_process(void *unused)
{
/**
* 初始化工作队列
* 可睡眠的延迟任务
*/
init_sleep_works();
init_vfs();
init_file_systems();
init_bus();
probe_devices();
init_tty();
mount_file_systems();
/**
* 初始化lwip协议栈
*/
init_lwip();
/**
* 启动所有从核
*/
launch_slave();
/**
* 打开console设备
* 将其作为默认的输出设备
*/
if (sys_open("/dev/console", O_RDWR, 0) < 0)
printk("Warning: unable to open an initial console.\n");
/**
* 用于printf
*/
(void) sys_dup(0);
(void) sys_dup(0);
boot_state = KERN_RUNNING;
__init_klibc();
dim_sum_test();
/**
* 将控制权交给用户线程
*/
usrAppInit();
return 0;
}
DIM-SUM系统的学习就从这里开始了,后续我会持续更新各个子系统的分析文章。
(目前DIM-SUM系统版本为HOTPOT,本文内容为鄙人愚见,有不足之处请大家指正,如果有意转载请标明来源:https://blog.csdn.net/wdy8841693/article/details/107937706。在这里感谢谢宝友前辈的付出,另外向大家推荐《自研操作系统:DIM-SUM设计与实现》这本书)