【嵌入式Linux】内核启动分析与源码跟踪

CodeWarrior ARMv7嵌入式Linux调试:共享库与内核源码级调试实战 在嵌入式Linux开发中,调试是定位复杂问题的关键环节。动态链接库(共享库)和操作系统内核的调试尤为困难,因为它们涉及运行时加载和系统核心机制。理解ELF文件格式、符号表、地址空间映射等底层原理,是进行有效调试的基础。通过源码级调试工具,开发者可以深入理解程序执行流程、内存状态和系统调用,这对于提升软件质量、优化性能和解决稳定性问题具有重要价值。在ARMv7架构的嵌入式系统中,这类技术广泛应用于驱动开发、系统移植和性能调优等场景。本文以CodeWarrior for ARMv7为例,详细解析了共享库调试中环 阅读详情

内核启动分析

  1. BIOS/UEFI阶段:计算机开机后首先会执行BIOS或UEFI程序,进行一些硬件初始化操作,如检查硬件配置信息、启动自检程序、加载操作系统引导程序等。
  2. Bootloader阶段:BIOS/UEFI会加载引导程序,比如GRUB等,这个引导程序会在屏幕上显示一个菜单,供用户选择要启动的操作系统,如果只有一个操作系统,那么该引导程序将自动启动内核。引导程序会根据用户选择或默认设置找到内核映像文件,加载到内存中。
  3. 内核启动自解压阶段:内核启动时,它首先会解压缩自身,然后进行一系列的初始化工作,如初始化CPU、内存管理、设备管理、文件系统等。其中,内存管理是最重要的一步,因为内核需要将系统中的所有可用内存映射到自己的地址空间中。
  4. 内核引导阶段:
  5. init进程启动阶段:当内核初始化完毕后,会启动init进程。在Linux系统中,init进程是用户空间中的第一个进程,它负责初始化系统环境,包括加载配置文件、启动系统服务等。
  6. 过渡到rootfs

跟踪线索

arch/arm64/kernel/head.S 入口

init/main.c start_kernel

kernel中敲make后会生成System.map,相关的函数名和地址都放里面了

通过地址追踪到函数所在的文件,使用交叉编译工具链的某个工具

//arch/arm64/kernel/vmlinux.lds.S 连接脚本:指定程序在代码段数据段中如何分配

通过它知道arm64的第一条就是_text

//第一条指令位置
	
{//System.map 函数 列表
ffffff8008080000 T _text  //第一条指令
ffffff8008081800 T vectors  //异常向量表
ffffff80096007f4 T start_kernel	 //内核初始化
}
//通过地址追踪到函数所在的文件,使用交叉编译工具链的addr2line工具
//vmlinux是所有调试信息还在的内核镜像,也在kernel-4.9目录下
$ aarch64-linux-gnu-addr2line ffffff8008080000 -e vmlinux -f /*查找地址对应的文件位置
 /home/yhai/kernel-4.9/arch/arm64/kernel/head.S:83     //这个文件的83行

$ aarch64-linux-gnu-addr2line ffffff8008081800 -e vmlinux -f
vectors
/home/yhai/kernel-4.9/arch/arm64/kernel/entry.S:393
 
 $aarch64-linux-gnu-addr2line ffffff80096007f4 -e vmlinux -f
  start_kernel
  /home/yhai/kernel-4.9/init/main.c:482

 */
 
{//arch/arm64/kernel/vmlinux.lds.S  连接脚本:指定程序在代码段数据段中如何分配
 21 OUTPUT_ARCH(aarch64)
 22 ENTRY(_text)  //入口地址
110 SECTIONS
111 {
130     .head.text : {
131         _text = .;    //代码段连接的时,第一条指令的位置
132         HEAD_TEXT
133     }
134     .text : {           /* Real text segment        */
135         _stext = .;     /* Text and read-only data  */
136             __exception_text_start = .;
137             *(.exception.text)
138             __exception_text_end = .;
139             IRQENTRY_TEXT
140             SOFTIRQENTRY_TEXT
141             ENTRY_TEXT
        }
    }
}

入口(汇编部分)

//arch/arm64/kernel/head.S  入口
    __HEAD
_head:
	    //对应前面 ffffff8008080000 T _text
83    b   stext               // branch to kernel start, magic
ENTRY(stext)
    bl  preserve_boot_args  //保存u-boot传入的启动参数
    bl  el2_setup           //异常级别(权限)的设置:建议EL2级别,如不则下降到EL1 
                            //Drop to EL1, w0=cpu_boot_mode  
    bl  set_cpu_boot_mode_flag  // 按 ctrl+]  会跳到定义处,   按ctrl+o 会返回原位置
    bl  __create_page_tables //创建内存映射表
    bl  __cpu_setup         //初始化CPU(配置访问权限,内存地址划分)
                            //查找源码: vscode -> arch/arm64 -> 右键 在文件夹中查找 __cpu_setup -> mm/proc.S
    b   __primary_switch    //主切换
ENDPROC(stext)
1.保存传入的启动参数
/*
 * Preserve the arguments passed by the bootloader in x0 .. x3
 */
preserve_boot_args:
	mov	x21, x0		// x21=FDT   (X0里存放的是u-boot传入的设备树dtb的首地址)

	adr_l	x0, boot_args			// record the contents of
	stp	x21, x1, [x0]			// x0 .. x3 at kernel entry
	stp	x2, x3, [x0, #16]

	dmb	sy				// needed before dc ivac with
					    	// MMU off

	add	x1, x0, #0x20			// 4 x 8 bytes
	b	__inval_cache_range		// tail call
ENDPROC(preserve_boot_args)
2.异常级别(特权)设置
//el2级别下的初始设置(el2_setup):  是el2,则进行虚拟化设置,处理完后用w0保存cpu的异常级别(el1或推荐的el2)
ENTRY(el2_setup)
	msr	SPsel, #1			// We want to use SP_EL{1,2}
	mrs	x0, CurrentEL  //读取当前异常级别
	cmp	x0, #CurrentEL_EL2  //判断是EL2否
	b.ne	1f   //不是则跳到1f标号处
	mrs	x0, sctlr_el2

	msr	sctlr_el2, x0
	b	2f

1:	mrs	x0, sctlr_el1

	msr	sctlr_el1, x0
	mov	w0, #BOOT_CPU_MODE_EL1		// This cpu booted in EL1
	isb
	ret

2:
#ifdef CONFIG_ARM64_VHE
	/*
	 * Check for VHE being present. For the rest of the EL2 setup,
	 * x2 being non-zero indicates that we do have VHE, and that the
	 * kernel is intended to run at EL2.
	 */
	mrs	x2, id_aa64mmfr1_el1
	ubfx	x2, x2, #8, #4
#else
	mov	x2, xzr
#endif

	/* Hyp configuration.  虚拟化的配置*/
	mov_q	x0, HCR_HOST_NVHE_FLAGS
	cbz	x2, set_hcr
	mov_q	x0, HCR_HOST_VHE_FLAGS
set_hcr:
	msr	hcr_el2, x0
	isb

	/* Generic timers.  通用定时器设置*/
	mrs	x0, cnthctl_el2
	orr	x0, x0, #3			// Enable EL1 physical timers
	msr	cnthctl_el2, x0
	msr	cntvoff_el2, xzr		// Clear virtual offset

#ifdef CONFIG_ARM_GIC_V3
	/* GICv3 system register access  中断系统系统寄存器访问*/
	mrs	x0, id_aa64pfr0_el1
	ubfx	x0, x0, #24, #4
	cbz	x0, 3f

	mrs_s	x0, ICC_SRE_EL2
	orr	x0, x0, #ICC_SRE_EL2_SRE	// Set ICC_SRE_EL2.SRE==1
	orr	x0, x0, #ICC_SRE_EL2_ENABLE	// Set ICC_SRE_EL2.Enable==1
	msr_s	ICC_SRE_EL2, x0
	isb					// Make sure SRE is now set
	mrs_s	x0, ICC_SRE_EL2			// Read SRE back,
	tbz	x0, #0, 3f			// and check that it sticks
	msr_s	ICH_HCR_EL2, xzr		// Reset ICC_HCR_EL2 to defaults

3:
#endif

	/* Populate ID registers. */
	mrs	x0, midr_el1
	mrs	x1, mpidr_el1
	msr	vpidr_el2, x0
	msr	vmpidr_el2, x1

	/*
	 * When VHE is not in use, early init of EL2 and EL1 needs to be
	 * done here.
	 * When VHE _is_ in use, EL1 will not be used in the host and
	 * requires no configuration, and all non-hyp-specific EL2 setup
	 * will be done via the _EL1 system register aliases in __cpu_setup.
	 */
	cbnz	x2, 1f

	/* sctlr_el1 */
	mov	x0, #0x0800			// Set/clear RES{1,0} bits
	msr	sctlr_el1, x0

	/* Coprocessor traps. */
	mov	x0, #0x33ff
	msr	cptr_el2, x0			// Disable copro. traps to EL2
1:


	/* EL2 debug */
	mrs	x0, id_aa64dfr0_el1		// Check ID_AA64DFR0_EL1 PMUVer
	sbfx	x0, x0, #8, #4
	cmp	x0, #1
	b.lt	4f				// Skip if no PMU present
	mrs	x0, pmcr_el0			// Disable debug access traps
	ubfx	x0, x0, #11, #5			// to EL2 and allow access to
4:
	csel	x0, xzr, x0, lt			// all PMU counters from EL1
	msr	mdcr_el2, x0			// (if they exist)

	/* Stage-2 translation */
	msr	vttbr_el2, xzr

	cbz	x2, install_el2_stub

	mov	w0, #BOOT_CPU_MODE_EL2		// This CPU booted in EL2
	isb
	ret

install_el2_stub:
	/* Hypervisor stub */
	adrp	x0, __hyp_stub_vectors
	add	x0, x0, #:lo12:__hyp_stub_vectors
	msr	vbar_el2, x0       //设置异常向量表 基地址

	/* spsr */
	mov	x0, #(PSR_F_BIT | PSR_I_BIT | PSR_A_BIT | PSR_D_BIT |\
		      PSR_MODE_EL1h)
	msr	spsr_el2, x0
	msr	elr_el2, lr
	mov	w0, #BOOT_CPU_MODE_EL2		// This CPU booted in EL2
	eret
ENDPROC(el2_setup)
3.设置CPU启动时的异常级别标志
//__boot_cpu_mode(全局变量,存储从前面的w0中保存的异常级别)
/*
 * Sets the __boot_cpu_mode flag depending on the CPU boot mode passed
 * in x20. See arch/arm64/include/asm/virt.h for more info.
 */
set_cpu_boot_mode_flag:
    adr_l   x1, __boot_cpu_mode
    cmp w0, #BOOT_CPU_MODE_EL2
    b.ne    1f
    add x1, x1, #4
1:  str w0, [x1]            // This CPU has booted in EL1
    dmb sy
    dc  ivac, x1            // Invalidate potentially stale cache line
    ret
ENDPROC(set_cpu_boot_mode_flag)
4.CPU初始化
//arch/arm64/mm/proc.S  cpu初始化(__cpu_setup): 配置访问权限,内存地址划分
ENTRY(__cpu_setup)
	tlbi	vmalle1				// Invalidate local TLB
	dsb	nsh

	mov	x0, #3 << 20
	msr	cpacr_el1, x0			// Enable FP/ASIMD
	mov	x0, #1 << 12			// Reset mdscr_el1 and disable
	msr	mdscr_el1, x0			// access to the DCC from EL0
	msr	tcr_el1, x10
	ret					// return to head.S
ENDPROC(__cpu_setup)
5.主切换
//主切换(primary_switch): 使能MMU, 准备0号进程和内核栈,设置异常向量 跳转到start_kernel
__primary_switch:
#ifdef CONFIG_RANDOMIZE_BASE
    mov x19, x0             // preserve new SCTLR_EL1 value
    mrs x20, sctlr_el1          // preserve old SCTLR_EL1 value
#endif

    bl  __enable_mmu   /*使能MMU(内存管理单元) -> 实现虚拟地址到实际物理地址的映射
     本质: 查表映射   
     用途:不连续变为连续,扩大地址范围
        
      */
#ifdef CONFIG_RELOCATABLE
    bl  __relocate_kernel //内核重定位(对应mmu开启后虚拟地址空间)
#endif
    ldr x8, =__primary_switched
    adrp    x0, __PHYS_OFFSET
    br  x8
ENDPROC(__primary_switch)
                          
                          	
/*
 * The following fragment of code is executed with the MMU enabled.
 *
 *   x0 = __PHYS_OFFSET
 */
__primary_switched:
	adrp	x4, init_thread_union
	add	sp, x4, #THREAD_SIZE
	adr_l	x5, init_task  //初始化内核任务(准备的0号线程/进程和内核栈)
	msr	sp_el0, x5			// Save thread_info

  //异常向量表设置
	adr_l	x8, vectors			// load VBAR_EL1 with virtual
	msr	vbar_el1, x8			// vector table address
	isb

	stp	xzr, x30, [sp, #-16]!
	mov	x29, sp

	str_l	x21, __fdt_pointer, x5		// Save FDT pointer(指向设备树dtb)

	ldr_l	x4, kimage_vaddr		// Save the offset between
	sub	x4, x4, x0			// the kernel virtual and
	str_l	x4, kimage_voffset, x5		// physical mappings


	b	start_kernel    //跳转到内核C 语言部分 init/main.c   start_kernel (启动内核初始化)
ENDPROC(__primary_switched)

内核初始化(C部分)

//start_kernel: 内核启动初始化
asmlinkage __visible void __init start_kernel(void)
{
	char *command_line;
	char *after_dashes;

	set_task_stack_end_magic(&init_task);
	//日志: Booting Linux on physical CPU 0x0
	smp_setup_processor_id(); //  -> arch/arm64/kernel/setup.c  多核CPU ID的安装
	debug_objects_early_init();

	/*
	 * Set up the the initial canary ASAP:
	 */
	boot_init_stack_canary();

	cgroup_init_early();

	local_irq_disable();
	early_boot_irqs_disabled = true;

/*
 * Interrupts are still disabled. Do necessary setups, then
 * enable them
 */
	boot_cpu_init();
	page_address_init();
	//日志: Linux version 4.9.253-tegra
	pr_notice("%s", linux_banner);
	//日志:Boot CPU: AArch64 Processor [411fd071]
	setup_arch(&command_line); //设置CPU体系架构 -> arch/arm64/kernel/setup.c
	                            //vim跟踪:  ctrl+] 跳转  ctrl+o 返回,   :f 查看当前文件路径
	mm_init_cpumask(&init_mm);
	setup_command_line(command_line); //安装命令行参数(u-boot传入的参数)
	setup_nr_cpu_ids();
	setup_per_cpu_areas();
	smp_prepare_boot_cpu();	/* arch-specific boot-cpu hooks */
	boot_cpu_hotplug_init();

	build_all_zonelists(NULL, NULL, false);
	page_alloc_init();

  //日志: Kernel command line: tegraid=21.1.2.0.0 ddr_die=4096M@2048M section=512M memtype=0 vpr_resize usb_port_owner_info=0 lane_owner_info=0 emc_max_dvfs=0 touch_id=0@63 video=tegrafb no_console_suspend=1 console=ttyS0,115200n8 debug_uartport=lsport,4 earlyprintk=uart8250-32bit,0x70006000 maxcpus=4 usbcore.old_scheme_first=1 lp0_vec=0x1000@0xff780000 core_edp_mv=1075 core_edp_ma=4000 gpt  earlycon=uart8250,mmio32,0x70006000  root=/dev/mmcblk0p1 rw rootwait rootfstype=ext4 console=ttyS0,115200n8 console=tty0 fbcon=map:0 net.ifnames=0 quiet root=/dev/mmcblk0p1 rw rootwait rootfstype=ext4 console=ttyS0,115200n8 console=tty0 fbcon=map:0 net.ifnames=0 "
	pr_notice("Kernel command line: %s\n", boot_command_line);   //命令行参数(u-boot传入)
	/* parameters may set static keys */
	jump_label_init();
	parse_early_param();
	after_dashes = parse_args("Booting kernel",
				  static_command_line, __start___param,
				  __stop___param - __start___param,
				  -1, -1, NULL, &unknown_bootoption);
	if (!IS_ERR_OR_NULL(after_dashes))
		parse_args("Setting init args", after_dashes, NULL, 0, -1, -1,
			   NULL, set_init_arg);

	/*
	 * These use large bootmem allocations and must precede
	 * kmem_cache_init()
	 */
	setup_log_buf(0);
	pidhash_init();
	vfs_caches_init_early();
	sort_main_extable();
	trap_init(); //系统调用初始化
	/*日志:  Virtual kernel memory layout:   内存布局
[    0.000000]     modules : 0xffffff8000000000 - 0xffffff8008000000   (   128 MB)
[    0.000000]     vmalloc : 0xffffff8008000000 - 0xffffffbebfff0000   (   250 GB)
[    0.000000]       .text : 0xffffff8008080000 - 0xffffff8008f80000   ( 15360 KB)
[    0.000000]     .rodata : 0xffffff8008f80000 - 0xffffff8009610000   (  6720 KB)
[    0.000000]       .init : 0xffffff8009610000 - 0xffffff8009e80000   (  8640 KB)
[    0.000000]       .data : 0xffffff8009e80000 - 0xffffff800a163008   (  2957 KB)
[    0.000000]        .bss : 0xffffff800a163008 - 0xffffff800a1fc0b4   (   613 KB)
[    0.000000]     fixed   : 0xffffffbefe7fd000 - 0xffffffbefec00000   (  4108 KB)
[    0.000000]     PCI I/O : 0xffffffbefee00000 - 0xffffffbeffe00000   (    16 MB)
[    0.000000]     vmemmap : 0xffffffbf00000000 - 0xffffffc000000000   (     4 GB maximum)
[    0.000000]               0xffffffbf00000000 - 0xffffffbf03fc8000   (    63 MB actual)
[    0.000000]     memory  : 0xffffffc000000000 - 0xffffffc0ff200000   (  4082 MB)
	*/
	mm_init();  //内存管理初始化 -> mem_init() -> arch/arm64/mm/init.c
	/*
	 * Set up the scheduler prior starting any interrupts (such as the
	 * timer interrupt). Full topology setup happens at smp_init()
	 * time - but meanwhile we still have a functioning scheduler.
	 */
	sched_init(); //内核调度初始化(多任务 并发调度)
	/*
	 * Disable preemption - early bootup scheduling is extremely
	 * fragile until we cpu_idle() for the first time.
	 */
	preempt_disable(); //禁止抢占
	if (WARN(!irqs_disabled(),
		 "Interrupts were enabled *very* early, fixing it\n"))
		local_irq_disable();
	idr_init_cache();
	rcu_init();

	/* trace_printk() and trace points may be used after this */
	trace_init();

	context_tracking_init();
	radix_tree_init();
  //日志: NR_IRQS:64 nr_irqs:64 0
	/* init some links before init_ISA_irqs() */
	early_irq_init(); //早期中断初始化: -> kernel/irq/irqdesc.c
	//日志: /interrupt-controller@60004000: 192 interrupts"
	init_IRQ(); //中断初始化: arch/arm64/kernel/irq.c	-> irqchip_init() -> drivers/of/irq.c   of_irq_init()   扫描设备树,匹配中断控制器节点
	tick_init(); //时钟事件(滴答)初始化: 每滴答一下,产生一个时间周期事件,通知系统
	rcu_init_nohz();
	init_timers(); //初始化定时器
	hrtimers_init();
	softirq_init();
	timekeeping_init();
	time_init();   //时间初始化
	sched_clock_postinit();
	printk_safe_init();
	perf_event_init();
	profile_init();
	call_function_init();
	WARN(!irqs_disabled(), "Interrupts were enabled early\n");
	early_boot_irqs_disabled = false;
	local_irq_enable();

	kmem_cache_init_late();

	/*
	 * HACK ALERT! This is early. We're enabling the console before
	 * we've done PCI setups etc, and console_init() must be aware of
	 * this. But we do want output early, in case something goes wrong.
	 */
	console_init(); //控制台初始化
	if (panic_later)
		panic("Too many boot %s vars at `%s'", panic_later,
		      panic_param);

	lockdep_info();

	/*
	 * Need to run this when irqs are enabled, because it wants
	 * to self-test [hard/soft]-irqs on/off lock inversion bugs
	 * too:
	 */
	locking_selftest();

#ifdef CONFIG_BLK_DEV_INITRD
	if (initrd_start && !initrd_below_start_ok &&
	    page_to_pfn(virt_to_page((void *)initrd_start)) < min_low_pfn) {
		pr_crit("initrd overwritten (0x%08lx < 0x%08lx) - disabling it.\n",
		    page_to_pfn(virt_to_page((void *)initrd_start)),
		    min_low_pfn);
		initrd_start = 0;
	}
#endif
	page_ext_init();
	debug_objects_mem_init();
	kmemleak_init();
	setup_per_cpu_pageset();
	numa_policy_init();
	if (late_time_init)
		late_time_init();
	sched_clock_init();
	calibrate_delay();
	pidmap_init();
	anon_vma_init();
	acpi_early_init();
#ifdef CONFIG_X86
	if (efi_enabled(EFI_RUNTIME_SERVICES))
		efi_enter_virtual_mode();
#endif
#ifdef CONFIG_X86_ESPFIX64
	/* Should be run before the first non-init thread is created */
	init_espfix_bsp();
#endif
	thread_stack_cache_init();
	cred_init();
	fork_init(); //进程初始化
	proc_caches_init();
	buffer_init();
	key_init();
	security_init();
	dbg_late_init();
	vfs_caches_init();
	signals_init(); //信号初始化
	/* rootfs populating might need page-writeback */
	page_writeback_init();
	proc_root_init();
	nsfs_init();
	cpuset_init();
	cgroup_init();
	taskstats_init_early();
	delayacct_init();

	check_bugs();

	acpi_subsystem_init();
	sfi_init_late();

	if (efi_enabled(EFI_RUNTIME_SERVICES)) {
		efi_late_init();
		efi_free_boot_services();
	}

	ftrace_init();

	/* Do the rest non-__init'ed, we're now alive */
	rest_init(); //创建初始进程
	             //各驱动模块的初始化: -> kernel_init-> kernel_init_freeable -> do_basic_setup -> do_initcalls()

	prevent_tail_call_optimization();
}
1.体系架构安装
//setup_arch: 设置CPU体系架构 和设备树
//arch/arm64/kernel/setup.c

void __init setup_arch(char **cmdline_p)
{
	pr_info("Boot CPU: AArch64 Processor [%08x]\n", read_cpuid_id());

	sprintf(init_utsname()->machine, UTS_MACHINE);
	init_mm.start_code = (unsigned long) _text;
	init_mm.end_code   = (unsigned long) _etext;
	init_mm.end_data   = (unsigned long) _edata;
	init_mm.brk	   = (unsigned long) _end;

	*cmdline_p = boot_command_line;

	early_fixmap_init();
	early_ioremap_init();

	setup_machine_fdt(__fdt_pointer);//设置 机器设备树

	parse_early_param();

	/*
	 *  Unmask asynchronous aborts after bringing up possible earlycon.
	 * (Report possible System Errors once we can report this occurred)
	 */
	local_async_enable();

	/*
	 * TTBR0 is only used for the identity mapping at this stage. Make it
	 * point to zero page to avoid speculatively fetching new entries.
	 */
	cpu_uninstall_idmap();

	xen_early_init();
	efi_init();
	arm64_memblock_init();

	paging_init();

	acpi_table_upgrade();

	/* Parse the ACPI tables for possible boot-time configuration */
	acpi_boot_table_init();

	if (acpi_disabled)
		unflatten_device_tree();   //解析设备树,存在全局变量of_root中,通过它可遍历整个设备树

	bootmem_init();

	kasan_init();

	request_standard_resources();

	early_ioremap_reset();

	if (acpi_disabled)
		psci_dt_init();
	else
		psci_acpi_init();

	cpu_read_bootcpu_ops();
	smp_init_cpus();
	smp_build_mpidr_hash();

}

//由u-boot通过寄存器x5 传递 设备树的地址dt_phys
static void __init setup_machine_fdt(phys_addr_t dt_phys)
{
	void *dt_virt = fixmap_remap_fdt(dt_phys);

	if (!dt_virt || !early_init_dt_scan(dt_virt)) { //检查设备树 幻数匹配否
		pr_crit("\n"
			"Error: invalid device tree blob at physical address %pa (virtual address 0x%p)\n"
			"The dtb must be 8-byte aligned and must not exceed 2 MB in size\n"
			"\nPlease check your bootloader.",
			&dt_phys, dt_virt);

		while (true)
			cpu_relax();
	}

  //获取机器 名字(单板)
	dump_stack_set_arch_desc("%s (DT)", of_flat_dt_get_machine_name());
}
2.剩余的初始化
//rest_init: 余下的初始化
static noinline void __ref rest_init(void)
{
	kernel_thread(kernel_init, NULL, CLONE_FS); //创建 1号进程(线程/任务):  init进程(kernel_init)
	pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES); //2号进程: 内核守护进程(kthreadd)

	//0号进程(当前任务): 空闲进程(即有事就就,没事idle空转待命)
	cpu_startup_entry(CPUHP_ONLINE); // cpu_idle_loop() kernel/sched/idle.c

// $ ps -aux  可查看 当前运行的所有进程
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.1 161068  5424 ?        Ss   09:56   0:12 /sbin/init 2   //init进程
root         2  0.0  0.0      0     0 ?        S    09:56   0:05 [kthreadd]     //内核守护进程
 // 0号进程 看不到,因它事内核进程,应用空间看不到
}


static int __ref kernel_init(void *unused)
{
	int ret;

	kernel_init_freeable();
	/* need to finish all async __init code before freeing the memory */
	async_synchronize_full();
	free_initmem();
	mark_readonly();
	system_state = SYSTEM_RUNNING;
	numa_default_policy();

	rcu_end_inkernel_boot();

	if (ramdisk_execute_command) {
		ret = run_init_process(ramdisk_execute_command);
		if (!ret)
			return 0;
		pr_err("Failed to execute %s (error %d)\n",
		       ramdisk_execute_command, ret);
	}

	/*
	 * We try each of these until one succeeds.
	 *
	 * The Bourne shell can be used instead of init if we are
	 * trying to recover a really broken machine.
	 */
	 /* 如果有启动参数做为执行命令,就执行
	 如无则 第一备用:/sbin/init,第二备用:/etc/init,第三备用:/bin/init,第四备用:/bin/sh。
	 */
	if (execute_command) {
		ret = run_init_process(execute_command);/*execute_command是由u-boot里传入的参数bootargs中init来指定的,
		                                     如 setenv bootargs root=nfs nfsroot=192.168.9.120:/nfs/rootfs console=ttySAC2,115200 init=/linuxrc ip=192.168.9.9
		                                      这里init 是linuxrc (由busybox编译生成)  ,
		                                     linuxrc主要是负责解析/etc下配置文件 如启动脚本/etc/init.d/rcS
		                                     */
		if (!ret)
			return 0;
		panic("Requested init %s failed (error %d).",
		      execute_command, ret);
	}

	//从ps -aux知 运行init进程是/sbin/init
	if (!try_to_run_init_process("/sbin/init") || // ->run_init_process -> do_execve() 从内核态切换到应用态(单向的)
	    !try_to_run_init_process("/etc/init") ||
	    !try_to_run_init_process("/bin/init") ||
	    !try_to_run_init_process("/bin/sh"))
		return 0;

	panic("No working init found.  Try passing init= option to kernel. "
	      "See Linux Documentation/init.txt for guidance.");
}

static noinline void __init kernel_init_freeable(void)
{
	do_basic_setup();  //基本设置(如 设备驱动初始化)
                     //在体系结构相关设置已完成后进行
	/* Open the /dev/console on the rootfs, this should never fail */
	if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0) //打开输入输出终端
		pr_err("Warning: unable to open an initial console.\n");

	load_default_modules();
}


/*
 * Ok, the machine is now initialized. None of the devices
 * have been touched yet, but the CPU subsystem is up and
 * running, and memory and process management works.
 *
 * Now we can finally start doing some real work..
 */
static void __init do_basic_setup(void)  //基本设置(如 设备驱动初始化)
{
	cpuset_init_smp();
	shmem_init();
	driver_init();  //驱动 初始化 -> drivers/base/init.c
	init_irq_proc();
	do_ctors();
	usermodehelper_enable();
	do_initcalls(); //各驱动模块初始化
}
3.驱动初始化
//driver_init: 驱动初始化(如/dev/  /sys/devices 文件系统)
/**
 * driver_init - initialize driver model.
 *
 * Call the driver model init functions to initialize their
 * subsystems. Called early from init/main.c.
 */
void __init driver_init(void)
{
	/* These are the core pieces */
	devtmpfs_init();   //设备文件系统初始化( /dev 所挂载的文件系统)
	devices_init(); /*设备初始化(创建 /sys/dev  /sys/devices )
	->drivers/base/core.c
	int __init devices_init(void)
	{
		devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);
		if (!devices_kset)
			return -ENOMEM;
		dev_kobj = kobject_create_and_add("dev", NULL);
		if (!dev_kobj)
			goto dev_kobj_err;
		sysfs_dev_block_kobj = kobject_create_and_add("block", dev_kobj);

		sysfs_dev_char_kobj = kobject_create_and_add("char", dev_kobj);
	}
	*/
	buses_init(); /*总线初始化(创建 /sys/bus 和 /sys/devices/system)
	drivers/base/bus.c     :set nonu取消行号
		int __init buses_init(void)
		{
		    bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL);
		    if (!bus_kset)
		        return -ENOMEM;

		    system_kset = kset_create_and_add("system", NULL, &devices_kset->kobj);
		    if (!system_kset)
		        return -ENOMEM;

		    return 0;
		}
	*/
	classes_init(); //设备类的初始化(创建  /sys/class)
	firmware_init(); //固件初始化(创建 /sys/firmware)
	hypervisor_init();

	/* These are also core pieces, but must come after the
	 * core core pieces.
	 */
	platform_bus_init(); /*平台设备总线初始化
  drivers/base/platform.c
	int __init platform_bus_init(void)
	{
	    int error;

	    early_platform_cleanup();

	    error = device_register(&platform_bus);
	    if (error)
	        return error;
	    error =  bus_register(&platform_bus_type);
	    if (error)
	        device_unregister(&platform_bus);
	    of_platform_register_reconfig_notifier();
	    return error;
	}
	struct bus_type platform_bus_type = {
    .name       = "platform",
    .dev_groups = platform_dev_groups,
    .match      = platform_match,
    .uevent     = platform_uevent,
    .pm     = &platform_dev_pm_ops,
	};
  */
	cpu_dev_init();
	memory_dev_init();
	container_dev_init();
	of_core_init(); //设备树初始化(创建 /sys/firmware/devicetree)
}
4.驱动模块初始化
动因 //维护灾难: 手动在内核init程序中,添加自己模块初始化,会带来维护灾难
     //          而维护一个初始化函数数组,也是会眼花缭乱

办法 //在内核镜像文件中,自定义一个段,这个段里面专门用来存放这些初始化函数的地址,
    //编译时自动添加到自定义段 
    //内核启动时,会从该段地址处取出函数指针,逐个执行。

//rest_init -> kernel_init-> kernel_init_freeable -> do_basic_setup -> do_initcalls()
static void __init do_initcalls(void)
{
	int level;

        //遍历:逐个调用各驱动模块初始化 ->  避免手动添加的灾难
	for (level = 0; level < ARRAY_SIZE(initcall_levels) - 1; level++)
		do_initcall_level(level);
}
5.声明驱动模块启动自动调用
//initcall使用实例
//从日志 切入
[    0.207540] Tegra Revision: A02 SKU: 0x8f CPU Process: 0 SoC Process: 1/* 源码跟踪
vscode搜寻:  
   drivers目录 右键 -> 在文件夹下查找  Tegra Revision: ->
   pr_info("Tegra Revision: %s SKU: 0x%x CPU Process: %d SoC Process: %d\n",
   early_initcall(tegra_init_fuse);
   
在system.map搜:   
  /tegra_init_fuse  ->
   ffffff8009639ba8 t tegra_init_fuse  -> 
      $ aarch64-linux-gnu-addr2line ffffff8009639ba8 -e vmlinux -f
       tegra_init_fuse
       /home/yhai/kernel-4.9/drivers/soc/tegra/fuse/fuse-tegra.c:330
   ffffff8009823498 t __initcall_tegra_init_fuseearly  -> 由来
	
源码跳转: 		
	early_initcall(tegra_init_fuse) -> ctrl+] ->
	#define early_initcall(fn)      __define_initcall(fn, early) -> ctrl+] ->
	
	#define __define_initcall(fn, id) \  
	static initcall_t __initcall_name(fn, id) __used \
	__attribute__((__section__(".initcall" #id ".init"))) = fn;  ->  ctrl+] ->
	
	#define __initcall_name(fn, id)   __initcall_##fn##id ->
	  __initcall_tegra_init_fuseearly
		
	*/
[    0.207556] DTS File Name: /dvs/git/dirty/git-master_linux/kernel/kernel-4.9/arch/arm64/boot/dts/../../../../../../hardware/nvidia/platform/t210/porg/kernel-dts/tegra210-p3448-0000-p3449-0000-b00.dts
/*跟踪
在system.map搜:
   __initcall_tegra_init_fuseearly的后面 
  __initcall_display_tegra_dts_infoearly  ->  /display_tegra_dts_info ->
  
  ffffff800963ae34 t display_tegra_dts_info ->
     $ aarch64-linux-gnu-addr2line ffffff800963ae34 -e vmlinux -f
		display_tegra_dts_info
		/home/yhai/kernel-4.9/drivers/soc/tegra/common.c:561
		
		-> pr_info("DTS File Name: %s\n", dts_fname);
  

*/
ldr、str、ldm、stm、msr、mrs、swi、svc、mrc等ARM指令详解及具体应用 本期和大家主要分享的是ldr、str、ldm、stm、msr、mrs、swi、svc、mrc等ARM指令的具体介绍,对这些基础概念掌握后,接下来就能够读懂基本的2440A的启动代码了,也有助于自己去独立的书写启动代码,依次更好的实现自己想要实现的功能,更好的理解正式启动代码设计流程的巧妙之处,对CPU处理事务的流程以及处理细节会有更深的认识; 阅读详情

相关推荐

寄存器内存读写指令(一) —— 单寄存器读写 LDR / STR

寄存器内存读写指令(一) —— 单寄存器读写 LDR / STR

challenglistic的博客 7026

【Linux内核|启动】内核启动流程(一)head.S分析

1. 内核启动入口 内核版本:linux-6.1 根据Makefile对KBUILD_LDS的定义,链接vmlinux使用的连接脚本为 arch/$(SRCARCH)/kernel/vmlinux.lds,实际上这个脚本是在编译过程中生成的。从链接脚本 arch/arm64/kernel/vmlinux.lds可以查到,程序的入口为 _text,镜像起始位置存放的是 .head.text段生成的指令。 OUTPUT_ARCH(aarch64) ENTRY(_text) SECTIONS { . = ((

2353

从零构建最小Linux系统:内核编译、根文件系统与QEMU启动实战

在实际操作系统开发领域,很多开发者对内核的理解停留在“调用系统API”的层面,而真正能动手修改内核、添加驱动、甚至构建一个最小可运行系统的经验却非常稀缺。这导致在遇到复杂的性能调优、稳定性问题或硬件适配需求时,往往无从下手。本文旨在填补这一实践空白,我们将从零开始,基于Linux内核源码,完成一个可启动、可交互的最小操作系统原型。这个过程将覆盖内核编译、根文件系统构建、引导配置、以及一个简单的用户态程序交互。无论你是想深入理解操作系统原理,还是为嵌入式开发或系统级编程打下坚实基础,这篇手把手的教程都将提供一

weixin_30905133的博客 367

Linux内核(Kernel)启动过程分析

Linux内核(Kernel)启动过程

Android应用开发工程师 2270

嵌入式Linux内核硬件调试实战:CodeWarrior与BDI2000深度解析

在嵌入式系统开发中,内核调试是定位复杂系统级问题的关键技术。其核心原理是通过硬件调试接口(如JTAG/BDM)直接访问处理器内部状态,实现对系统运行态的精准控制。这种硬件辅助调试方式具有极高的技术价值,尤其适用于系统启动失败、内核死锁、内存泄漏等传统日志调试难以解决的场景。通过源码级调试,开发者能够实时查看寄存器、内存数据和函数调用栈,从根本上提升问题诊断效率。在嵌入式Linux领域,结合专用调试探头(如Abatron BDI2000)与集成开发环境(如CodeWarrior IDE),可以构建完整的硬件调

weixin_32496547的博客 372

Linux内核分析:实验五--使用GDB跟踪系统调用执行过程

Linux内核分析:实验五–使用GDB跟踪系统调用执行过程刘畅 原创作品转载请注明出处 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000概述本次实验使用GDB跟踪一个系统调用,上一次实验分别使用了C语言和嵌入式汇编实现了write系统调用。这次实验把上次实验写的系统调用代码加入MenuOS中,并且跟踪这个系统调用的执行

Lansing999的专栏 2372

Linux加速启动,启动时间的极限优化

在上次完成嵌入式应用的Linux裁减后,Linux的启动时间仍需要 7s 左右,虽然勉强可以接受,但仍然没有达到我个人所追求的目标——2s 以内。况且,在实际的商用环境中,设备可靠性的要求可是“5个9”(99.999%,即OOS时间低于5分钟/年),这就意味着每减少一秒钟Linux启动(设备复位)时间,对可靠性都是一个明显的提升。

reille的笔记 1万+

linux内核启动过程详细分析

systemd就是为解决这一问题而诞生的,它的设计目标是:为系统的启动和管理提供一整套解决方案。 根据linux管理,字母d是守护进程daemon的缩写,systemd 就是它守护整个系统。守护进程是一直执行的,人为关不了 使用了systemd,就不需要init了,systemd取代了init,成为系统第一个进程,其他进程都是它的子进程

景天科技苑 1万+

Linux启动时间的极限优化(Z)

作者: Maco   该文章转载自网络大本营:http://xrss.cn/Info/13420.Html在上次完成嵌入式应用的Linux裁减后,Linux的启动时间仍需要 7s 左右,虽然勉强可以接受,但仍然没有达到我个人所追求的目标——2s 以内。况且,在实际的商用环境中,设备可靠性的要求可是“5个9”(99.999%,即OOS时间低于5分钟/年),这就意味着每减少一秒钟Linux启动(设备复...

gioc的专栏 169

DM-VIO论文精读:从‘延迟边缘化’到‘位姿图BA’,手把手拆解单目VIO性能超越双目的秘密

本文深入解析DM-VIO技术如何通过延迟边缘化和位姿图BA创新,解决单目视觉惯性里程计(VIO)的尺度不可观性问题,并在精度上超越传统双目系统。文章详细介绍了延迟边缘化的双因子图结构设计、位姿图BA的多级初始化策略,以及系统级优化带来的性能提升,为VIO研究与实践提供了重要参考。

weixin_30616969的博客 427

计算机组成原理课程设计_微程序控制的简单模型机设计

微程序控制的简单模型机设计 要求 基本要求 完成一个简单主机的设计,模型机应具备如下能力:在自行设计的指令集基础上,运行自行编写的简单用户程序,实现特定运算功能。 弹性要求 在此基础上,完成必要的扩展,实现模型机的功能扩展设计,包括:实现多种运算功能(指令集的扩充)、支持更为丰富的寻址方式、有更为完善的微程序设计等。 实验软件和硬件环境 QuartusII, FPGA计算机组...

sailist的记录站 3万+

Linux启动时间的极限优化

作者: Maco 在上次完成嵌入式应用的Linux裁减后,Linux的启动时间仍需要 7s 左右,虽然勉强可以接受,但仍然没有达到我个人所追求的目标——2s 以内。况且,在实际的商用环境中,设备可靠性的要求可是“5个9”(99.999%,即OOS时间低于5分钟/年),这就意味着每减少一秒钟Linux启动(设备复位)时间,对可靠性都是一个明显的提升。  言归正传,如何着手对Linux的启动时间进...

weixin_34208283的博客 110

【机械视觉】C#+VisionPro实战:位深图(CogImage8Grey)与Bitmap互转技巧及工业检测应用

本文详细介绍了在工业检测中如何使用C#和VisionPro实现位深图(CogImage8Grey)与Bitmap的高效互转技巧。通过实战代码示例和工业应用案例,帮助开发者解决图像处理中的常见问题,提升检测精度和系统性能,适用于半导体、汽车零部件等高精度检测场景。

weixin_29250573的博客 186

一文通透:嵌入式 Linux 内核调试与分析实战指南(以 ARM 架构为例)

一文通透:嵌入式 Linux 内核调试与分析实战指南(以 ARM 架构为例)

深耕嵌入式领用多年, 致力于分享嵌入式领域技术! 1771

QT使用openssl实现RSA加解密

使用openssl生成公私钥,随后用QT对相应的信息进行RSA加解密

weixin_42098322的博客 3867

Zemax光机热集成分析:从FEA数据导入到光学性能评估实战

在精密光学系统设计中,环境温度变化和机械应力是导致像质恶化的关键因素,这涉及到光、机、热多物理场的耦合分析。其核心原理在于,通过有限元分析(FEA)软件(如ANSYS)模拟热与力学载荷下的结构变形和温度分布,并将这些物理场数据精准映射到光学设计软件(如Zemax)中,以评估其对调制传递函数(MTF)、点列图等核心光学指标的影响。这项技术的工程价值在于,它使设计从静态的理想模型跃升至动态的工程现实,能够在设计阶段就预测并补偿环境扰动,从而显著提升光学系统在航空航天、车载镜头、高功率激光等严苛工况下的可靠性与稳

weixin_30565327的博客 355

什么是Linux内核,如何搞懂Linux内核?(Linux内核学习笔记合集来了!)

操作系统(简称为OS)是一中软件平台,可创建一个环境,用户可以再该环境中再计算设备上运行不同的应用程序。它被移动设备,标签,台式机,web服务器,视频游戏机等不同的设备所利用。如果你有这些技能,你可以试试给暴雪发简历,给他们写服务器后台,以为全世界的魔兽都运行在你的服务器软件上。当然,一般的网络公司,哪怕是百度级别的,都应该毫不犹豫的录用你。每当将操作系统加载到内存中时,首先,将加载内核并将其保留在那里,直到操作系统关闭。内核负责处理低级任务,例如任务管理,内存管理,风险管理等。内核是操作系统的关键组件。

weixin_44909451的博客 5879

DINOv3实战指南:如何利用零样本视觉模型革新工业质检

本文详细介绍了如何利用DINOv3零样本视觉模型革新工业质检流程。通过无需标注数据的自监督学习技术,DINOv3能快速部署高精度检测系统,显著降低成本和提升效率。文章涵盖模型选型、数据准备、实时检测优化及典型行业解决方案,为工业质检提供革命性AI实践指南。

ansible6ops的博客 120

linux跟踪系统调用到内核,Linux内核分析:实验五--使用GDB跟踪系统调用执行过程...

Linux内核分析:实验五–使用GDB跟踪系统调用执行过程刘畅 原创作品转载请注明出处 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000概述本次实验使用GDB跟踪一个系统调用,上一次实验分别使用了C语言和嵌入式汇编实现了write系统调用。这次实验把上次实验写的系统调用代码加入MenuOS中,并且跟踪这个系统调用的执...

weixin_39692045的博客 492

【WRF-Urban】使用 LCZ 替换 WRF 运行中的 LUCC 数据

使用 LCZ 替换 WRF 运行中的 LUCC 数据

WW、forever的博客 987

立体仓库WCS控制系统-AGV通信代码

string str = "任务下发,WCS->NT8000(q-Message),ikey:" + p.ikey.ToString() + "fromStation:" + p.fromStation.ToString() + "toStation:" + p.toStation.ToString();if (nt8000.Connect(_ServerAddr, _ServerPort)) QueueSpeech.AddQueue(string.Format("与NT8000重连成功"));

xdpcxq的专栏 2201

linux2.6.35.3优化内核启动时间之杂记

linux2.6.35.3优化内核启动时间之杂记

开阳-英飞凌-小华 1064

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

CodeWarrior ARMv7开发工具:嵌入式Linux多核调试与性能分析实战

在嵌入式系统开发中,集成开发环境(IDE)与调试工具链是连接硬件与软件的关键桥梁,其核心价值在于提供从底层寄存器访问到上层应用调试的全栈支持。其工作原理通常基于Eclipse等成熟框架,通过深度定制的调试引擎、硬件配置工具和性能分析模块,实现对特定处理器架构(如ARM Cortex-A系列)的优化支持。这种深度集成带来的技术价值在于显著降低开发门槛,提升调试效率,尤其在多核处理器、TrustZone安全架构等复杂场景下。在应用场景上,它广泛服务于嵌入式Linux内核/模块调试、裸机程序开发、闪存编程以及系统

weixin_30236595的博客 325

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

举例跟踪分析Linux内核5.0系统调用处理过程

使用Ubuntu编译Linux内核5.0

BYF_694042的博客 382

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

Openwrt与Qemu联袂:打造高效内核源码调试环境

本文详细介绍了如何利用OpenWrt与Qemu构建高效的内核源码级调试环境。通过OpenWrt灵活的模块化构建系统与Qemu强大的虚拟化及GDB调试支持,开发者可以安全、便捷地进行Linux内核的源码级单步调试、自定义模块开发与验证,极大提升了嵌入式内核学习与问题排查的效率。

uber9的博客 409
上一篇: 【C++/嵌入式梳理】一、23.结构体指针 | 指针和引用 | 万能指针 | 野指针
下一篇: 【C++/嵌入式梳理】一、24.智能指针 | 其他
赛博二哈
博客等级 码龄8年 5768粉丝 98原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值