面向应用开发与嵌入式工程师。不堆概念,只讲「内核做什么、代码在哪、模块怎么串起来」。
1. 内核在系统中的位置
Linux 内核是运行在最高特权级的核心程序,位于用户态程序与硬件之间:向上提供系统调用,向下管理 CPU、内存、块设备、网络与中断。
text
┌─────────────────────────────────────┐
│ 用户空间:Shell / 应用 / 库(glibc) │
├─────────────────────────────────────┤ 系统调用 syscall
│ 内核空间:Linux Kernel │
│ 进程调度 / 内存 / VFS / 网络 / 驱动 │
├─────────────────────────────────────┤
│ 硬件:CPU / MMU / 存储 / 网卡 / 外设 │
└─────────────────────────────────────┘
三个层次(自顶向下)
| 层次 | 内容 | 典型目录 |
|---|---|---|
| 系统调用接口 | open/read/write/fork 等 SCI | arch/*/kernel/、kernel/ |
| 架构无关代码 | 调度、内存、VFS、网络协议 | kernel/ mm/ fs/ net/ |
| 架构相关代码 | 中断、MMU、平台启动 | arch/arm64/ arch/x86/ |
glibc 通过系统调用进入内核;用户进程各自拥有虚拟地址空间,内核独占独立地址空间。这与微内核「仅保留最小核心、服务外置」不同------Linux 选择宏内核,基础能力集成在内核态,换取更低的跨边界开销。
2. 一条命令背后的内核链路
执行 ./app 时,内核侧大致经历:
#mermaid-svg-03vYL02aXnp7YaZK{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-03vYL02aXnp7YaZK .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-03vYL02aXnp7YaZK .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-03vYL02aXnp7YaZK .error-icon{fill:#552222;}#mermaid-svg-03vYL02aXnp7YaZK .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-03vYL02aXnp7YaZK .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-03vYL02aXnp7YaZK .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-03vYL02aXnp7YaZK .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-03vYL02aXnp7YaZK .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-03vYL02aXnp7YaZK .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-03vYL02aXnp7YaZK .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-03vYL02aXnp7YaZK .marker{fill:#333333;stroke:#333333;}#mermaid-svg-03vYL02aXnp7YaZK .marker.cross{stroke:#333333;}#mermaid-svg-03vYL02aXnp7YaZK svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-03vYL02aXnp7YaZK p{margin:0;}#mermaid-svg-03vYL02aXnp7YaZK .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-03vYL02aXnp7YaZK .cluster-label text{fill:#333;}#mermaid-svg-03vYL02aXnp7YaZK .cluster-label span{color:#333;}#mermaid-svg-03vYL02aXnp7YaZK .cluster-label span p{background-color:transparent;}#mermaid-svg-03vYL02aXnp7YaZK .label text,#mermaid-svg-03vYL02aXnp7YaZK span{fill:#333;color:#333;}#mermaid-svg-03vYL02aXnp7YaZK .node rect,#mermaid-svg-03vYL02aXnp7YaZK .node circle,#mermaid-svg-03vYL02aXnp7YaZK .node ellipse,#mermaid-svg-03vYL02aXnp7YaZK .node polygon,#mermaid-svg-03vYL02aXnp7YaZK .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-03vYL02aXnp7YaZK .rough-node .label text,#mermaid-svg-03vYL02aXnp7YaZK .node .label text,#mermaid-svg-03vYL02aXnp7YaZK .image-shape .label,#mermaid-svg-03vYL02aXnp7YaZK .icon-shape .label{text-anchor:middle;}#mermaid-svg-03vYL02aXnp7YaZK .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-03vYL02aXnp7YaZK .rough-node .label,#mermaid-svg-03vYL02aXnp7YaZK .node .label,#mermaid-svg-03vYL02aXnp7YaZK .image-shape .label,#mermaid-svg-03vYL02aXnp7YaZK .icon-shape .label{text-align:center;}#mermaid-svg-03vYL02aXnp7YaZK .node.clickable{cursor:pointer;}#mermaid-svg-03vYL02aXnp7YaZK .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-03vYL02aXnp7YaZK .arrowheadPath{fill:#333333;}#mermaid-svg-03vYL02aXnp7YaZK .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-03vYL02aXnp7YaZK .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-03vYL02aXnp7YaZK .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-03vYL02aXnp7YaZK .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-03vYL02aXnp7YaZK .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-03vYL02aXnp7YaZK .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-03vYL02aXnp7YaZK .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-03vYL02aXnp7YaZK .cluster text{fill:#333;}#mermaid-svg-03vYL02aXnp7YaZK .cluster span{color:#333;}#mermaid-svg-03vYL02aXnp7YaZK div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-03vYL02aXnp7YaZK .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-03vYL02aXnp7YaZK rect.text{fill:none;stroke-width:0;}#mermaid-svg-03vYL02aXnp7YaZK .icon-shape,#mermaid-svg-03vYL02aXnp7YaZK .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-03vYL02aXnp7YaZK .icon-shape p,#mermaid-svg-03vYL02aXnp7YaZK .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-03vYL02aXnp7YaZK .icon-shape .label rect,#mermaid-svg-03vYL02aXnp7YaZK .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-03vYL02aXnp7YaZK .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-03vYL02aXnp7YaZK .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-03vYL02aXnp7YaZK :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} shell fork/exec
分配 task_struct
建立页表与 VMA
VFS open 可执行文件
页缓存 / 块 I/O 读入
加载 ELF 映射到用户空间
跳转用户态 _start
| 步骤 | 子系统 | 关键点 |
|---|---|---|
| fork/exec | 进程管理 | copy_process、load_elf_binary |
| 内存映射 | 内存管理 | mmap、VMA、页表 |
| 读文件 | VFS + 块层 | dentry/inode → bio → 驱动 |
| 调度运行 | 调度器 | CFS、schedule() |
理解这条链,排障时能快速判断:崩溃在内存、文件还是 I/O。
3. 源码顶层目录速查
克隆主线内核后,优先熟悉这些目录:
| 目录 | 职责 |
|---|---|
arch/ |
体系结构相关:启动、MMU、中断、平台 DTS |
kernel/ |
调度、信号、锁、时间、power |
mm/ |
物理页、虚拟内存、slab、swap |
fs/ |
VFS 及 ext4/xfs 等具体 FS |
net/ |
协议栈(TCP/IP、socket) |
ipc/ |
信号量、消息队列、共享内存 |
drivers/ |
字符/块/网络设备驱动 |
block/ |
块层、I/O 调度 |
init/ |
start_kernel 启动路径 |
include/ |
内核头文件 |
lib/ |
内核内工具库(CRC、radix tree 等) |
security/ |
SELinux 等 |
virt/kvm/ |
虚拟化 |
scripts/ Kconfig |
构建与配置 |
阅读建议 :先抓 init/main.c、sched/core.c、mm/slub.c、fs/namei.c 的调用关系,再按问题深入驱动或协议栈。
4. 五大子系统
4.1 进程管理与调度
- 进程描述 :
task_struct(PID、状态、内存描述符、文件表、调度实体) - 创建 :
fork()→copy_process();exec()替换映像 - 线程:Linux 用轻量进程实现,内核视角均为可调度的 task
- 调度 :现代内核默认 CFS(完全公平调度),基于红黑树与虚拟运行时间;实时类用 FIFO/RR
c
/* 概念:每个 task 在 runqueue 中竞争 CPU */
schedule(); /* 内核主动/被动让出 CPU 的入口 */
源码入口:kernel/fork.c、kernel/sched/core.c。
4.2 内存管理
- 分页:主流 4KB 页;MMU 完成虚拟→物理翻译
- 分配器层级:memblock(启动)→ 伙伴系统(页)→ slab/slub(小对象)→ vmalloc(大块虚拟连续)
- 换出:内存紧张时 page 换到 swap
- 用户视角 :
malloc走 glibc 堆;大块用mmap;与内核页 fault 联动
源码入口:mm/page_alloc.c、mm/slub.c、mm/memory.c(缺页异常)。
4.3 虚拟文件系统(VFS)
一切皆文件:普通文件、目录、设备、socket 都经 VFS 统一接口。
text
open("/path/file")
→ path_lookup → dentry 缓存
→ inode(元数据 + 数据块指针)
→ 具体 FS 操作表 (ext4/xfs/...)
→ 返回 fd
| 结构 | 作用 |
|---|---|
| super_block | 文件系统实例 |
| inode | 文件元数据 |
| dentry | 路径名缓存 |
| file | 打开实例(fd 对应) |
常见 FS:ext4(通用)、xfs(大文件/高并发)、squashfs(嵌入式只读)、procfs/sysfs(内核接口)。
4.4 网络协议栈
自顶向下:应用 → socket → TCP/UDP → IP → 链路层 → 驱动。
- 用户态通过 Berkeley socket 与内核交互
- Netlink 用于路由、udev 等与内核双向通信
- 驱动在
drivers/net/;协议在net/ipv4/、net/ipv6/
4.5 设备驱动
| 类型 | 特点 | 例子 |
|---|---|---|
| 字符设备 | 字节流、无缓冲seek | 串口、GPIO |
| 块设备 | 按块、有请求队列 | eMMC、NVMe |
| 网络设备 | 数据包收发 | 以太网 MAC |
Linux 3.x 后主流为 设备树(DT)+ platform/driver 模型 :compatible 匹配 → probe() 初始化硬件。
5. 内核模块(.ko)动态扩展
模块是可加载/卸载的内核代码,无需重编整内核。
bash
lsmod # 已加载模块
sudo insmod xxx.ko # 加载(需手动处理依赖)
sudo modprobe xxx # 加载(自动解析依赖)
sudo rmmod xxx # 卸载
modinfo xxx.ko # 模块元信息
最小模块模板:
c
#include <linux/module.h>
#include <linux/init.h>
static int __init demo_init(void)
{
pr_info("demo: loaded\n");
return 0;
}
static void __exit demo_exit(void)
{
pr_info("demo: unloaded\n");
}
module_init(demo_init);
module_exit(demo_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Demo kernel module");
编译为 .ko 后 insmod 即可;dmesg 查看 pr_info 输出。
6. 调试与观测手段
| 工具/方法 | 用途 |
|---|---|
printk / pr_info |
最基础内核日志 |
/proc /sys |
运行时参数与设备信息 |
ftrace / perf |
性能与调用链 |
kprobe / ftrace function_graph |
函数级跟踪 |
kgdb |
源码级断点调试(需额外配置) |
KASAN |
内存越界检测 |
oops / panic 日志 |
崩溃栈分析 |
应用层问题用 strace 看系统调用;内核/driver 问题从 dmesg 与对应子系统 proc 节点入手。
7. 学习路径建议
- 先建立地图:用户态 → syscall → 五大子系统 → 驱动/硬件
- 再读启动链 :
head.S→start_kernel→rest_init(与嵌入式启动衔接) - 用工具验证 :
strace、/proc/pid/maps、lsmod、mount - 小步读源码 :跟一条
open()或fork()进内核,不要一上来通读drivers/
不必一开始就写驱动;能把「进程---内存---文件---块 I/O」串起来,就已经超过大多数「只会用命令」的层次。
8. 小结
| 模块 | 一句话 |
|---|---|
| 进程调度 | 管理 task 与 CPU 时间片 |
| 内存 | 虚拟地址、页表、分配与回收 |
| VFS | 统一文件接口,衔接具体 FS 与块层 |
| 网络 | 协议栈与 socket |
| 驱动 | 把硬件接入内核子系统 |
内核学习的本质是:在特权级代码里,看清资源如何被抽象、分配、回收。掌握本文地图,再按需深入各子系统源码,效率远高于漫无目的翻目录。