Linux 系统分层架构:从硬件通电到 systemd 进程管理

Linux 系统分层架构:从硬件通电到 systemd 进程管理

一句话总结:本文梳理了 Linux 系统从硬件通电到 systemd 系统管理的完整分层架构,核心结论是内核解决"硬件抽象"和"进程运行平台"两个机制问题,systemd 解决"跑什么进程、怎么管理"的策略问题,二者职责边界清晰。

流程图

通电,从固定地址执行
加载内核+DTB到内存,跳转
创建PID 1,执行/sbin/init
按依赖图并行启动
提供IPC/日志/设备管理
svc/syscall特权指令切换
read/write/ioctl/mmap
open/read/fork/exec
硬件层 CPU/内存/Flash/GPIO
引导层 U-Boot 裸机代码
Linux 内核 驱动+进程平台
systemd PID 1 首个用户态进程
守护进程 journald/udevd/networkd
应用层 bmcweb/ipmid/shell
系统调用 用户态↔内核态 唯一入口

内容梳理

一、硬件层:硅芯片

BMC(Baseboard Management Controller - 基板管理控制器)芯片(如 ASPEED AST2500)通电后,CPU 从硬件固定的复位向量地址(通常是 SPI Flash 的 0 地址)取第一条指令执行。此时没有"内核"、没有"进程"、没有"文件"------只有裸硅。

  • CPU 内核(ARM / x86)处于最底层,上电后 PC 指针指向固件入口
  • 内存控制器(DDR Controller)尚未初始化,DRAM 不可用
  • 外设(I2C 控制器、GPIO、SPI Flash 控制器)通过物理地址映射在总线上的寄存器操作

二、引导层:U-Boot(Das U-Boot - 通用引导加载器)

U-Boot 是一个裸机程序,不依赖任何操作系统,跑在 CPU 的特权模式,负责三件事:

  1. 初始化 CPU 和 DDR 内存(芯片刚通电时内存还不能用,U-Boot 先跑在 CPU 内部 SRAM 里)
  2. 从 Flash 找到 Linux 内核镜像(Image/zImage)和设备树(DTB, Device Tree Blob - 设备树二进制)
  3. 把内核加载到内存,设置 r0/r1 寄存器,跳转过去

U-Boot 的边界:内核一旦启动,U-Boot 就从内存里消失了------它的代码段被内核覆盖,再无关联。它是一次性的。

三、Linux 内核层:两个核心职责

内核是持续运行的平台,从开机到关机一直在工作。

启动序列与 PID 初始化
复制代码
start_kernel()
  → setup_arch()            // 根据设备树初始化平台硬件
  → mm_init()               // 初始化内存管理子系统
  → sched_init()            // 初始化进程调度器
  → rest_init()
      → 手动构造 PID 0      // idle task, 每个CPU一个, CPU空闲时跑halt指令
      → kernel_thread(kernel_init)  // 创建 PID 1 (内核线程)
          → 加载驱动, mount 根文件系统
          → run_init_process("/sbin/init")
          → PID 1 从内核线程变为用户态进程 systemd
      → 创建 PID 2          // kthreadd, 后续所有内核线程由其 fork

关键纠正:PID 0 不是"进程",是内核本身的 idle task 化身,永远不退出。PID 1 是第一个用户态进程。所有后续用户态进程由 PID 1 fork,所有内核线程由 PID 2 fork。

职责一:硬件抽象(驱动)

驱动全在内核态,但通过三种接口向用户态暴露能力:

接口 例子 用户态操作
/dev/ 设备节点 /dev/i2c-3 open()ioctl(I2C_RDWR) → 内核操作 I2C 控制器寄存器
/sys/ sysfs /sys/class/hwmon/hwmon0/temp1_input cat 读 → 驱动当场读传感器寄存器 → 返回值
/proc/ procfs /proc/cpuinfo 内核汇总信息暴露

驱动分层结构:

复制代码
硬件寄存器 (物理地址 0x1e78a000)
  → 平台驱动 (i2c-aspeed.c, AST2500 I2C 控制器)
    → 驱动框架 (i2c-dev.c 字符设备 / hwmon.c sysfs 框架)
      → 暴露给用户态 (/dev/i2c-3 / /sys/class/hwmon/)
职责二:进程运行平台(调度与隔离)
  • 调度器(CFS, Completely Fair Scheduler - 完全公平调度器):单核物理 CPU 通过时间片轮流跑各个进程,一秒切换几十次,看起来像同时运行
  • MMU(Memory Management Unit - 内存管理单元):每个进程有独立的虚拟地址空间,0x400000 在进程 A 里和进程 B 里映射到完全不同的物理内存,互相不可见
系统调用:用户态和内核态的唯一合法入口

系统调用不是独立的一层,而是内核层顶部的"门"。用户态进程通过特权指令(ARM: svc / x86: syscall / 旧 x86: int 0x80)触发 CPU 异常,CPU 切换到特权模式,内核处理请求后返回,CPU 切回非特权模式。

复制代码
用户态: open("/dev/i2c-3", O_RDWR)
  → glibc 翻译 → svc #0 (ARM 特权指令)
  → CPU 切到内核态 → sys_call_table[__NR_openat]
  → 内核找到 inode → 找到驱动 open 函数 → 返回 fd
  → CPU 切回用户态 → 进程拿到 fd

四、systemd 层:系统管理

systemd 是内核启动的第一个用户态进程(PID 1),它的边界是:只管理进程,不管硬件

systemd 做 systemd 不做
成为 PID 1,是所有用户态进程的祖先 不管硬件驱动(那是内核的活)
管理服务启动顺序和依赖(DAG 依赖图并行启动) 不管理内存页表和虚拟地址(MMU 在内核里)
监控进程状态,崩溃自动重启 不调度进程(CFS 调度器在内核里)
收集日志(journald) 不暴露设备节点(udev 创建 /dev/ 但实际 IO 由内核驱动处理)
管理 cgroup 资源限制 不写驱动代码
提供基于 D-Bus 的 IPC 通信 socket 机制由内核提供
核心设计哲学
  • cgroup 集成 :每个服务属于一个 cgroup,能精确跟踪所有子进程,kill 不会漏掉 fork 出来的子进程
  • socket 激活:systemd 先创建 socket 监听,有请求才 wake up 真正服务,服务挂了不影响 socket 队列
  • 统一状态机:所有 unit(service/socket/mount/timer/device)用同一套状态机(active/inactive/failed/reloading)

五、Init 系统历史演化

SysV init(1983 年)

通过 /etc/rc.d/rc?.d/ 下的 shell 脚本按编号串行启动服务。

  • 痛点:完全串行、依赖靠数字编号暗示、启动完就不管、没有 cgroup、shell 脚本脆弱、碎片化严重
Upstart(2006 年,Ubuntu 主导)

引入事件驱动启动,"网络就绪"事件可同时触发 sshd 和 httpd 并行启动,支持崩溃自动重启。

  • 遗留问题:依赖表达仍模糊、无 cgroup 集成、配置文件与 Debian/Fedora 不通用、社区分裂
systemd(2010 年,Red Hat)

不是单纯的 init 重写,而是重新定义了 Linux 系统管理层的设计。整合了 init + inetd + crond + syslogd + udev + ConsoleKit + ...,用统一设计哲学替代一堆零散组件。

总结与展望

总结

  • 内核提供"机制"(硬件抽象 + 进程平台),systemd 提供"策略"(跑什么、怎么管)
  • PID 0 是内核 idle task 化身而非进程,PID 1 是第一个用户态进程,PID 2 创建所有内核线程
  • 驱动全在内核态,通过 /dev、/sys、/proc 三种接口向用户态暴露能力
  • 系统调用是穿越用户态/内核态边界的唯一合法入口,通过 CPU 特权指令实现
  • systemd 的统一整合思路用 cgroup + socket 激活 + 统一状态机解决 SysV init 的根本缺陷

展望/趋势

  • systemd 在嵌入式领域的渗透:OpenBMC 全面使用 systemd 管理 BMC 服务,其 socket 激活对资源受限芯片尤为有利(按需启动,省内存)
  • 引导层简化:U-Boot 正在被 Linux 内核自带的直接启动方案(如 LinuxBoot)部分替代,减少不必要的引导代码
  • 内核态/用户态边界演变:eBPF 模糊了这条边界------允许用户编写的安全代码在内核态运行,未来设备驱动的一部分逻辑可能被推到用户态(DPDK/SPDK 思路)
  • systemd 的争议与接受:systemd 的"大一统"哲学至今有争议,但它解决了 Linux 生态碎片化的实际问题,已成为事实标准

深度思考:大一统 vs 小零件的哲学之争

争议不浮于表面,根源在耦合强度

systemd 争议看似是"技术好不好用"的争论,但本质上是两种软件设计哲学的对立。理解这个对立,比记住 systemd 本身更重要。

两种哲学定义

A. 大一统(Monolithic / Integrated --- 单体整合设计)

把相关的事放在一起做,统一设计,统一接口。

systemd 是典型。另一个典型是 Linux 内核本身------驱动、文件系统、网络栈全在内核态同一个地址空间里跑。PostgreSQL(PostgreSQL --- 关系型数据库)也是一个引擎统一处理关系数据、JSON、时序和向量存储。

B. 小零件(Modular / Composable --- 模块化可组合设计)

每个工具只做一件事,通过标准接口组合成复杂系统。

Unix 管道的极致表达:ls | grep | sort | wc,每个工具只做一件事,拼接完成复杂任务。微服务架构也是这个思路------几百个独立的小服务各自部署,通过 API 互相调用。

两种哲学的优缺点对比

维度 大一统 小零件
调试排查 同一进程内看调用堆栈,边界清晰 问题跨越几十个服务,需要分布式追踪
理解成本 新人要面对一个巨大的整体才能上手 每个零件简单,但零件间的配合规则同样要学,总认知负担未必小
接口稳定性 内部可以快速重构,私有边界改动自由 零件靠公开接口协作,一改接口就破坏兼容性,不敢轻易动
创新速度 所有改动要经过核心团队,遗留决策会逐渐让系统变重 每个零件独立演进,坏掉只影响自己;但版本兼容矩阵会慢慢爆炸
故障半径 核心崩了全崩,边界少所以故障源少 一个零件崩了其他还能跑,但零件太多每个都可能是故障源
标准化 一个仓库风格统一 每个团队按自己的偏好来,生态碎片化
强制绑定 用 A 就绑死 B/C/D,想换代价大 按需自选零件,但也意味着你自己负责组装和验证兼容性

谁在什么场景赢了

场景 赢家 原因
操作系统内核(Linux vs 微内核) 大一统 内核组件间调用极频繁,IPC(Inter-Process Communication --- 进程间通信)开销承受不起;微内核(Minix / GNU Hurd)在这个领域失败了
init 系统(systemd vs SysV 脚本) 大一统 碎片化 shell 脚本各自为政,统一管理才解决依赖编排和资源控制的刚需
Web 后端(微服务 vs 单体) 来回摇摆 当前微服务占优(独立扩缩容),但拆太细后调试和事务一致性成新灾难;近年"Modular Monolith"(逻辑模块化但部署一体)开始回归
Unix 命令行(pipe 组合) 小零件 至今未被挑战,设计典范
数据库(PostgreSQL vs 专用存储引擎) 大一统 一个引擎覆盖多种数据类型,运维成本低
前端框架(全栈 vs 微前端) 来回摇摆 目前全栈框架在回归

本质规律:耦合强度决定正确选择

识别系统组件之间的耦合强度,是判断用哪种哲学的第一性原理。

复制代码
紧密耦合(调用频繁、共享状态、低延迟要求)
  → 大一统更合适
  → 拆开反而引入 IPC 开销和状态同步问题
  → 例:内核、数据库引擎、init 系统

松散耦合(独立运行、偶尔通信、对延迟不敏感)
  → 小零件更合适
  → 硬绑在一起反而拖慢迭代、放大故障半径
  → 例:命令行管道、微服务、浏览器插件

systemd 争议的真正根,在于 Linux 系统管理的各个组件(日志、网络、设备管理)到底是"紧密耦合"还是"松散耦合"------反对派认为它们是松散的(不该绑在一起),支持派认为它们实际上是紧密的(进程日志必然和进程生命周期挂钩,网络状态必然影响服务启动依赖)。事实证明支持派的判断更准确。

我从中得出的判断框架

  1. 不要先站队,先问耦合强度。遇到"大一统还是拆零件"的选择,不做风格偏好判断,先画组件间的调用关系图,看频率、看共享状态、看延迟要求
  2. 在错误的地方用错误的哲学是灾难。把 Linux 内核拆成微服务是灾难,把命令行工具写成大一统框架也是灾难
  3. 两种哲学不是对错问题,是场景适配问题。一个人的"单体怪兽"是另一个人的"内部重构空间";一个人的"灵活组合"是另一个人的"集成噩梦"
  4. 争论本身常常浮于表面。systemd 十年论战中,喊"违反 Unix 哲学"的人和喊"解决了真实问题"的人,讨论的经常不是同一层------前者在谈美学,后者在谈实践
相关推荐
王二端茶倒水14 分钟前
从千兆到万兆:小区、园区、酒店网络运营该怎么升级?
架构
喵个咪15 分钟前
技术复盘:基于 go-wind-cms 的官网+商城双业务渐进拆分实战
后端·架构·go
ZengLiangYi27 分钟前
批量导入 1000 条对话的性能优化实战
javascript·后端·架构
猪脚踏浪1 小时前
linux 拷贝文件或目录到指定的位置
linux
大树8817 小时前
金刚石散热越强,管路越先见顶
大数据·运维·服务器·人工智能·ai
摇滚侠17 小时前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
霸道流氓气质17 小时前
领域驱动设计(DDD)在 Spring Boot 微服务中的实践指南
运维·spring boot·微服务
bush417 小时前
嵌入式linux学习记录十四、术语
linux·嵌入式
东方佑17 小时前
FRSM 规模效应与架构对比补充报告
架构
载数而行52017 小时前
Linux 11 动态监控指令top
linux