bootinglinux启动过程中的Booting分支精彩深度剖析

1. Booting分支介绍

在Linux系统的启动过程中,有一段称为Booting的处理过程。它负责加载内核、初始化系统、启动初始化程序等一系列关键任务。而在这个过程中,又有一个重要的分支,即Booting分支 。Booting分支是处理内核启动阶段和初始化进程的代码路径,它通过多个子分支来完成不同的任务。

下面将对Booting分支的架构、子分支以及它们的作用进行详细介绍。

2. Booting分支的架构

Booting分支的架构是一个多路分支结构,它包含了多个子分支,每个子分支又包含了多个处理过程。Booting分支主要是由startup_32()函数和各个子分支构成的。

void __init startup_32(void)

{

setup_arch(&command_line);

inquire_remote_irq();

trap_init();

sched_init();

mm_init();

check_bugs();

cpu_init();

init_IRQ();

init_timers();

init_softirq();

time_init();

console_init();

init_post();

printk(KERN_INFO "Kernel panic - not syncing: Attempted to kill init!\n");

for(;;)

idle();

}

3.子分支及其作用

3.1 setup_arch()

setup_arch()函数是Booting分支的第一个子分支,它主要是负责平台的初始化和架构的选择,包括CPU、内存、设备的初始化,堆栈的设置,以及异常和中断的处理等。

其中,最重要的是CPU初始化,这也是整个系统启动的关键。在CPU初始化过程中,会调用平台架构提供的cpu_arch_init()函数和hw_irq_init()函数。前者会根据CPU类型初始化寄存器和调用内存管理和中断控制的初始化函数;后者会根据硬件平台初始化中断控制器,为软中断和定时器中断提供服务,这对后面一些子分支的正常运作非常重要。

static void __init setup_arch(char **cmdline_p)

{

identify_boot_cpu();

early_math_init();

console_early_init();

early_serial_console_init();

BSP_processor_setup(); // 调用硬件平台实现的BSP_processor_setup函数

page_address_init();

debug_objects_mem_init();

debug_objects_setup();

parse_early_param();

early_acpi_boot_init();

setup_memory();

seq_printf_version();

reserve_initrd();

memblock_init();

mem_init();

pgtable_l5_enabled = boot_cpu_has(X86_FEATURE_PTI);

pci_iommu_alloc();

setup_vm_final();

check_bugs(); // 调用check_bugs函数

cachemode_init();

kernel_identify();

security_init();

dmi_scan_machine();

setup_per_cpu_pageset();

usermodehelper_init();

smp_prepare_boot_cpu();

radix_tree_init();

printk(KERN_DEBUG "Zone ranges: \n");

show_mem_ranges(0, -1);

printk(KERN_DEBUG "Kernel command line: %s\n", saved_command_line);

}

3.2 check_bugs()

check_bugs()函数是Booting分支的第二个子分支,它主要是用于检测系统中的CPU缺陷和其他硬件缺陷,并对其进行修复或提示。在这个函数中,会根据CPU类型和硬件平台的不同,对一些已知的缺陷进行修复或屏蔽,以保证系统正常运行。

例如,某些CPU可能存在的问题,比如Intel的F00F问题,AMD的79问题、27问题,以及VIA的9问题等,都会在这个函数中得到检测和修复。

static void __init check_bugs(void)

{

static const char *const x86_bug_flags[] = { // 缺陷列表

"f00f",

"spectre_v1",

"spectre_v2",

"spectre_v2_user",

"spectre_v4",

"mds",

"tsx_async_abort",

"tsx_async_abort_ssb",

"virt-ssbd",

"virt-ssbd-clear-on-vm-entry",

"virt-ssbd-no",

"amd-ssbd",

"amd-ssbd-clear-on-vm-entry",

"amd-ssbd-no",

"amd-uninitialized_vmbr",

"tsx_force_abort",

"mpx",

"eagerfpu",

"amd_e400",

"leaftouch",

};

u32 eflags;

setup_force_cpu_bug(X86_BUG_F00F);

/* Only check CPU bugs if the user allows it */

if (cpu_bug_enabled || (boot_cpu_data.x86_vendor != X86_VENDOR_AMD &&

boot_cpu_data.x86_vendor != X86_VENDOR_HYGON))

check_bugs_cpu();

/* Only check MDS if SSBD is not catching MDS */

if (mds_no=mds_no_cpu())

pr_info("Mitigation: Clear CPU buffers; SMT vulnerable\n");

}

3.3 init_IRQ()

init_IRQ()函数是Booting分支的第三个子分支,它是内核的中断子系统初始化函数。在这个函数中,会对系统中的所有中断进行初始化、注册和分配,建立中断控制器与设备的联系,并设置中断控制器硬件的一些寄存器等。

在系统运行过程中,各种设备和软件都可以向处理器发出中断请求,它们会通过中断控制器按照优先级顺序被递交到CPU执行。因此,中断控制器的正确初始化对整个系统的稳定性和正确性都非常重要。

void init_IRQ(void)

{

int i;

/* Hardware setup... through the hardware specific asm code */

init_ISA_irqs();

/* Search the table looking for paravirtualized or fake IRQs */

for (i = 0; i < NR_IRQ_VECTORS; i++)

if (!vector_is_real(i))

arch_local_irq_disable();

user_defined_bustype_init();

if (!handle_irq)

panic("%s: No irq handler for vector\n", __func__);

/* Set up the timer interrupt */

if (arch_initcall_has_run)

bind_cpu(CPU_MASK_ALL);

timer_interrupt_init();

arch_initcall(call_do_IRQ); // 调用call_do_IRQ函数

}

3.4 init_timers()

init_timers()函数是Booting分支的第四个子分支,它主要负责系统的时间管理。在这个函数中,会对定时器进行初始化,包括硬件时钟的设置、时钟中断的分配和注册、时钟分辨率的调整等。

系统时间的准确性和稳定性对于一些关键应用程序非常重要,包括网络通信、日志记录、文件时间戳等。因此,在系统中对时间管理的优化和改进是非常必要的。

void __init init_timers(void)

{

#ifdef CONFIG_HZ_PERIODIC

early_boot_irqs_disabled = 1;

#endif

tick_check_broadcast_init(); // 调用tick_check_broadcast_init函数

init_hrtimers(); // 调用init_hrtimers函数

tick_setup_sched_timer(); // 调用tick_setup_sched_timer函数

clocksource_done_booting(); // 调用clocksource_done_booting函数

}

3.5 console_init()

console_init()函数是Booting分支的第五个子分支,它主要是初始化系统控制台,为系统的输出和输入提供支持。在这个函数中,会初始化与系统控制台相关的一些重要数据结构和变量,比如虚拟终端、键鼠输入、串口和显示器等。

系统控制台可以使用户能够与系统进行交互和调试。一些命令行界面的工具,比如Boot Loader、Shell,都需要在控制台上进行输入和输出。因此,控制台初始化是整个系统Booting分支中最重要的一部分。

int __init console_init(void)

{

if (early_boot_irqs_disabled)

local_irq_enable();

#ifndef MODULE

con_init();

#endif

if (!uart_console()) {

if (!vc_cons_allocated())

goto no_console;

vc_init();

}

prandom_seed(state, sizeof(state));

if (register_console(&dummy_console))

panic("Couldn't register console");

return 0;

no_console:

pr_emerg("No consoles found, booting without console\n");

return 1;

}

3.6 init_post()

init_post()函数是Booting分支的最后一个子分支,它主要是对最初的系统进程——init进程进行初始化,并将其放入进程调度队列中。在这个函数中,还会进行一些内存回收、系统优化和异常处理等。

init进程是整个Linux系统中最重要的进程之一,它是由内核启动后自动创建的第一个用户空间进程,并负责系统进程的初始化和启动。因此,它的正确初始化和运行非常重要。

void __init init_post(void)

{

if (x86_cpu_init.acpi.probe_roms)

drm_pci_init();

device_init();

process_init(); // 进程初始化

init_IRQ();

/* Compute various values based on the CPU topology */

topology_init();

rcu_init();

notifier_init();

prandom_seed_state(state, sizeof(state));

perf_event_init();

task_rq_lock_wait_init();

uv_nmi_init();

init_fair_sched();

init_cgroup_subsys();

softirq_init();

timekeeping_init();

profile_init();

atomics_init();

printk_safe_init();

panic_later_init();

pid_max = PID_MAX_DEFAULT;

init_workqueues();

init_idle_bootup_task(current);

kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND |

CLONE_KERNEL | SIGCHLD); // 创建内核线程

}

总结

通过以上对Linux系统启动过程中Booting分支的介绍,我们可以了解到在Linux系统初始化过程中,Booting分支的重要性。Booting分支通过多个子分支,从硬件系统平台、中断处理、时钟管理、控制台输出、进程创建等多个方面进行了细致的系统配置和初始化,为后续用户任务的运行提供了非常坚实的基础。

当然,这里仅是对Booting分支的粗略浅析,还有许多细节问题需要进一步深入研究和讨论。

操作系统标签