收到。启动流程用 ASCII 图表达,按 5 个块拆开,每块一张图:
块一:固件与引导
┌──────────────────────┐
│ 按下电源键 │
│ CPU 复位向量 │
│ 0xFFFFFFF0 │
└──────────┬───────────┘
│
▼
┌────────────────────────────────────┐
│ 固件层 │
│ (BIOS / UEFI) │
└─────────────────┬──────────────────┘
│
┌───────────────────────────┼───────────────────────────┐
│ │ │
▼ ▼ ▼
┌─────────────────┐ ┌─────────────────────┐ ┌─────────────────────┐
│ BIOS │ │ UEFI │ │ MBR │
│ POST 自检 │ │ 读取 NVRAM 变量 │ │ 第一扇区 512B │
│ 读取 MBR │ │ 加载 *.efi 文件 │ │ 446B 引导代码 │
│ 跳转 0x7C00 │ │ 从 ESP 分区加载 │ │ 64B 分区表 │
└────────┬────────┘ └──────────┬──────────┘ │ 0x55AA 魔数 │
│ │ └─────────────────────┘
└───────────────┬───────────┘
▼
┌───────────────────────────────────────┐
│ GRUB / systemd-boot │
│ 读取 grub.cfg / loader/*.conf │
│ 加载内核到 0x100000 │
│ 加载 initramfs 到高位内存 │
│ 填充 struct boot_params │
│ 跳转到内核 _start │
└───────────────────────────────────────┘
数据结构关键点:
- MBR 位于磁盘 LBA 0,512 字节,最后两字节必须为
0x55 0xAA - ESP 分区类型 GUID(UEFI):
C12A7328-F81F-11D2-BA4B-00A0C93EC93B struct boot_params位于物理地址0x10000,传递 initramfs 地址 (hdr.ramdisk_image) 和大小
块二:内核入口与架构初始化
┌─────────────────────┐
│ _start │
│ header.S │
│ 实模式 (16-bit) │
└──────────┬──────────┘
│
▼
┌─────────────────────┐
│ go_to_protected │
│ 设置 A20 线 │
│ 加载 GDT │
│ → 保护模式 32-bit │
└──────────┬──────────┘
│
▼
┌─────────────────────┐
│ startup_32 │
│ head_64.S │
│ 设置临时页表 │
│ → 长模式 64-bit │
└──────────┬──────────┘
│
▼
┌─────────────────────┐
│ startup_64 │
│ decompress_kernel │
│ 解压 vmlinuz │
│ 跳转 C 入口 │
└──────────┬──────────┘
│
▼
┌────────────────────────────────────────────────────────────────────┐
│ start_kernel() [init/main.c] │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ setup_arch() 解析 boot_params,初始化 init_mm.pgd │ │
│ │ trap_init() 设置 IDT,注册 syscall 入口 │ │
│ │ mm_init() 伙伴系统,SLAB 分配器 │ │
│ │ sched_init() 为每个 CPU 创建 struct rq,CFS 红黑树 │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ rest_init() ──────────→ kernel_thread(kernel_init) → PID 1 │
│ ─────────→ kernel_thread(kthreadd) → PID 2 │
│ │ │
│ ▼ │
│ ┌───────────────────────┐ │
│ │ cpu_startup_entry │ │
│ │ idle 进程 (PID 0) │ │
│ └───────────────────────┘ │
└────────────────────────────────────────────────────────────────────┘
数据结构关键点:
struct boot_params:位于0x10000,引导程序填充,内核setup_arch读取init_mm:内核初始mm_struct,pgd指向内核页表,mm_users初始为 1struct rq:每 CPU 一个,包含cfs_rq(红黑树根tasks_timeline)task_struct:PID 1 初始mm=NULL(内核线程),files=NULL
块三:initramfs
┌─────────────────────────────────────────────────────────────────┐
│ kernel_init() │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ populate_rootfs() 解压 initramfs 到 rootfs (tmpfs) │ │
│ │ /init 成为 PID 1 (用户态进程) │ │
│ └─────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ /init 脚本 (busybox / dracut / mkinitcpio) │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ mount -t proc proc /proc │ │
│ │ mount -t sysfs sysfs /sys │ │
│ │ mount -t devtmpfs devtmpfs /dev │ │
│ │ │ │
│ │ modprobe nvme ← 加载存储驱动 │ │
│ │ modprobe ext4 │ │
│ │ modprobe dm-crypt │ │
│ │ │ │
│ │ cryptsetup open /dev/sda2 root ← 解锁 LUKS │ │
│ │ mount /dev/mapper/root /sysroot ← 挂载真实根 │ │
│ │ │ │
│ │ switch_root /sysroot /sbin/init │ │
│ │ ├─ chroot("/sysroot") ← 修改 fs->root │ │
│ │ └─ execve("/sbin/init") ← PID 1 替换 │ │
│ └─────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌───────────────────────────────┐
│ initramfs 内存释放 │
│ free_initrd() │
│ 控制权交给 /sbin/init │
└───────────────────────────────┘
数据结构关键点:
- initramfs 是 cpio 归档(非 ext2 镜像),
populate_rootfs解压到rootfs(tmpfs类型的super_block) switch_root:chroot修改当前进程task_struct->fs->root,execve替换 PID 1modprobe通过init_module系统调用将.ko加载到内核,module数据结构加入modules链表
块四:PID 1(systemd)启动阶段
┌─────────────────────────────────────────────────────────────────┐
│ /sbin/init → systemd main() │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 解析内核命令行 (systemd.unit=) │ │
│ │ 挂载 /sys/fs/cgroup (cgroup2) │ │
│ │ 初始化 struct Manager (unit 哈希表) │ │
│ │ 加载 /usr/lib/systemd/system/*.service │ │
│ └─────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌───────────────────────────────────────┐
│ default.target │
│ (graphical.target / multi-user) │
└───────────────────┬───────────────────┘
│
┌───────────────────────┼───────────────────────┐
│ │ │
▼ ▼ ▼
┌─────────────────┐ ┌─────────────────────┐ ┌─────────────────────┐
│ sysinit.target │ → │ basic.target │ → │ multi-user.target │
│ ┌─────────────┐│ │ ┌───────────────┐ │ │ ┌───────────────┐ │
│ │ udevd ││ │ │ D-Bus │ │ │ │ NetworkManager│ │
│ │local-fs ││ │ │ logind │ │ │ │ sshd │ │
│ │swap ││ │ │ journald │ │ │ │ cron │ │
│ │sysctl ││ │ │ timers │ │ │ │ │ │
│ └─────────────┘│ │ └───────────────┘ │ │ └───────────────┘ │
└─────────────────┘ └─────────────────────┘ └──────────┬──────────┘
│
▼
┌─────────────────────┐
│ graphical.target │
│ ┌───────────────┐ │
│ │ gdm/sddm │ │
│ │ greetd │ │
│ └───────────────┘ │
└─────────────────────┘
数据结构关键点:
- systemd
struct Manager:包含units哈希表(key=unit 名称),jobs队列(当前正在执行的操作) - unit 依赖图:
After=/Before=/Wants=/Requires=构成有向图,systemd 拓扑排序执行 - cgroup v2:
/sys/fs/cgroup/system.slice/下每个 service 有独立 cgroup 目录(cpu.max、memory.max等控制文件,systemd 通过写入这些文件应用资源限制)
块五:用户登录与会话建立
┌──────────────────────┐
│ multi-user.target │
│ 就绪 │
└──────────┬───────────┘
│
┌──────────────────────────┼──────────────────────────┐
│ │ │
▼ ▼ ▼
┌──────────────────────────┐ ┌──────────────────────────┐ ┌──────────────────────────┐
│ TTY 登录 (文本) │ │ 图形登录 (DM) │ │ SSH 登录 (远程) │
│ │ │ │ │ │
│ getty 打开 /dev/ttyN │ │ gdm/sddm/greetd 启动 │ │ systemd 启动 sshd │
│ ├─ login 验证密码 │ │ ├─ 启动 Xorg/Wayland │ │ ├─ 监听 0.0.0.0:22 │
│ │ /etc/passwd + │ │ ├─ 显示登录界面 │ │ ├─ fork() 处理连接 │
│ │ /etc/shadow │ │ ├─ PAM 认证 │ │ ├─ pty 分配 │
│ ├─ execve(/bin/bash) │ │ └─ execve(desktop) │ │ └─ execve(/bin/bash) │
│ └─ 继承 tty fd 0/1/2 │ │ systemd --user │ │ │
└──────────────────────────┘ └──────────────────────────┘ └──────────────────────────┘
│ │ │
└──────────────────────────┼──────────────────────────┘
▼
┌─────────────────────────────────────┐
│ 用户 Shell 会话 │
│ task_struct (用户态 mm_struct) │
│ 继承 stdin/stdout/stderr │
│ (指向 tty / pty 设备文件) │
│ │
│ 读取启动文件: │
│ ~/.bashrc / ~/.zshrc / config.fish│
│ ─────────────────────────────── │
│ 等待用户输入命令 │
│ fork() + execve() 执行命令 │
└─────────────────────────────────────┘
数据结构关键点:
getty使用fork()+execve("/bin/login"),login验证后fork()+execve("/bin/bash"),Shell 继承fd 0/1/2(指向/dev/ttyN)/etc/passwd:用户名:密码占位:UID:GID:描述:家目录:Shell;/etc/shadow:用户名:加密密码哈希:最后修改:...- PAM 认证:通过
/etc/pam.d/配置文件动态加载认证模块(pam_unix.so读取/etc/shadow) systemd --user:用户级 systemd 实例,管理用户会话服务(pipewire、gpg-agent等),其Manager结构独立于系统级实例,--user启动的进程task_struct->signal->user指向当前用户而非 root
五块总览(数据流链):
固件 (MBR/GPT/ESP)
↓ 加载
引导程序 (GRUB/systemd-boot)
↓ 加载内核到 0x100000 + initramfs + boot_params
内核入口 (header.S → startup_32 → startup_64 → decompress_kernel)
↓ 初始化 start_kernel (arch/mem/sched/trap)
PID 1 (kernel_init → /init → systemd)
↓ target 依赖链 (sysinit → basic → multi-user → graphical)
用户登录 (getty / DM / sshd)
↓ fork + execve
用户 Shell