linux内核
课程报名
linux内核启动流程全景图
课程介绍
相关文章
相关视频
问答
开机
购买课程并登录之后,才可查看完整版数据
GitHub登录
uefi启动流程
systemd-boot启动流程
linux内核启动流程
课外阅读
linux内核构建流程
Intel® 64 and IA-32 Architectures Software Developer Manuals // intel官方文档
AMD64 Architecture Programmer’s Manual // amd官方文档,它和上面的intel官方文档,都是内核开发必看的文档
drivers/firmware/efi/libstub/x86-stub.c:
efi_status_t __efiapi efi_pe_entry(efi_handle_t handle, efi_system_table_t *sys_table_arg) // linux内核作为uefi应用的入口函数
efi_stub_entry(handle, sys_table_arg, NULL);
drivers/firmware/efi/libstub/x86-stub.c:
void __noreturn efi_stub_entry(efi_handle_t handle, efi_system_table_t *sys_table_arg, struct boot_params *boot_params) // inux内核作为uefi应用,有好几种启动方式,但这几种启动方式最终都会调用这个函数
const struct linux_efi_initrd *initrd = NULL;
unsigned long kernel_entry;
efi_system_table = sys_table_arg; // 把 sys_table_arg 保存到全局变量 efi_system_table,uefi 的所有功能,基本都是通过 system table 提供的
if (!IS_ENABLED(CONFIG_EFI_HANDOVER_PROTOCOL) || !boot_params)
efi_allocate_bootparams(handle, &boot_params);
drivers/firmware/efi/libstub/x86-stub.c:
static efi_status_t efi_allocate_bootparams(efi_handle_t handle, struct boot_params **bp) // 分配一个boot_params结构体,该结构体用来存放内核启动所需的各种参数,它会沿着启动流程代码,一路传递下去
efi_guid_t proto = LOADED_IMAGE_PROTOCOL_GUID; // 一个uefi的协议id,用于获取内核作为uefi应用的加载信息
unsigned long alloc;
efi_bs_call(handle_protocol, handle, &proto, (void **)&image); // 获取内核作为uefi应用的加载信息,这些信息被保存到全局变量image
efi_allocate_pages(PARAM_SIZE, &alloc, ULONG_MAX); // 分配一个 struct boot_params 实例,把这块内存的起始地址赋值到 alloc
struct boot_params *boot_params = memset((void *)alloc, 0x0, PARAM_SIZE); // 把 boot_params 内存清零
struct setup_header *hdr = &boot_params->hdr;
hdr->type_of_loader = 0x21;
char *cmdline_ptr = efi_convert_cmdline(image); // 获取 systemd-boot 传递过来的内核参数,也就是 image->load_options 字段
efi_set_u64_split((unsigned long)cmdline_ptr, &hdr->cmd_line_ptr, &boot_params->ext_cmd_line_ptr); // 将这些内核参数赋值到boot_params结构体的对应字段上,供后续使用
*bp = boot_params;
return EFI_SUCCESS;
struct setup_header *hdr = &boot_params->hdr;
efi_setup_5level_paging(); // 设置5级页表
parse_options(CONFIG_CMDLINE); // 解析编译到内核里的命令行参数
unsigned long cmdline_paddr = ((u64)hdr->cmd_line_ptr | ((u64)boot_params->ext_cmd_line_ptr << 32));
parse_options((char *)cmdline_paddr); // 解析systemd-boot传递过来的内核参数
efi_decompress_kernel(&kernel_entry, boot_params); // 将真正的内核解压到内存,并获取其入口函数地址,bzImage是压缩过后的内核,vmlinux才是真正的内核
efi_load_initrd(image, hdr->initrd_addr_max, ULONG_MAX, &initrd); // 加载initrd
drivers/firmware/efi/libstub/efi-stub-helper.c:
efi_status_t efi_load_initrd(efi_loaded_image_t *image, unsigned long soft_limit, unsigned long hard_limit, const struct linux_efi_initrd **out)
efi_guid_t tbl_guid = LINUX_EFI_INITRD_MEDIA_GUID;
struct linux_efi_initrd initrd, *tbl;
efi_status_t status = efi_load_initrd_dev_path(&initrd, hard_limit); // 通过uefi空间的一个固定设备路径加载initrd,因为systemd-boot就是通过这种方式传递initrd的,所以这一步应该是成功的
if (status == EFI_NOT_FOUND)
status = efi_load_initrd_cmdline(image, &initrd, soft_limit, hard_limit); // 通过命令行参数加载initrd,该命令行参数包括systemd-boot传递过来的,以及内置的,因为上一步已经加载到initrd了,这一步就不会执行了
if (status == EFI_UNSUPPORTED || status == EFI_NOT_READY)
return EFI_SUCCESS;
if (status != EFI_SUCCESS)
return status;
efi_bs_call(allocate_pool, EFI_LOADER_DATA, sizeof(initrd), (void **)&tbl); // 分配一个initrd实例,地址赋值到tbl
*tbl = initrd; // 把上面加载的initrd,赋值到tbl
efi_bs_call(install_configuration_table, &tbl_guid, tbl); // 将加载的initrd的内存地址存放到uefi的配置表里,这样以后也可以通过uefi的配置表,来获取initrd
if (out)
*out = tbl;
return EFI_SUCCESS;
if (initrd && initrd->size > 0) // 如果加载到了initrd,则将它的内存起始地址及大小,赋值到boot_params结构体的对应字段上,供后续使用
efi_set_u64_split(initrd->base, &hdr->ramdisk_image, &boot_params->ext_ramdisk_image);
efi_set_u64_split(initrd->size, &hdr->ramdisk_size, &boot_params->ext_ramdisk_size);
exit_boot(boot_params, handle); // 获取当前所有内存的使用情况,赋值到boot_params里,然后调用EFI_BOOT_SERVICES.ExitBootServices(),终止uefi的boot services,此行为也表示linux内核正式从uefi手里接管整个系统
efi_5level_switch(); // 启用5级页表
enter_kernel(kernel_entry, boot_params); // 跳转到真正的内核代码,并把boot_params传入其中
arch/x86/kernel/head_64.S:
startup_64 // 真正内核代码的入口函数
arch/x86/kernel/head64.c:
__startup_64 // 初始化early_top_pgt页表,映射内核到对应的虚拟地址空间
切换到新的early_top_pgt页表
切换到内核虚拟地址来运行内核代码
xxxxxxxxxxxxxxxxxxxxxxxx:
xxxxxxxxxxxxxxxxxxx // xxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxx:
xxxxxxxxxxxxxxxxxxxxxxx // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxx:
xxxxxxxxxxxxx // xxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxx:
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxx
xxxxxxxxxxx:
xxxxxxxxxxxxxxxxxxxxxxx // xxxxxxxx
xxxxxxxxxxxxxxxxxxxx // xxxxxxxxxxx
xxxxxxxxxxxx:
xxxxxxxxxxxxx // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxx:
xxxxxxxxxxxxxxxxx // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxx:
xxxxxxxxxxxxxxxxxx // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx:
xxxxxxxxxxxxxxxxxxx // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxx:
xxxxxxxxxxxxxxxxxxxxx // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxx:
xxxxxxxxxx // xxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxx:
xxxxxxxxxxxxx // xxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxx // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxx:
xxxxxxxxxxxxxxxxx // xxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxx:
xxxxxxxxx // xxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxx:
xxxxxxxxxxxxxxxxxxxxx // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxx:
xxxxxxxxxxxxxxxxxxxxxxxxxxx // xxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxx:
xxxxxxxxxxxxxxx // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxx:
xxxxxxxx // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxx:
xxxxxxxxxxxx // xxxxxxxx
xxxxxxxxxxxxxxx:
xxxxxxxxxxxxxxxxxxx // xxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxx:
xxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxx:
xxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxx:
xxxxxxxxxxxxxxx // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxx:
xxxxxxxxxxxxxxxxxxx // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxx:
xxxxxxxxxxxxxxxxxxxxxx // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxx:
xxxxxxxx
xxxxxxxxxxxxx:
xxxxxxxxxxxxxxxxx // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxx:
xxxxxxxxxxxxxxx
xxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxx // xxxxxxxxxx
xxxxxxxxxxxxxx
xxxxxxxxxxxx