2026/3/28 23:59:03
网站建设
项目流程
网站建设终身不用维护,南昌网络公司,2022今天刚刚发生地震了,十大外贸电商平台#x1f4e2; 引言#xff1a;按下电源键的那一刻#xff0c;发生了什么#xff1f;
你好#xff0c;我是你们的底层技术探路者。
每天我们都在与 Linux 打交道#xff0c;无论你是做嵌入式开发、服务器运维还是 Android ROM 定制。当你按下电源键#xff0c;屏幕亮起 引言按下电源键的那一刻发生了什么你好我是你们的底层技术探路者。每天我们都在与 Linux 打交道无论你是做嵌入式开发、服务器运维还是 Android ROM 定制。当你按下电源键屏幕亮起直到出现登录提示符这短短几秒钟内CPU 执行了数以亿计的指令。这段旅程被称为Boot Process。它是计算机科学中最精妙的接力赛ROM Code点燃火炬。Bootloader冲刺跑初始化硬件。Kernel接管世界构建秩序。User Space繁荣的文明建立。今天我将扒开 Linux 的外衣深入源码基于 ARM64 架构带你彻底搞懂这条“全链路”。本文硬核指数 ⭐⭐⭐⭐⭐建议先收藏再细品 第一阶段史前时代 —— 硬件上电与 ROM Code在 Linux 内核介入之前必须先有“立足之地”。1.1 上电复位 (Power-on Reset)当电压稳定后SoC 内部的时序控制逻辑会产生复位信号。此时CPU 的所有寄存器被重置PC 指针 (Program Counter)指向一个固定的物理地址通常是0xFFFF0000或0x00000000。1.2 BROM (Boot ROM)这个地址映射的是 SoC 内部的一小块只读存储器iROM。这里的代码是芯片出厂时固化的不可修改。它的任务非常简单且关键初始化最基本的时钟。检测启动介质拨码开关决定是从 SD 卡、eMMC 还是 NAND Flash 启动。将 Bootloader 的第一阶段代码加载到片内 SRAM 中。为什么是 SRAM因为此时 DDR内存还没初始化根本没法用SRAM 虽然贵且小通常只有几十 KB但它不需要初始化就能直接读写。 第二阶段短跑运动员 —— Bootloader (U-Boot)Bootloader 的王者无疑是U-Boot。由于 BROM 只能加载很小的代码U-Boot 通常设计为两级架构SPL U-Boot Proper。2.1 SPL (Secondary Program Loader)SPL 是 U-Boot 的“先遣队”体积极小运行在 SRAM 中。核心任务初始化 DDR 控制器这是最重要的一步让巨大的外部内存可用。将完整的 U-Boot (u-boot.bin) 搬运到 DDR 中。跳转到 DDR 执行。2.2 U-Boot Proper (完全体)此时我们已经运行在宽敞的 DDR 里了。U-Boot 开始大展拳脚board_init_f(Frontend)初始化串口为了打印 Log、定时器、分配堆栈。board_init_r(Rear)初始化网卡、Flash、USB 等外设。加载内核读取uImage或Image到内存。加载设备树 (DTB)读取.dtb文件到内存。2.3 关键时刻移交控制权在 U-Boot 的最后会执行bootm命令。它做了一件极具仪式感的事情——设置寄存器然后通过汇编指令跳转。对于 ARM64x0寄存器存放设备树DTB在内存中的物理地址。x1,x2,x3通常保留为 0。Jump直接跳转到内核的入口地址。Load SPL to SRAMInit DDR Load U-BootLoad Kernel DTBPower OnBROM / ROM CodeU-Boot SPLU-Boot Proper in RAMKernel Entry 第三阶段内核觉醒 —— 汇编入口 (head.S)从这里开始我们进入 Linux Kernel 的源码世界。入口文件通常位于arch/arm64/kernel/head.S。此时MMU内存管理单元是关闭的CPU 看到的还是物理地址。3.1 核心动作关闭中断在世界秩序建立之前不接受任何干扰。校验 CPU ID确认当前运行在哪个核上Boot CPU 还是 Secondary CPU。校验 DTB确认 U-Boot 传来的设备树地址是合法的。创建临时页表这是为了开启 MMU 做准备。Linux 内核使用的是虚拟地址如0xffff...必须建立物理地址到虚拟地址的映射。开启 MMUsctlr_el1寄存器写入开启分页机制。瞬间变化这一行汇编执行前PC 指针是0x40080000物理执行后PC 变成了0xffff8000...虚拟。3.2 跳转到 C 语言汇编的使命结束代码跳转到start_kernel()。️ 第四阶段大构建师 —— start_kernel()这是 Linux 内核中最大的函数位于init/main.c。它是整个操作系统的“构造函数”。由于内容太多我将其整理为一张 Mermaid 时序图rest_initsched_initmm_initsetup_archstart_kernelhead.Srest_initsched_initmm_initsetup_archstart_kernelhead.S0号进程 (Swapper) 诞生创建 1号进程 (Init) 和 2号进程 (Kthreadd)Jump to C code1. 解析 DTB, 确定内存布局return2. trap_init (初始化异常向量表)3. 内存管理初始化 (伙伴系统/Slub)return4. 调度器初始化 (CFS)return5. time_init (初始化系统时钟)6. 剩下的初始化4.1 关键初始化详解setup_arch()解析设备树确定有多少内存保留内存Reserved Memory在哪里。mm_init()初始化内存分配器。从此以后kmalloc可以工作了。sched_init()初始化进程调度器。从此以后多任务成为可能。rest_init()这是start_kernel的最后一步。它不会返回而是创建了两个祖先进程PID 1 (init)所有用户进程的祖先。PID 2 (kthreadd)所有内核线程的祖先。PID 0 (idle)自己退化为 idle 进程没事做的时候就跑它省电。 第五阶段文明建立 —— 用户空间 (User Space)此时内核已经就绪但系统还是个“空壳”因为没有用户程序。PID 1 进程通常是/sbin/init开始执行。5.1 挂载根文件系统 (Rootfs)内核通过解析 cmdline 中的root/dev/mmcblk0p2参数找到根文件系统所在的存储分区并将其挂载到 VFS 的根目录/。如果这一步失败你就会看到著名的Kernel panic - not syncing: VFS: Unable to mount root fs。5.2 Init 系统的分歧根据 Linux 发行版的不同Init 程序分为三类SysVinit老派读取/etc/inittab依序执行脚本。简单但慢。Systemd现代并行启动服务通过 Socket 激活。复杂但快。BusyBox Init嵌入式轻量级读取/etc/init.d/rcS。5.3 最终的 ShellInit 进程会根据配置启动getty进程监听串口或终端。当你输入用户名密码验证通过后getty会exec启动一个shell如 bash 或 sh。屏幕上跳出Welcome to Linux 6.xLogin: _恭喜你系统启动完成❓ 常见启动故障排查 (Troubleshooting)讲了这么多原理最后送大家几个实战锦囊。Q1: 卡在 “Starting kernel …” 不动了原因通常是 U-Boot 传给内核的 DTB 地址不对或者内核崩溃在了串口驱动初始化之前早期的汇编阶段。解法开启earlycon早期控制台在 bootargs 中加入earlyconuart8250,mmio32,0x...让内核在汇编阶段就能打印 Log。Q2: 提示 “No init found”原因Rootfs 挂载成功了但是找不到/sbin/init或者该文件没有执行权限或者架构不对在 ARM 上跑了 x86 的程序。解法检查 Rootfs 制作过程检查动态库依赖。Q3: 内存识别不全比如 4G 只有 2G原因U-Boot 传递的内存参数不对或者设备树里memory节点配置错误。 总结Linux 的启动流程是一场宏大的接力赛。硬件负责提供跑道。Bootloader是第一棒负责热身和起跑。Kernel是第二棒负责制定规则和构建环境。Init是最后一棒将应用生态带给用户。理解了这个流程你就不再是一个只会写代码的 Coder而是一个拥有上帝视角的系统工程师。 下一步行动你在调试 Linux 启动时遇到过最崩溃的问题是什么A. 卡在 U-Boot 进不去内核。B. Rootfs 挂载失败Panic。C. 驱动加载时死机。 欢迎在评论区留言交流如果你想看如何手写一个简单的 Bootloader请点赞告诉我下期安排