内核启动流程-初探

目录

1. Question

  1. 内核启动入口在哪,怎么从代码中找到入口?
  2. 启动内核前,要有哪些准备工作?
  3. 进程调度是何时开始的?
  4. 多核启动是何时完成的?

2. 程序入口

基于linux-5.15 ARM64分析。

2.1. _text

从链接脚本arch/arm64/kernel/vmlinux.lds可以查到,程序的入口为_text,镜像起始位置存放的是.head.text段生成的指令码。搜索.head.text,可以找到include/linux/init.h__HEAD定义.section ".head.text","ax"

ENTRY(_text)

SECTIONS
{
 . = ((((((-(((1)) << ((((48))) - 1)))) + (0x08000000))) + (0x08000000)));
 .head.text : {
  _text = .;
  KEEP(*(.head.text))
 }
/* For assembly routines */
#define __HEAD      .section    ".head.text","ax"
#define __INIT      .section    ".init.text","ax"
#define __FINIT     .previous

再看一下arch/arm64/kernel/vmlinux.lds是怎么生成的,编译日志中,会有LDS arch/arm64/kernel/vmlinux.ldsscripts/Makefile.build中可以看到,也就是通过对arch/arm64/kernel/vmlinux.lds.S进行预处理得到了最终的链接脚本。

# Linker scripts preprocessor (.lds.S -> .lds)
# ---------------------------------------------------------------------------
quiet_cmd_cpp_lds_S = LDS     $@
      cmd_cpp_lds_S = $(CPP) $(cpp_flags) -P -U$(ARCH) \
                             -D__ASSEMBLY__ -DLINKER_SCRIPT -o $@ $<

$(obj)/%.lds: $(src)/%.lds.S FORCE
        $(call if_changed_dep,cpp_lds_S)

2.2. head.S

再搜索__HEAD,可以看到程序起始代码位于arch/arm64/kernel/head.S。通过代码梳理出内核启动基本流程。

__HEAD
/*
 * DO NOT MODIFY. Image header expected by Linux boot-loaders.
 */
efi_signature_nop           // special NOP to identity as PE/COFF executable
b   primary_entry           // branch to kernel start, magic
.quad   0                   // Image load offset from start of RAM, little-endian
le64sym _kernel_size_le         // Effective size of kernel image, little-endian
le64sym _kernel_flags_le        // Informative flags, little-endian
.quad   0               // reserved
.quad   0               // reserved
.quad   0               // reserved
.ascii  ARM64_IMAGE_MAGIC       // Magic number
.long   .Lpe_header_offset      // Offset to the PE header.

3. 内核启动基本流程

3.1. head.S

  1. 建立页表
  2. 开启MMU
  3. 跳转到start_kernel

内核启动流程-初探

3.2. start_kernel

start_kernel执行setup_arch和一些子系统的初始化,最后通过rest_init创建kernel_init和kthreadd两个线程,分别为1号和2号线程。kernel_init的工作主要通过kernel_init_freeable完成

  1. 执行early_initcall指定的函数
  2. 拉起从CPU,并启动多核调度
  3. 通过do_initcalls执行built-in到内核的各个initcall
  4. 挂载根文件系统
  5. 执行根文件系统中的init程序,kernel_init演变为init进程,内核也随之切换到用户态

内核启动流程-初探

上一篇:【EZCONNECT】 sqlnet.ora文件中EZCONNECT参数与Easy Connect Naming Method方法


下一篇:U-Boot _main函数