Linux 学习笔记(一):Linux 的前世今生、系统架构与发展脉络
第一课:Linux 导论——前世今生、系统架构与发展脉络
本课定位:建立全局视角。暂时不追求记住所有细节,而是先弄清 Linux 从哪里来、由哪些部分组成、各部分怎样协作,以及嵌入式 Linux 开发究竟在做什么。
1. 本课目标
学完本课后,应当能够回答以下问题:
- Linux 是内核、操作系统,还是发行版?
- Unix、GNU、Linux 三者是什么关系?
- Linux 为什么能从个人项目发展成现代计算基础设施?
- 用户程序如何通过系统调用使用 CPU、内存、文件和网络?
- Linux 内核包含哪些主要子系统?
- 一块 ARM 开发板从上电到出现登录终端经历了什么?
- 主线内核、长期支持内核和厂商 BSP 有什么区别?
- 将 Linux 移植到开发板,真正需要修改哪些层?
2. Linux 到底是什么
“Linux”在不同语境中有三种常见含义,学习时必须区分。
2.1 严格含义:Linux 是内核
Linux kernel 是操作系统的核心。它运行在 CPU 的特权级中,负责管理:
- 进程与线程;
- CPU 调度;
- 物理内存和虚拟内存;
- 文件系统;
- 设备与驱动;
- 网络协议栈;
- 进程间通信;
- 权限、安全和资源隔离。
内核本身并不直接提供常用的文本编辑器、编译器、Shell、桌面环境和软件包管理器。只有内核,通常还不能构成一个方便使用的完整系统。
2.2 广义含义:基于 Linux 内核的操作系统
日常所说“我安装了 Linux”,通常是指一个完整系统,它往往包含:
- Linux 内核;
- C 标准库,例如 glibc 或 musl;
- Shell,例如 Bash、Zsh;
- 基础命令,例如
ls、cp、grep; - 初始化与服务管理系统,例如 systemd、OpenRC、BusyBox init;
- 软件包管理工具;
- 编译器、网络工具及其他用户空间程序;
- 可选的图形系统和桌面环境。
2.3 发行版:把组件整合成可安装、可维护的产品
Ubuntu、Debian、Fedora、Arch Linux 等是 Linux 发行版。发行版维护者会决定:
- 使用哪个内核版本和补丁集;
- 默认安装哪些软件;
- 软件包格式和仓库;
- 目录布局和系统默认配置;
- 安全更新策略;
- 发布周期和支持周期。
因此可以建立第一个重要公式:
Linux 系统 ≈ Linux 内核 + 用户空间 + 配置 + 软件包生态 + 维护策略
“GNU/Linux”强调很多桌面和服务器发行版同时大量使用 GNU 工具。实际交流中,“Linux”作为完整系统的简称已经非常普遍。技术讨论时,只要明确当前指内核还是完整系统即可。
3. Linux 的前世:从 Unix 说起
3.1 Multics:宏大系统带来的启发
20 世纪 60 年代,MIT、通用电气和贝尔实验室合作开发 Multics。它试图构建一个支持多用户、分时、权限控制和高可靠性的通用计算系统。
Multics 非常超前,但设计复杂、开发成本高。贝尔实验室后来退出项目。参与者 Ken Thompson、Dennis Ritchie 等人从中吸收了分时、多用户、层次文件系统等思想,同时希望做出一个更小、更清晰、更实用的系统。
3.2 Unix 的诞生
1969 年,Ken Thompson 在贝尔实验室使用 PDP-7 计算机实现了一个早期系统。1970 年左右,该系统开始被称为 Unix。
早期 Unix 使用汇编语言编写。Dennis Ritchie 设计并发展了 C 语言,Unix 随后在 1973 年前后大部分改用 C 重写。这件事意义重大:
- 系统不再和某一种 CPU 汇编紧密绑定;
- 移植到新硬件的成本显著下降;
- 操作系统可以用相对高级、又能贴近硬件的语言开发;
- C 与 Unix 由此相互推动,成为系统软件的重要基础。
3.3 Unix 哲学
Unix 不仅是一套代码,也形成了一组影响深远的工程思想:
- 一个程序做好一件事;
- 程序之间使用清晰接口组合;
- 文本是常见的数据交换格式;
- 用管道把简单工具组合成复杂流程;
- 机制与策略尽量分离;
- 尽量让系统行为可观察、可自动化。
例如:
journalctl -k | grep -i usb | less
这里多个程序通过管道组合:读取内核日志、筛选 USB 信息、分页查看。每个工具都不庞大,但组合能力很强。
“一切皆文件”也是 Unix/Linux 常见概括。更准确地说,Linux 尽量用文件描述符和统一的读写接口表示普通文件、终端、管道、套接字和部分设备。它是一种接口统一思想,并不是说所有内核对象在本质上都是磁盘文件。
3.4 Unix 的分化与标准化
Unix 后来形成了多个重要分支:
- 加州大学伯克利分校发展的 BSD;
- AT&T 推进的 System V;
- 各硬件厂商维护的商业 Unix,例如 AIX、HP-UX、Solaris。
不同 Unix 之间出现接口差异,软件移植变得困难。POSIX 标准因此逐步形成,用于规范进程、文件、信号、线程、Shell 和基础工具等接口。
Linux 不是原始 Unix 源代码的直接后代,也没有使用 AT&T Unix 代码,但它深受 Unix 设计影响,并实现了大量 POSIX 接口,所以通常称为“类 Unix 系统”。
4. GNU:自由操作系统的用户空间基础
4.1 GNU 项目的目标
1983 年,Richard Stallman 宣布启动 GNU 项目,目标是创建一套用户可以自由使用、研究、修改和分享的类 Unix 操作系统。GNU 是递归缩写:GNU’s Not Unix。
1985 年,自由软件基金会 FSF 成立。这里的“自由”重点是软件自由,不等同于价格必须为零。
4.2 GNU 提供了什么
GNU 项目逐步开发了大量关键组件:
- GCC 编译器工具链;
- GDB 调试器;
- glibc C 标准库;
- Bash Shell;
- GNU Binutils,例如链接器
ld、汇编器as、目标文件工具; - Coreutils,例如
ls、cp、mv、cat; - Make 构建工具;
- Emacs 编辑器;
- GPL 自由软件许可证。
到 1990 年代初,GNU 已拥有较完整的用户空间,但 GNU 自己的 Hurd 内核尚未达到广泛实用的状态。Linux 内核出现后,GNU 用户空间与 Linux 内核很快结合,构成了可用的自由类 Unix 系统。
4.3 GPL 的作用
GNU General Public License 是 copyleft 许可证。粗略理解,它允许用户运行、研究、修改和再发布软件;当基于 GPL 代码发布衍生作品时,需要继续提供相应源码并保留同类自由。
许可证细节涉及法律边界,不能只靠一句话概括。但从 Linux 发展史看,GPLv2 使多家公司和个人可以共同改进内核,同时要求分发相关修改时履行源码义务。这降低了某一家公司独占公共内核成果的可能性。
5. Linux 的诞生与成长
5.1 Minix 的背景
1987 年,Andrew S. Tanenbaum 发布 Minix。它主要用于操作系统教学,采用微内核思想,并提供可阅读的源代码。Linus Torvalds 在学习和使用 Minix 的过程中,希望更充分地利用自己的 80386 PC,也希望获得一个更适合实际使用和自由改造的系统。
5.2 1991:一个个人项目公开了
1991 年 8 月 25 日,芬兰学生 Linus Torvalds 在 Usenet 新闻组发布消息,介绍自己正在为 386/486 AT 兼容机编写一个自由操作系统内核。他最初称其为“只是一个爱好”,并没有预料它后来会发展到今天的规模。
1991 年 9 月,早期 Linux 0.01 源码发布。最初版本功能有限,只支持特定 x86 硬件,开发工具和用户空间则大量来自 GNU。
1992 年前后,Linux 内核转为 GNU GPLv2 许可。互联网协作、GNU 工具链、GPL 许可与低成本 PC 共同为 Linux 的快速成长创造了条件。
5.3 关键时间线
| 时间 | 事件 | 意义 |
|---|---|---|
| 1969 | Unix 早期系统诞生 | 奠定类 Unix 系统思想 |
| 1973 | Unix 大部分以 C 重写 | 提高可移植性,推动 C 成为系统语言 |
| 1983 | GNU 项目启动 | 建设自由的类 Unix 用户空间 |
| 1987 | Minix 发布 | 操作系统教学与早期 PC 类 Unix 实践 |
| 1991 | Linux 0.01 发布 | Linux 内核项目开始公开发展 |
| 1992 | Linux 采用 GPLv2 | 形成开放协作与共享改进基础 |
| 1993 | Debian、Slackware 等早期发行版出现 | 完整 Linux 系统更容易安装和维护 |
| 1994 | Linux 1.0 发布 | 网络等核心功能趋于成熟 |
| 1996 | Linux 2.0 发布 | 支持 SMP 等能力,企鹅 Tux 成为标志 |
| 2003 | Linux 2.6 发布 | 扩展性、调度、驱动和嵌入式支持持续增强 |
| 2005 | Git 为 Linux 内核开发而创建 | 支撑大规模分布式协作 |
| 2008 | Android 1.0 发布 | Linux 内核大规模进入移动设备 |
| 2011 | 版本号进入 3.x | 版本号策略简化,不表示架构突然重写 |
| 2015 | 版本号进入 4.x | 内核持续渐进演进 |
| 2019 | 版本号进入 5.x | 延续成熟的开发模型 |
| 2022 | 版本号进入 6.x | Linux 继续在硬件、性能、安全方面演进 |
版本号的大整数变化通常不是“全新一代系统”的标志。Linux 内核采用持续演进方式,真正重要的是具体版本包含哪些功能、修复和硬件支持。
5.4 发行版生态形成
早期用户需要手工组合内核、GNU 工具与其他软件。发行版逐渐把这些内容打包,并加入安装器、依赖管理、软件仓库和安全维护。
重要发行版及其影响包括:
- Debian:强调社区治理、自由软件原则和稳定的软件包体系;
- Slackware:早期重要发行版,风格简洁传统;
- Red Hat/Fedora:对企业 Linux、RPM 生态和上游开发影响深远;
- SUSE/openSUSE:在企业和欧洲市场有长期影响;
- Ubuntu:基于 Debian,改善安装、桌面与定期发布体验;
- Arch Linux:滚动更新、简洁透明,适合希望主动理解系统的用户;
- Gentoo:以源码构建和高度可配置著称;
- Alpine Linux:轻量,常使用 musl 和 BusyBox,在容器中常见;
- Android:使用 Linux 内核,但用户空间与典型 GNU/Linux 发行版明显不同;
- OpenWrt:面向路由器和嵌入式网络设备;
- Buildroot、Yocto Project:它们更准确地说是构建嵌入式 Linux 系统的工具体系,而不是普通桌面发行版。
5.5 Linux 为什么成功
Linux 的成功不是单一技术因素造成的,而是多种条件叠加:
- 开放源码与协作许可:个人、学校和企业都能检查并改进代码。
- Unix/POSIX 接口基础:已有大量知识、工具和应用可以迁移。
- GNU 工具链及时成熟:编译器、Shell、库和命令已经可用。
- 互联网兴起:邮件列表、补丁和源码可以全球协作。
- 低成本硬件普及:普通 PC 就能学习和贡献内核。
- 可移植性:Linux 后来支持 x86、ARM、RISC-V、PowerPC 等多种架构。
- 工程治理逐渐成熟:维护者层级、代码审查、自动测试和稳定分支共同支撑大项目。
- 企业投入:云计算、服务器、芯片和设备厂商都需要一个共享内核基础。
- 不绑定单一产品形态:同一个内核可用于服务器、手机、路由器、汽车和开发板。
6. Linux 系统的总体架构
先建立一张自上而下的地图:
┌──────────────────────────────────────────────────────┐
│ 用户应用:编辑器、编译器、浏览器、服务器、你的程序 │
├──────────────────────────────────────────────────────┤
│ Shell / 系统服务 / 桌面 / 容器运行时 / 基础命令 │
├──────────────────────────────────────────────────────┤
│ 用户空间库:glibc、musl、libstdc++、其他运行库 │
├──────────────── 系统调用边界 ────────────────────────┤
│ Linux 内核 │
│ 进程调度 | 内存管理 | VFS | 驱动 | 网络 | IPC | 安全 │
├──────────────────────────────────────────────────────┤
│ 硬件:CPU、RAM、存储、网卡、USB、GPIO、显示控制器等 │
└──────────────────────────────────────────────────────┘
6.1 用户空间与内核空间
现代 CPU 提供不同特权级。Linux 把系统大致分成两类执行环境:
- 用户空间:普通应用运行的位置,权限受限,不能随意访问物理内存或直接控制硬件;
- 内核空间:内核运行的位置,拥有更高权限,可以管理页表、中断、设备和调度。
这种隔离非常关键。若每个程序都能直接改写任意内存和硬件寄存器,一个程序的错误就可能破坏整个系统。
在 AArch64 中,Linux 常见地让用户程序运行在 EL0,内核运行在 EL1。更高异常级可能由固件或虚拟化层使用。x86 则常用 Ring 3 表示用户态、Ring 0 表示内核态。
6.2 系统调用:用户程序进入内核的正式入口
应用需要打开文件、创建进程、申请内存或发送网络数据时,通过系统调用请求内核服务。常见系统调用包括:
openat():打开文件;read()、write():读写文件描述符;mmap():建立内存映射;clone():创建进程或线程;execve():装入并执行新程序;socket()、connect():使用网络;ioctl():对设备或特殊对象执行控制操作。
普通 C 程序通常不是手写 CPU 陷入指令,而是调用 libc 包装函数。例如 printf() 先处理格式化和用户态缓冲,最终可能通过 write() 系统调用输出。
应用函数 → C 库包装 → 系统调用 → 内核子系统 → 驱动/硬件
系统调用与普通函数调用不同。它会跨越权限边界,内核必须验证地址、长度、权限和对象状态。
6.3 Linux 是单体内核,但支持模块化
Linux 通常归类为单体内核:调度、内存、文件系统、网络和大量驱动都运行在同一个内核地址空间中。它不是把多数服务拆到独立用户态进程的纯微内核。
“单体”不等于“所有代码编成一个不可拆文件”。Linux 支持可加载内核模块:
- 功能可以直接内建进内核镜像;
- 也可以编译成
.ko模块,在运行时加载; - 模块仍然运行在内核空间,出错可能导致整个系统崩溃。
查看模块的常用命令:
lsmod
modinfo 模块名
sudo modprobe 模块名
7. Linux 内核的主要子系统
7.1 进程、线程与调度
Linux 用任务来描述可调度执行实体。一个程序运行后成为进程;同一进程可以拥有多个线程,它们共享地址空间和部分资源。
内核需要解决:
- 哪个任务下一刻使用哪个 CPU;
- 高优先级和实时任务如何响应;
- 多核之间如何负载均衡;
- 阻塞任务何时睡眠、何时被唤醒;
- 如何保存和恢复任务的寄存器上下文。
常见概念:
- PID:进程标识符;
- 上下文切换:CPU 从一个任务切换到另一个任务;
- 调度类:普通、公平、实时、截止期限等不同策略;
- 进程状态:运行、可运行、可中断睡眠、不可中断睡眠、停止、僵尸等;
- nice 值:影响普通任务的调度权重,不是绝对优先级承诺;
- CPU affinity:限制任务可以在哪些 CPU 上运行。
观察命令:
ps -eo pid,ppid,stat,ni,comm
top
cat /proc/loadavg
负载平均值不仅统计“正在使用 CPU”的任务,也涉及处于特定不可中断等待状态的任务,因此不能简单等同于 CPU 使用率。
7.2 虚拟内存
每个用户进程看到的是自己的虚拟地址空间。CPU 的 MMU 根据页表把虚拟地址翻译为物理地址。这样可以实现:
- 进程之间的地址隔离;
- 内存保护和权限检查;
- 程序使用连续虚拟地址,而物理页不必连续;
- 共享库和共享内存;
- 文件映射;
- 按需分配;
- 写时复制;
- 交换空间。
进程的典型虚拟地址区域可能包括:
低地址
程序代码段 text
只读数据 rodata
已初始化数据 data
未初始化数据 bss
堆 heap 向高地址扩展(概念模型)
mmap 映射区 共享库、文件映射、匿名映射
栈 stack 常向低地址扩展(概念模型)
高地址
这只是帮助理解的简化图。实际布局受 CPU 架构、ABI、地址随机化和动态链接器影响。
Linux 内存管理还包括:
- 页分配器;
- slab/slub 小对象分配器;
- 页缓存;
- 内存回收;
- OOM 处理;
- NUMA 策略;
- huge pages;
- 内存控制组。
“空闲内存很少”不一定代表内存不足。Linux 会利用闲置内存做缓存,并可在应用需要时回收。观察时应综合 MemAvailable、交换活动、回收压力和 OOM 记录。
free -h
cat /proc/meminfo
cat /proc/pressure/memory
7.3 VFS 与文件系统
VFS 是虚拟文件系统层。它向上提供统一接口,向下适配 ext4、XFS、Btrfs、F2FS、NFS、procfs、sysfs 等不同文件系统。
几个重要对象:
- inode:描述文件对象的元数据和底层操作,不等同于文件名;
- dentry:目录项和路径查找缓存,把名字关联到 inode;
- file:一次已打开文件实例,保存偏移量、标志等状态;
- file descriptor:进程中的小整数,指向已打开对象。
同一个 inode 可以通过硬链接拥有多个名字。多个进程也可以打开同一个文件,并拥有不同或共享的打开状态。
特殊虚拟文件系统很重要:
/proc:进程和部分内核状态;/sys:设备模型、总线、驱动和内核对象;/dev:设备节点;tmpfs:以内存为主要后端的临时文件系统;cgroup2:资源控制层次。
7.4 设备模型与驱动
驱动是内核理解和控制硬件的代码。Linux 设备模型把硬件组织为总线、设备、驱动、类等对象,并通过 sysfs 暴露部分关系。
常见总线与设备类型:
- platform device:SoC 内部不可自动枚举的外设常用;
- PCI/PCIe:可枚举高速设备;
- USB:可枚举外设;
- I2C、SPI:嵌入式低速外设总线;
- UART:串口;
- GPIO:通用输入输出;
- MMC/SD/SDIO:存储卡或无线模块等;
- MIPI DSI/CSI:显示和摄像头相关接口。
驱动通常处理:
- 寄存器和内存映射 I/O;
- 中断;
- DMA;
- 时钟、复位、电源域;
- pinctrl 引脚复用;
- 设备与用户空间之间的接口;
- 挂起、恢复和运行时电源管理。
7.5 设备树
ARM SoC 上大量硬件不能像 PCI 那样自动发现。设备树用于描述“板上有什么硬件、地址在哪里、如何连接、使用哪些资源”。
常见文件:
.dts:设备树源文件;.dtsi:可复用的包含文件;.dtb:编译后的设备树二进制;- overlay:对基础设备树进行增量修改的机制,使用方式依平台而异。
设备树描述硬件,不应塞入本可由通用驱动逻辑决定的策略。驱动读取 compatible 等属性,与能处理该设备的驱动匹配。
简化示例:
uart2: serial@fe660000 {
compatible = "vendor,soc-uart";
reg = <0x0 0xfe660000 0x0 0x100>;
interrupts = <0 116 4>;
status = "disabled";
};
&uart2 {
pinctrl-names = "default";
pinctrl-0 = <&uart2m0_xfer>;
status = "okay";
};
示例中的地址、属性和中断号只是结构演示,不能直接用于我们的开发板。
7.6 网络协议栈
Linux 内核实现从套接字到网卡的网络路径,包括:
- socket API;
- TCP、UDP、IP、ICMP;
- IPv4 与 IPv6;
- 路由;
- 邻居子系统;
- 网络设备层;
- 防火墙与包过滤;
- 流量控制;
- bridge、VLAN、bonding、tunnel;
- 网络命名空间。
发送数据的简化路径:
应用 write/send
→ socket
→ TCP/UDP
→ IP 与路由
→ qdisc
→ 网络设备驱动
→ DMA/网卡
接收路径大体反向,但还会涉及中断、NAPI、软中断和协议处理。
7.7 IPC、同步与信号
进程间通信方式包括:
- 管道和命名管道;
- Unix domain socket;
- TCP/UDP socket;
- 共享内存;
- 消息队列;
- futex;
- 信号;
- eventfd、timerfd、signalfd 等文件描述符接口。
信号是一种异步通知机制。SIGKILL 和 SIGSTOP 不能被普通进程捕获、阻塞或忽略;其他信号通常可以按规则处理。
7.8 权限与安全
传统 Unix 权限模型包含:
- 用户 UID;
- 用户组 GID;
- 文件的 owner/group/other 权限位;
- 特殊位 setuid、setgid、sticky;
- root 用户。
现代 Linux 又加入:
- capabilities:把 root 的庞大权限拆成更细粒度能力;
- ACL:比传统三组权限更细;
- LSM:Linux Security Modules 框架;
- SELinux、AppArmor 等强制访问控制方案;
- seccomp:限制进程可调用的系统调用;
- namespaces:隔离进程视图;
- cgroups:限制和统计资源;
- 内核加固、模块签名、安全启动等机制。
容器主要利用 namespaces、cgroups、capabilities、联合文件系统等 Linux 能力。容器通常共享宿主机内核,所以它不是硬件虚拟机,也不能天然提供与虚拟机完全相同的隔离边界。
8. 用户空间由什么组成
8.1 C 标准库
glibc、musl 等 C 库提供:
- ISO C 标准函数;
- POSIX 接口;
- 系统调用包装;
- 动态链接器;
- 线程、名称解析、区域设置等运行时能力。
应用二进制能否运行,不只取决于 CPU 架构,也取决于 ABI、动态链接器路径、库版本和依赖。
8.2 Shell 不是内核
Shell 是普通用户空间程序,负责解析命令、展开变量、设置重定向、创建管道、启动程序和管理作业。
command_a < input.txt | command_b > output.txt
Shell 解析这行文本后,会调用 pipe()、fork()/clone()、dup2()、execve()、wait() 等接口组织多个进程。真正执行权限隔离、管道传输和进程调度的是内核。
8.3 PID 1 与服务管理
内核启动第一个用户空间进程后,该进程通常获得 PID 1。它负责启动后续服务、处理孤儿进程,并参与关机流程。
常见实现包括:
- systemd;
- SysV init;
- OpenRC;
- BusyBox init;
- 面向容器或专用系统的极简 init。
systemd 不属于 Linux 内核。它是常见的用户空间系统与服务管理器。
8.4 BusyBox
BusyBox 把许多常用命令实现集中在一个可执行文件中,通过不同命令名调用不同功能。它体积小、依赖少,在 initramfs、路由器和嵌入式根文件系统中非常常见。
BusyBox 命令与 GNU 同名工具的选项不一定完全一致。编写跨嵌入式系统脚本时,不能默认所有 GNU 扩展都存在。
8.5 图形系统是可选层
Linux 可以完全没有图形界面。图形栈可能包含 DRM/KMS、Mesa、Wayland/X11、窗口合成器和桌面环境。服务器与嵌入式设备经常只运行命令行或专用 UI。
内核与桌面环境不是一回事。GNOME、KDE Plasma 等是用户空间桌面系统。
9. 从上电到登录:Linux 启动链路
不同 SoC 的细节不同,但 ARM 开发板常见链路可以概括为:
上电/复位
↓
芯片 Boot ROM
↓
第一阶段加载程序(TPL/SPL 或厂商 loader)
↓
固件与第二阶段 Bootloader(可能含 TF-A、U-Boot)
↓
加载 Linux kernel + DTB + 可选 initramfs
↓
内核解压、初始化 CPU/内存/中断/驱动
↓
挂载临时或真实根文件系统
↓
启动 PID 1
↓
启动系统服务、终端、网络和应用
9.1 Boot ROM
Boot ROM 固化在 SoC 内部,由芯片厂商提供。它会根据启动引脚、熔丝或内部策略,从 eMMC、SD、SPI Flash、USB 等介质寻找下一阶段代码。
Boot ROM 能做的事情有限,通常只初始化最基础硬件并验证或加载小型启动代码。
9.2 SPL/TPL 与 DRAM 初始化
片上 SRAM 很小,而完整 U-Boot 和内核通常需要 DRAM。早期加载阶段的重要任务是初始化时钟、引脚和 DRAM,然后把更大程序载入内存。
厂商提供的 DDR 初始化代码或训练参数经常是板级启动的关键,也可能是厂商 BSP 中最难替换的部分之一。
9.3 U-Boot
U-Boot 是嵌入式系统常见 Bootloader。它可以:
- 从存储或网络读取内核、设备树和 initramfs;
- 设置内核命令行;
- 选择启动槽或恢复系统;
- 提供串口命令行;
- 读写环境变量;
- 执行启动脚本;
- 在某些平台参与固件更新。
Bootloader 不等于 Linux。它在 Linux 内核接管硬件之前运行。
9.4 内核启动
内核获得控制权后会建立自身运行环境,初始化调度器、内存管理、IRQ、时钟、设备模型和驱动,并根据内核命令行寻找根文件系统。
串口日志是开发板启动排错的第一现场。常见参数包括 console=、root=、rootfstype=、init= 等,但具体值必须根据板卡和存储布局确定。
9.5 initramfs 与根文件系统
initramfs 是内核启动早期可使用的内存文件系统。它可以装载根存储所需驱动、解密磁盘、组装 RAID、执行恢复逻辑,然后切换到真实 rootfs。
嵌入式设备也可以直接把小型 initramfs 当作最终系统,或直接挂载 eMMC/SD/NAND 上的根文件系统。
根文件系统 rootfs 是以 / 为根的用户空间目录树。它包含程序、库、配置、设备节点挂载点和运行目录等。rootfs 不是内核镜像。
10. Linux 目录树的基本角色
Linux 没有 Windows 式盘符。所有已挂载文件系统共同组成从 / 开始的一棵树。
| 路径 | 常见用途 |
|---|---|
/boot |
内核、initramfs、Bootloader 相关文件,具体布局依系统而异 |
/dev |
设备节点 |
/etc |
系统级配置 |
/home |
普通用户主目录 |
/proc |
进程与内核状态的虚拟文件系统 |
/sys |
设备模型与内核对象的虚拟文件系统 |
/run |
本次启动期间的运行时状态 |
/tmp |
临时文件 |
/usr |
大量用户空间程序、库和共享数据 |
/var |
经常变化的数据,如日志、缓存、队列 |
/mnt、/media |
临时或可移动文件系统的常见挂载点 |
现代发行版常采用 merged /usr 布局,/bin、/sbin、/lib 可能是指向 /usr 对应目录的符号链接。不要把某个历史目录布局误认为所有 Linux 系统都必须完全相同。
11. Linux 内核如何开发和发布
11.1 上游主线
Linux 内核在 kernel.org 及其 Git 仓库中协作开发。代码按子系统由多层维护者审查,再逐步汇入 Linus Torvalds 的主线。
一个典型开发周期包含:
- 新版本发布后开启合并窗口;
- 维护者提交已准备好的新功能;
- 发布多个
-rc候选版本; - 集中测试和修复回归;
- 发布新的正式版本。
Linux 内核开发不是把所有代码直接发给 Linus。绝大多数补丁先经过具体子系统维护者和邮件列表评审。
11.2 Stable 与 LTS
- mainline:当前主线开发;
- stable:正式版本后的重要修复分支;
- LTS:被选中进行较长期维护的稳定内核系列;
- 发行版内核:发行版在某个基础版本上加入补丁和配置;
- 厂商 BSP 内核:芯片或板卡厂商维护的硬件支持版本。
LTS 不表示永远维护,也不表示每个厂商分支都能自动获得全部上游修复。必须确认具体维护期限、补丁来源和厂商同步策略。
11.3 上游与厂商 BSP
厂商 BSP 往往能最快支持新 SoC 的所有功能,但可能存在:
- 基于较旧内核;
- 大量未上游补丁;
- 驱动质量和接口风格不统一;
- 二进制固件或闭源组件;
- 更新安全修复困难;
- 与特定 Android 版本深度耦合。
主线内核通常拥有更好的长期维护性、代码审查和通用生态,但新芯片的某些功能可能尚未支持完整。
我们后续会同时问两个问题:
- 怎样用厂商 BSP 先让板子完整工作?
- 怎样减少厂商绑定,逐步靠近上游 Linux?
这比简单地认为“版本号越新越好”更符合真实工程。
12. 嵌入式 Linux 与桌面 Linux 有何异同
二者使用相同的 Linux 核心概念:进程、虚拟内存、VFS、驱动、网络、权限和系统调用都存在。但嵌入式系统通常更关注:
- 启动时间;
- 存储与内存占用;
- 交叉编译;
- 只读根文件系统;
- 断电可靠性;
- 看门狗;
- OTA 与 A/B 更新;
- 硬件驱动和设备树;
- 实时性与低延迟;
- 功耗和热管理;
- 安全启动与固件签名;
- 长期维护和供应链稳定性。
12.1 交叉编译
开发主机和目标板 CPU 架构不同,例如:
开发主机:x86_64 Linux
目标开发板:AArch64 Linux
此时在主机上运行 AArch64 交叉编译器,生成供目标板执行的二进制:
aarch64-linux-gnu-gcc
└──── target ────┘
工具链需要匹配目标 ABI、C 库与动态链接环境。能生成 AArch64 指令并不自动代表程序能在任意 AArch64 根文件系统运行。
12.2 BSP 是什么
BSP 即 Board Support Package,通常包含:
- Bootloader;
- Linux 内核及补丁;
- 设备树;
- 驱动;
- 固件;
- 交叉工具链或构建配置;
- 根文件系统构建方案;
- 烧录、打包与升级工具;
- 板级示例和文档。
不同厂商对“SDK”和“BSP”的命名边界并不一致。拿到泰山派资料后,我们要先检查目录和构建脚本,而不是仅凭名称判断内容。
12.3 “移植 Linux”实际在做什么
把 Linux 带到新板卡通常不是从零编写操作系统,而是让现有各层适配硬件:
- 确认 Boot ROM 支持的启动介质与镜像格式;
- 让早期固件和 Bootloader 初始化 DRAM、时钟与存储;
- 配置或修改内核;
- 编写或调整设备树;
- 补充缺失驱动;
- 构建 rootfs;
- 设计分区、打包和烧录流程;
- 验证串口、存储、网络、USB、显示等外设;
- 完成更新、恢复、安全和量产策略。
真正的难点往往不是“编译成功”,而是硬件资料、启动链、驱动依赖、时序、电源管理、稳定性和长期维护。
13. 现代 Linux 开发工具与方法
我们的路线以 Linux 为开发主机,并优先采用可复现、可自动化、可观察的工具。
13.1 源码与协作
- Git:版本管理、分支、补丁和历史追踪;
- 邮件补丁工作流:内核社区仍大量使用;
- Forge 平台:GitLab、GitHub 或自建服务可承载项目协作;
- CI:自动构建、静态检查、测试与制品归档。
13.2 构建系统
- Kbuild/Kconfig:Linux 内核自身的配置与构建体系;
- Make、CMake、Meson、Ninja:应用和库常用;
- Buildroot:较直接地构建工具链、rootfs、内核和 Bootloader;
- Yocto Project/OpenEmbedded:适合复杂产品、分层元数据和长期产品化;
- ccache/sccache:减少重复编译时间。
13.3 调试与观测
- GDB:用户程序和远程调试;
strace:观察系统调用;ltrace:观察部分动态库调用;perf:性能分析与硬件计数器;- ftrace、trace-cmd:内核跟踪;
- eBPF/bpftrace:可编程观测;
gdbserver:目标板远程调试;- JTAG/OpenOCD:早期启动和底层硬件调试,是否可用取决于芯片与板卡;
- QEMU:模拟架构或整机,适合自动化与部分无硬件开发;
- 串口终端:开发板启动调试最基础也最重要的工具。
13.4 可复现环境
容器、声明式构建文件和锁定的工具链可以减少“我的机器能编译”的问题。但也要注意:
- 容器共享宿主内核;
- USB/JTAG/串口设备需要显式授权;
- 内核构建对文件权限、挂载和缓存有特殊需求;
- 不能让容器掩盖真实依赖关系;
- 最终制品仍需记录编译器、配置、源码提交和补丁版本。
“最先进”不等于追逐最新名词,而是使用可维护、可验证、自动化且适合目标的工程方法。
14. Linux 今天在哪里
Linux 已广泛用于:
- 云计算与数据中心;
- Web、数据库和基础设施服务器;
- 超级计算机;
- Android 手机和平板;
- 路由器、交换机和防火墙;
- NAS 和家庭服务器;
- 智能电视、机顶盒与家电;
- 汽车系统;
- 工业控制与机器人;
- 无人机、摄像机和物联网设备;
- 开发板和单板计算机;
- 开发者桌面;
- Windows Subsystem for Linux 等跨平台开发环境。
它不是在所有领域都占据同样份额,也不是所有“使用 Linux 内核”的设备都提供相同用户体验。Linux 的核心优势是可塑性:从几十 MB 甚至更小的专用系统,到大型服务器,都能按需求组合。
14.1 持续发展的方向
现代 Linux 持续关注:
- 新 CPU 架构和硬件支持,例如 ARM64、RISC-V;
- 调度、存储、网络和内存性能;
- 实时性;
- Rust 等内核开发新语言的受控引入;
- eBPF 与可观测性;
- 安全加固、机密计算和供应链安全;
- 容器、虚拟化与云原生工作负载;
- 能耗、移动设备和异构计算;
- 图形、AI 加速器及其他专用硬件;
- 驱动上游化和长期维护。
Linux 的发展模式不是推倒重来,而是在保持用户空间兼容性和工程稳定性的同时持续演进。
15. 常见误解
误解 1:Linux 就是 Ubuntu
Ubuntu 是 Linux 发行版之一。Linux 内核还被 Debian、Fedora、Arch、OpenWrt、Android 及大量定制系统使用。
误解 2:Linux 是某家公司开发的
Linux 由全球社区与大量企业共同开发。Linus Torvalds 和维护者体系负责关键技术治理,但项目不等于某一家公司的私有产品。
误解 3:开源等于没有版权、可以任意使用
开源软件仍受版权和许可证约束。使用和分发时必须遵守 GPL、MIT、BSD、Apache 等具体许可证条件。
误解 4:root 可以绕过一切
root 权限很大,但仍可能受到内核机制、只读挂载、capabilities、LSM、安全启动、硬件保护和外部系统限制。root 也不能修复不存在的驱动或错误的硬件连接。
误解 5:内存占用越低越好
Linux 会主动把内存用于页缓存。评价内存状态要看可回收性、工作集、交换和压力,而不是只看 free 一列。
误解 6:设备树就是驱动
设备树描述硬件,驱动实现操作硬件的逻辑。只有设备树而没有匹配驱动,设备通常仍不能工作。
误解 7:换一个新内核就能支持所有硬件
硬件支持还依赖设备树、驱动、固件、Bootloader、时钟电源配置和厂商补丁。新版本可能改善支持,也可能需要迁移工作。
误解 8:能启动就算移植完成
能看到 Shell 只是早期里程碑。稳定性、外设、功耗、异常恢复、升级、安全、压力测试和维护策略同样属于移植成果。
误解 9:Shell 命令就是 Linux 内核
多数命令是用户空间程序。Shell 负责组织它们,内核提供底层资源管理和系统调用。
误解 10:容器里运行的是一套独立内核
普通容器共享宿主机内核,只隔离部分用户空间视图和资源。虚拟机通常拥有自己的客体内核。
16. 用命令观察本机 Linux 架构
以下命令都是观察性练习。先理解输出含义,不必死记参数。
16.1 内核与架构
uname -a
uname -r
uname -m
cat /etc/os-release
注意:uname -r 显示内核版本,/etc/os-release 描述发行版。两者不是同一个概念。
16.2 CPU 与内存
lscpu
free -h
cat /proc/meminfo | head -n 20
16.3 进程与 PID 1
ps -p 1 -o pid,comm,args
ps -ef | head
16.4 文件系统与挂载
findmnt
df -hT
lsblk -f
df 关注已挂载文件系统容量,lsblk 关注块设备结构,两者观察角度不同。
16.5 设备与驱动
lspci -k # 主要用于 PCI/PCIe 设备
lsusb
lsmod
ls -l /sys/class
开发板不一定有 lspci 工具或 PCI 设备。/sys 与设备树通常更重要。
16.6 内核日志
journalctl -k -b
dmesg --color=always | less -R
是否允许普通用户读取 dmesg 取决于系统安全配置。
16.7 系统调用
strace -o /tmp/ls.strace ls /etc
less /tmp/ls.strace
观察 openat、read、mmap、close 等调用,理解一个简单命令背后也需要内核提供许多服务。
17. 面向开发板的核心心智模型
以后遇到任何问题,先判断它属于哪一层:
应用程序
↓ API/库
用户空间运行库与系统服务
↓ 系统调用
Linux 内核通用子系统
↓ 驱动模型
具体设备驱动
↓ 总线/寄存器/中断/DMA
SoC 与板级硬件
启动问题再使用另一条链:
Boot ROM
→ 早期 loader/DRAM 初始化
→ 固件/U-Boot
→ Kernel + DTB
→ rootfs
→ PID 1
→ 服务与应用
排错时不要跨层猜测。例如:
- Bootloader 根本没加载 DTB,就不要先调用户程序;
- 驱动没有 probe,就不要先怀疑应用 API;
- 网络接口没有 link,就不要先排查 DNS;
- rootfs 缺少动态链接器,程序就算架构正确也启动不了。
能定位层级,通常就解决了排错的一半。
18. 本课总结
- 严格来说,Linux 是内核;完整可用系统还需要用户空间和发行版维护体系。
- Unix 提供了重要的系统思想和接口传统,GNU 提供了大量自由用户空间工具,Linux 补上了关键内核。
- Linux 是类 Unix、POSIX 兼容取向的系统,但不是从原始 Unix 源码直接派生出来的产品。
- Linux 采用单体但模块化的内核架构,核心子系统运行在内核空间。
- 应用通过系统调用跨越用户态与内核态边界,请求内核管理资源。
- 内核的主要组成包括调度、内存、VFS、驱动、网络、IPC 和安全。
- 嵌入式启动链包含 Boot ROM、早期加载器、Bootloader、内核、设备树、rootfs 和 PID 1。
- BSP 是支撑具体板卡的一组软件与配置,不只是一个内核压缩包。
- 厂商 BSP 功能可能完整但维护成本高,主线内核维护性好但新硬件支持可能不完整。
- Linux 移植的目标不是“编译通过”,而是形成可启动、可验证、可更新、可维护的完整系统。
19. 课后练习
19.1 观察练习
在 Linux 开发主机执行第 16 节命令,把结果整理到后续实验记录中,并回答:
- 当前发行版是什么?
- 当前内核版本是什么?
- CPU 架构是什么?
- PID 1 是哪个程序?
- 根文件系统是什么类型?
- 系统加载了多少内核模块?
- 从内核日志中找一条 USB 或存储设备初始化信息。
19.2 思考题
- 为什么说 Bash 不是 Linux 内核的一部分?
- 为什么应用不能直接访问任意物理内存?
printf()与write()是什么关系?- 单体内核与内核模块化是否矛盾?
- 设备树和设备驱动分别解决什么问题?
- 为什么厂商 BSP 已经能运行,却仍值得推动驱动上游化?
- 容器和虚拟机在内核使用方式上最根本的区别是什么?
- 开发板已经出现登录提示符,为什么不能据此宣布移植完成?
19.3 动手题
选择一个简单命令,例如 cat /etc/os-release,使用 strace 观察它。尝试找出:
- 它加载了哪些共享库;
- 它用哪个系统调用打开文件;
- 它如何读取内容;
- 它如何把结果写到终端;
- 程序最后以什么状态退出。
20. 术语表
| 术语 | 含义 |
|---|---|
| Kernel | 内核,管理硬件与系统资源的核心软件 |
| User space | 普通应用和系统服务运行的受限空间 |
| System call | 用户程序请求内核服务的正式接口 |
| ABI | 二进制接口约定,包括调用约定、数据布局等 |
| POSIX | 一组类 Unix 操作系统接口标准 |
| Distribution | 将内核、用户空间和维护体系整合起来的发行版 |
| BSP | 板级支持包,支撑具体 SoC/板卡的软件集合 |
| Bootloader | 在内核前运行并负责加载、准备系统的软件 |
| Root filesystem | 以 / 为根的用户空间文件系统 |
| Device tree | 描述不可自动枚举硬件及其连接关系的数据结构 |
| Driver | 控制设备并接入内核框架的代码 |
| VFS | 为不同文件系统提供统一接口的虚拟文件系统层 |
| PID | 进程标识符 |
| MMU | 完成虚拟地址转换和内存保护的硬件单元 |
| DMA | 设备与内存之间直接传输数据的机制 |
| IRQ | 中断请求 |
| IPC | 进程间通信 |
| LTS | 长期支持版本或分支,期限必须查具体维护声明 |
| Upstream | 上游项目,通常指官方主线社区 |
| Downstream | 基于上游修改的发行版或厂商分支 |
21. 延伸阅读
- Linux 内核官方网站:https://www.kernel.org/
- Linux 内核文档:https://docs.kernel.org/
- Linux 内核源码:https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/
- GNU 项目:https://www.gnu.org/
- GNU C Library 文档:https://www.gnu.org/software/libc/manual/
- POSIX/Open Group 基础规范:https://pubs.opengroup.org/onlinepubs/9799919799/
- Debian:https://www.debian.org/
- Buildroot:https://buildroot.org/
- Yocto Project:https://www.yoctoproject.org/
下一课:Linux 开发环境与命令行基础,从终端、Shell、路径、权限、帮助系统和文本流开始,为后续编译内核与操作开发板打基础。
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐

所有评论(0)