从开机到运行程序

从一个冷冰冰、内存全空的铁盒子,到你能双击图标跑起一个程序——中间发生了什么?本文从计算机组成原理讲到操作系统,并用几个动态交互把「开机」和「运行」拆给你看。

一、上电前的硬件:为什么不能直接跑程序

先认识四个主角,理解它们的「脾气」——尤其是内存会失忆

🧠
CPU
只会一条条执行指令;一上电就傻傻地从固定地址开始取指令
💾
DRAM 内存
快,但断电即清空。上电瞬间里面全是「随机垃圾」
🧾
ROM / Flash
断电不丢,存着 BIOS/UEFI——开机第一段程序就在这
🖴
磁盘
存操作系统和你的程序;但 CPU 不能直接执行,得先搬进内存
核心矛盾:程序必须进内存才能被 CPU 执行,但内存一上电是空的。那 CPU 第一句该执行哪条指令?答案藏在下一节——ROM 里的 BIOS 就是为解决这个问题而存在的。

二、按下电源:CPU 从哪里开始跑

复位向量(Reset Vector)——计算机启动的「起跑线」

🏁 复位向量

通电后,CPU 的复位电路会把 程序计数器(PC / IP) 设成一个固定的硬件地址——这个地址指向 ROM 里 BIOS 的入口。x86 上这个地址是 0xFFFF0(几乎在内存顶端的 ROM 区)。

# 上电瞬间(x86 简化)
Reset:  CS:IP = 0xFFFF:0x0000   # 物理地址 0xFFFF0,落在 BIOS ROM 内
        CPU 从这里取第一条指令执行  →  进入 BIOS

于是 CPU 开始执行 BIOS 代码——它不知道什么是「操作系统」,只知道「按地址取指令」。BIOS 才是第一个被运行的软件。

三、开机时序:一步步点亮一台机器(可交互)

点「下一步」推进;观察右侧哪个硬件在被使用。也可「自动播放」

🧠
CPU
取指执行
🧾
ROM/BIOS
开机固件
💾
内存 RAM
逐步被填入
🖴
磁盘
存有 OS
准备就绪——点「下一步」开始开机
从按下电源到操作系统就绪,一共 10 步。右侧硬件会随步骤亮起,表示「此刻谁在忙」。

四、BIOS / UEFI 与引导加载

自检 → 找启动盘 → 读 512 字节 MBR → GRUB 登场

🔍 POST 自检

BIOS 先跑 POST(加电自检):检查内存、显卡、键盘等是否正常,并在屏幕上打印/点亮提示。有问题就「哔」一声卡住。

🎯 寻找启动设备

按设定的启动顺序(U 盘→硬盘→网络…),找第一个「有引导扇区」的盘。引导扇区末尾必须有 0x55AA 这个「签名」才算合法。

📥 MBR → Bootloader(以 GRUB 为例)

  • BIOS 把磁盘第 0 扇区(MBR,512 字节)读进内存 0x7C00,然后跳过去执行。
  • MBR 太小,只够做「第一阶段」,它再去加载功能更全的 GRUB 第二阶段
  • GRUB 显示启动菜单,把你选的内核镜像(如 vmlinuz)和初始内存盘(initramfs)读进内存。
  • 最后 GRUB 把控制权交给内核——到这里,BIOS 的使命完成
# 引导链一句话
BIOS ─读─▶ MBR(0x7C00) ─加载─▶ GRUB Stage2 ─读─▶ 内核 vmlinuz + initramfs ─跳─▶ 内核启动

五、内核启动:把「大管家」请进门

解压内核、切到保护模式、初始化各子系统、建立系统调用表

🛠️ 内核都干了啥

  • 解压 & 切换模式:从实模式切到保护模式 / 长模式,才能用上 32/64 位地址和大内存。
  • 初始化内存管理:建立伙伴系统、Slab 分配器,后面所有内存分配都靠它。
  • 初始化中断 & 进程:建立中断向量表;创建 0 号进程(swapper / idle),它是所有进程的祖先。
  • 挂驱动、挂根文件系统:先挂 initramfs(内存里的临时根,含必要驱动),再用它去挂真实的硬盘根文件系统。
  • 建立系统调用表:把「用户态↔内核态」那道门装好。
为什么需要 initramfs?内核未必自带你磁盘控制器的驱动。initramfs 是一份「临时急救包」,先让内核能识别磁盘,再挂载真正的根——本质和「先有梯子才能上房」一样。

六、第一个用户进程:操作系统「活」了

init / systemd(PID 1)——一切用户程序的祖宗

👑 PID 1 上任

内核最后会启动一个用户态进程 init(现代是 systemd),它是所有用户进程的「老祖宗」(其他进程都是它 fork 出来的)。它负责:

  • 启动系统服务(网络、日志、数据库…);
  • 拉起登录界面 / 桌面环境;
  • 接管「孤儿进程」的回收。

到这一步,屏幕上出现登录界面或桌面——操作系统彻底就绪,可以接收你的指令了

七、运行一个程序:从敲命令到 main()(可交互)

点「下一步」,看一个进程如何被「凭空」搭建起来并跑起来

准备就绪——点「下一步」启动 ./hello
你将看到:shell 如何通过 fork + exec,把一个新程序「装」进一块全新的地址空间。

进程地址空间(逐步填充)

栈 stack函数调用、局部变量(从高地址向下长)
堆 heap动态分配 malloc(从低地址向上长)
数据段 data / .bss全局变量、静态变量
代码段 code / .text程序的机器指令(只读、可执行)

↑ 高地址  内存向上生长示意  ↓ 低地址

八、程序到底怎么"跑":取指-执行 + 系统调用(可交互)

点「单步执行」,看 CPU 如何一条条取指、译码、执行;遇到系统调用会陷入内核

用户态 (Ring 3)

寄存器

PC(程序计数器)
0
SP(栈指针)
0xFF
AX
0
BX
0

执行日志

点击「单步执行」开始…

指令内存

看到了吗?当执行到第 3 条 SYSCALL 时,模式徽章会瞬间从「用户态」变红成「内核态」——这就是陷入(trap):CPU 自动切换到内核、跳到系统调用处理程序干完活,再切回用户态。你日常每个 printf、每次存文件,背后都经历了这闪一下的特权切换。

九、总结:开机像开一家公司

把抽象流程具象化,一遍就记住

  • 上电 / BIOS = 开门、开灯、查水电(自检),确认房子能住人。
  • 引导加载(GRUB) = 找到公司钥匙和蓝图,把它交到负责人手里。
  • 内核启动 = 搭好公司的组织架构、财务系统、通讯录(内存/中断/进程/系统调用)。
  • init / systemd = CEO 上任,开始招人、开各业务部门。
  • 你运行程序 = CEO 给你开了一个新项目组(fork+exec),配好办公室(地址空间),你组里的人(CPU)开始按流程干活;要对外联系(写文件/网络)时,统一走公司的对外窗口(系统调用)。
一句话收尾:按下电源后,硬件按固定地址 blindly 跑起 BIOS;BIOS 把内核搬进内存;内核初始化一切、拉起 init;从那以后,你运行的每一个程序,都是内核用「进程 + 虚拟内存 + 系统调用」这套机制,在硬件之上为你「虚拟」出来的一方小天地。

想了解这些机制的原理?见本目录:操作系统的本质与实现原理 · 操作系统核心概念全景 · 进程生命周期 · 虚拟内存动画