Linux系统移植:Kernel 启动流程

Linux系统移植:Kernel 启动流程

linux 内核代码启动首先看他的链接脚本 vmlinux.lds 文件,文件如下:

20220321193507

指明了一个函数实体入口:stext 函数,位置 arch/arm/kernel/head.S 中

函数入口上有段注释

20220321193847

规定了执行函数前要关闭 MMU、关闭 D-cache、r0=0、r1=machine nr(也就是机器 ID)、r2=atags 或者设备树(dtb)首地址(r1、r2是函数传入的参数)

函数先进行一些判断和转换,确定模式在 BE8 以及使用 Thumb 指令

20220321194431

然后借助 safe_svcmode_maskall 宏, 屏蔽 IRQ、FIQ中断,切换到 SVC 模式;将 r9 中保存的原来的 CPSR 的值保存到 SPSR 中

safe_svcmode_maskall r9

然后读取处理器 ID,ID 值保存在 r9 寄存器

20220321194836

调用函数__lookup_processor_type 检查当前系统是否支持此 CPU,如果支持的就获取 procinfo 信息到 r5

20220321194950

Linux 内核将每种处理器都抽象为一个 proc_info_list 结构体,每种处理器都对应一个procinfo,因此通过处理器 ID 来找到对应的 procinfo 结构

后面几个宏定义,根据定义执行不同功能,跳过这些不看

121 行,调用函数__vet_atags 验证 atags 或设备树(dtb)的合法性

20220321195149

然后调用 __create_page_tables 创建页表,将函数 __mmap_switched 的地址保存到 r13 寄存器中

20220321203709

最后调用 __enable_mmu 函 数使能 MMU,__enable_mmu 通过调用 __turn_mmu_on 来打开 MMU,然后__turn_mmu_on 在最后会执行 r13 里面保存的__mmap_switched 函数,__mmap_switched 最终调用 start_kernel 来启动 Linux 内核

start_kernel 函数定义在文件 init/main.c 中,该函数通过调用众多的子函数来完成 Linux 启动之前的一些初始化工作,这里子函数非常多,不具体展开了,这些函数会设置栈大小,初始化堆栈,配置一些架构相关代码,完成一些内核的初始化,一切准备完成后调用 rest_init 函数

rest_init 函数定义在文件 init/main.c 中

20220321204748

先调用函数 rcu_scheduler_starting,启动 RCU 锁调度器

然后调用函数 kernel_thread 创建 kernel_init 进程,init 进程一开始是内核进程(也就是运行在内核态),后面 init 进程会在根文件系统中查找名为“init”这个程序,这个“init”程序处于用户态,通过运行这个“init”程 序,init 进程就会实现从内核态到用户态的转变

之后调用函数 kernel_thread 创建 kthreadd 内核进程,此内核进程的 PID 为 2,kthreadd 进程负责所有内核进程的调度和管理

最后调用函数 cpu_startup_entry 来进入 idle 进程,cpu_startup_entry 会调用 cpu_idle_loop,cpu_idle_loop 是个 while 循环,也就是 idle 进程代码,idle 进程类似于 RTOS 里面的空闲任务,idle 进程并没有使用函数进行创建,而是由主进程演变而来的

在 Linux中断下输入 ps -A 可以看到 init 进程和 ktthreadd 进程

20220321205241

systemd 是 init 进程

init 进程用很重要,该进程内初始化一些内容,根据 ramdisk_execute_command 和 execute_command 的参数,在根文件系统中找到一个可运行的 init 程序,如果都为空,那么就执行 “/sbin/init”、“/etc/init”、“/bin/init”和“/bin/sh”这四个备用 init 程序,如果这四个也没有执行,那完蛋,系统启动失败,除此之外 init 进程内也会调用 do_basic_setup 函数用于完成 Linux 下设备驱动初始化工作,完成 Linux 下驱动模型子系统的初始化,同时打开设备“/dev/console”,作为系统的标准输入和输出,实现命令行交互,以及调用函数 prepare_namespace 来挂载根文件系统,所以 do_basic_setup 这个函数对 init 进程来说很重要!

以上就是 linux 内核启动的一个大致过程

猜你喜欢

转载自blog.csdn.net/qq_45396672/article/details/123646158