首页/目录/全部文章

全部文章

八个专题的源码、算法与协议笔记都在这里。

笔记列表

04 irqbypass 与 Kconfig 构建机制与实现详解

04 irqbypass 与 Kconfig 构建机制与实现详解

1. virt/lib/irqbypass.c

配置CONFIG_IRQ_BYPASS_MANAGER(tristate,virt/lib/Kconfig)。

作用:IRQ producer(如 VFIO 物理设备中断)与 consumer(如 KVM guest 注入路径)注册匹配,在硬件支持时建立 bypass 通道,减少 host 内核中转延迟。

核心结构(include/linux/irqbypass.h):

  • struct irq_bypass_produceradd_consumer / del_consumer / stop / start
  • struct irq_bypass_consumer:对称回调
1
2
3
static LIST_HEAD(producers);
static LIST_HEAD(consumers);
static DEFINE_MUTEX(lock);

irq_bypass_register_producer 时尝试与已有 consumer __connect;断开时 __disconnect

模块:可 =m 独立加载;ARM64 KVM 配置 select IRQ_BYPASS_MANAGER

版权 注明 Linaro — 与 ARM IRQ forwarding 场景一致,利于 RK3588 + VFIO 直通。

2. virt/kvm/Kconfig — 能力声明

该文件 直接 config KVM(在 arch/arm64/kvm/Kconfig),仅定义 HAVE_* 能力供架构 select:

符号 含义
HAVE_KVM_IRQCHIP 内核 irqchip 模型
HAVE_KVM_IRQ_ROUTING GSI 路由表
HAVE_KVM_IRQFD eventfd 注入
HAVE_KVM_EVENTFD 依赖 EVENTFD
HAVE_KVM_DIRTY_RING 脏页环
HAVE_KVM_DIRTY_RING_TSO 仅 x86 强序
HAVE_KVM_DIRTY_RING_ACQ_REL 弱序架构
KVM_MMIO / KVM_ASYNC_PF MMIO 合并 / 异步缺页
KVM_VFIO VFIO 桥
HAVE_KVM_IRQ_BYPASS 与 bypass 管理器协作
KVM_COMPAT 32 位 compat ioctl(ARM64 排除

注意:

1
2
3
config KVM_COMPAT
def_bool y
depends on KVM && COMPAT && !(S390 || ARM64 || RISCV)

ARM64 KVM compat ioctl 路径。

3. arch/arm64/kvm/Kconfig 菜单

1
2
3
4
5
6
7
8
9
menuconfig VIRTUALIZATION
menuconfig KVM
depends on HAVE_KVM
select MMU_NOTIFIER
...
select IRQ_BYPASS_MANAGER
select HAVE_KVM_IRQ_BYPASS
config NVHE_EL2_DEBUG
config PROTECTED_NVHE_STACKTRACE
  • HAVE_KVM:在 arch/arm64/Kconfig select HAVE_KVM(CPU 具备虚拟化扩展时);
  • NVHE_EL2_DEBUG:非 VHE EL2 对象调试;
  • pKVM 相关选项在 arch Kconfig 更深处理(见 05 文档)。

4. 构建依赖链

1
2
3
4
5
arch/arm64/Kconfig: HAVE_KVM
→ arch/arm64/kvm/Kconfig: KVM + source virt/kvm/Kconfig
→ virt/kvm/Makefile.kvm → kvm-y 对象
→ arch/arm64/kvm/Makefile: + arm.o vgic/... hyp/
virt/Makefile → virt/lib/ → irqbypass.o

5. 与 drivers/vfio 的 Kconfig 交叉

drivers/vfio/Kconfigsource "virt/lib/Kconfig",保证 VFIO 与 KVM 可共用 IRQ_BYPASS_MANAGER

6. RK3588 配置示例

1
2
3
4
5
CONFIG_VIRTUALIZATION=y
CONFIG_KVM=y
CONFIG_IRQ_BYPASS_MANAGER=y
# 可选模块
# CONFIG_KVM=m

需确认 CPU 支持 Virtualization/proc/cpuinfo 或 cpufeature);部分 裁剪 BSP 关闭以缩小内核。

7. 小结

  • irqbypass.c:VFIO/KVM 中断直通匹配器;
  • virt/kvm/Kconfig:架构无关能力字典;
  • 实际 KVM 开关arch/arm64/kvm/Kconfig

RK3588 直通中断优化需 SMMU + VFIO + IRQ_BYPASS 与硬件 GIC 支持一并考虑。

05 ARM64 KVM 与 virt 目录边界详解

05 ARM64 KVM 与 virt 目录边界详解

1. 为何必须读 arch/arm64/kvm

virt/19 文件;RK3588 上可运行的 KVM 依赖 arch/arm64/kvm/(~98 文件 + hyp/)实现:

能力 主要位置
EL2 hypervisor hyp/nvhe/hyp/vhe/
Guest 退出处理 handle_exit.c
Stage-2 MMU mmu.c
虚拟 GIC vgic/vgic-v3.cvgic-its.c
PSCI 关机/CPU on psci.c
系统寄存器陷阱 sys_regs.c
指针认证/MTE(视配置) sys_regs、特性检测
Protected KVM pkvm.c + hyp 内存保护
虚拟 architected timer arch_timer.c
PMU 虚拟化 pmu.cpmu-emul.c

arch/arm64/kvm/Makefile

1
2
3
include $(srctree)/virt/kvm/Makefile.kvm
kvm-y += arm.o mmu.o ... vgic/*.o
obj-$(CONFIG_KVM) += kvm.o hyp/

kvm.o = virt 公共对象 + 上表 arch 对象;hyp/ 单独构建 EL2 镜像对象。

2. arm.c — 架构枢纽

  • kvm_arch_hardware_setupkvm_vm_ioctl_enable_cap
  • VHE vs nVHE 模式(kvm_modekvm_protected_mode_initialized);
  • per-CPU kvm_arm_hardware_enabledkvm_hyp_vector
  • PSCI、PMU、指针认证 特性交互。

kvm_arch_vcpu_ioctl_run 最终进入汇编 guest entryhyp)。

3. vGIC 子系统

1
2
3
4
5
6
7
8
arch/arm64/kvm/vgic/
├── vgic.c / vgic-init.c # 框架
├── vgic-v2.c / vgic-v3.c # 版本实现
├── vgic-v4.c # 直接注入(HW 支持时)
├── vgic-its.c # LPI/MSI
├── vgic-mmio*.c # 寄存器仿真
├── vgic-irqfd.c # 与 eventfd 衔接
└── vgic-kvm-device.c # KVM_DEVICE API

文档:Documentation/virt/kvm/devices/arm-vgic-v3.rstarm-vgic-its.rst

virt/irqchip.c 负责 GSI 路由表;vgic 负责 IRQ 注入到 guest CPU 接口

4. hyp/ — EL2 代码

  • nVHE(Non-VHE):hypervisor 运行在 EL2,host 内核在 EL1;
  • VHE(Virtualization Host Extensions):host 与 hyp 同在 EL2 部分合并;
  • pKVM:将部分内存标记为 hypervisor protected,隔离恶意 host。

RK3588 Cortex-A76/A55 是否支持 VHE 取决于 ARMv8.1+ 与内核配置;需查 BSP config/proc/cpuinfo

5. 与 virt/kvm 的调用边界

调用方向 示例
virt → arch kvm_arch_vcpu_ioctl_run, kvm_arch_init_vm
arch → virt kvm_set_irq, kvm_vcpu_kick, file_is_kvm
用户态 → virt ioctl → arch KVM_RUN 路径

弱符号:virt 中 __weak 默认空实现,arch 提供强符号。

6. 其它 virt 相关树(非 virt/)

路径 说明
drivers/virtio/ paravirt I/O 设备前端/后端
drivers/vfio/ 设备直通框架
drivers/virt/coco/ 机密虚拟机(SEV 等,非 ARM RK 主流)
tools/virtio/tools/kvm/ 用户态工具

7. 阅读路线图(ARM64 KVM)

  1. virt/kvm/kvm_main.c — ioctl 与 VM 生命周期;
  2. arch/arm64/kvm/arm.c — caps 与 run;
  3. arch/arm64/kvm/handle_exit.c — exit 原因;
  4. arch/arm64/kvm/mmu.c — Stage-2;
  5. arch/arm64/kvm/vgic/vgic-v3.c — 中断;
  6. Documentation/virt/kvm/arm/hyp-abi.rst — ABI 约定。

8. 小结

分析 virt/ 目录 ≠ 分析完 ARM64 KVM。RK3588 虚拟化问题 多数应在 arch/arm64/kvm 与 hyp 下定位virt/ 提供 VM 容器与通用 IRQ/内存/VFIO 框架。

06 RK3588 平台关联与使用场景详解

06 RK3588 平台关联与使用场景详解

1. virt/ 与 Rockchip 定制

virt/ 目录无 Rockchip 补丁。RK3588 KVM 行为由 ARM64 通用 KVM + SoC CPU 特性 / IOMMU(SMMU) / GIC 决定。

平台相关启动优化(Thunder Boot 等)在 drivers/soc/rockchip/不经过 virt/kvm

2. 硬件与配置前提

条件 说明
ARMv8 虚拟化扩展 EL2 可用;arch/arm64/Kconfig select HAVE_KVM
GICv3/GICv4 RK3588 典型 GIC-600 系列 → vGICv3/ITS 模型
SMMU 设备直通需 Stage-2 与 VFIO 协同
内核配置 CONFIG_VIRTUALIZATION=yCONFIG_KVM=y

检查:

1
2
grep -E 'Virtualization|KVM' /proc/config.gz   # 或 .config
ls -l /dev/kvm

许多 嵌入式 BSP 默认关闭 KVM 以减小内核与攻击面。

3. 典型使用场景

场景 说明
Android 容器 / Cuttlefish RK3588 开发板作 host,依赖 KVM + vGIC + virtio
** QEMU 虚拟机** 调试用 Linux/Windows guest
crosvm / Cloud Hypervisor Rust VMM,ioctl 同 /dev/kvm
pKVM(若启用) 隔离敏感 VM 免受 host 内核威胁(Android 安全方向)
纯嵌入式产品 通常 不用 KVM,virt 代码不链入

4. 性能与调优触点

组件 调优
halt_poll_nskvm_main.c 模块参数) idle 退出延迟 vs host CPU
coalesced mmio + ioeventfd virtio 吞吐
dirty ring 大内存 VM 迁移
IRQ bypass + VFIO 直通网卡中断延迟
arch_timer guest 时钟漂移

用户态工具:tools/kvm/kvm_stat(见 linuxDoc/tools)。

5. 与 RK3588 外设直通的现实限制

  • GPU/NPU/VPU 多为专有驱动,非标准 PCI VFIO 场景;虚拟化常用手动 virtio-GPU 或 API 转发,而非 virt/kvm/vfio.c 直通;
  • PCIe 网卡/USB 控制器 在具备 SMMU 时可考虑 VFIO;
  • IOMMU 组 划分不当会导致 VFIO 组不完整。

6. 调试路径

现象 建议
/dev/kvm 不存在 检查 CONFIG_KVM、模块是否加载 kvm
KVM_CREATE_VM 失败 dmesg kvm;CPU 是否支持 EL2
Guest 无中断 vgic 配置、DT 未参与 KVM,查 QEMU -machine virt,gic-version=3
直通失败 CONFIG_VFIOCONFIG_IRQ_BYPASS_MANAGER、SMMU 驱动
EL2 panic CONFIG_NVHE_EL2_DEBUG 打开更详细 hyp 报错

trace:trace/events/kvm + arch/arm64/kvm/trace_arm.h

7. 与文档、测试

  • 内核文档:Documentation/virt/kvm/arm/index.rst
  • 自测:tools/testing/selftests/kvm/(含 aarch64 QEMU 配置)
  • API:Documentation/virt/kvm/api.rst

8. 配置清单(host 开发向)

1
2
3
4
5
6
CONFIG_VIRTUALIZATION=y
CONFIG_KVM=y
CONFIG_IRQ_BYPASS_MANAGER=y
CONFIG_VFIO=y
# 视需求
# CONFIG_KVM_DIRTY_RING=y

模块方式:

1
2
3
modprobe kvm
# arm64 上 kvm 模块通常已链接 arch 代码
lsmod | grep kvm

9. 小结

RK3588 上 virt/ 提供与其它架构相同的 KVM 公共 ioctl 与 IRQ/VFIO 框架;平台差异体现在 是否启用 KVMSMMU/GIC用户态 VMM。深入实现请联合 05-ARM64边界 与 BSP defconfig

virt 文档索引

virt 文档索引

Linux 6.1(RK3588)内核 virt/ 目录源码分析与 KVM 公共层说明。

定位说明

virt/19 个文件,是 多架构共享的 KVM 公共实现kvm_main.c 等),不是完整虚拟化子系统。ARM64 上 RK3588 的 KVM 主体在 arch/arm64/kvm/(约 98 个源文件 + hyp/),通过 include virt/kvm/Makefile.kvm 链入同一 kvm.ko

勿混淆

路径 含义
virt/ KVM 架构无关公共代码
arch/arm64/kvm/ ARM64 虚拟机、VGIC、EL2 hypervisor
drivers/virt/ 其它虚拟化驱动(ACRN、CoCo SEV…)
Documentation/virt/ 文档(含 ARM KVM API)

总览文档

文档 说明
virt目录作用与KVM公共层机制详解.md 总览、分层、ioctl 路径、RK3588
源码目录与模块索引.md virt/ 文件表、Makefile.kvm 映射

功能组专题(原理 + 实现)

编号 文档 源码重点
01 kvm_main核心机制与实现详解.md VM/VCPU、ioctl、mmu_notifier、halt poll
02 中断事件与VFIO机制与实现详解.md eventfd.cirqchip.cvfio.c
03 内存脏页与异步PF机制与实现详解.md dirty_ring.cpfncache.casync_pf.ccoalesced_mmio.c
04 irqbypass与Kconfig构建机制与实现详解.md lib/irqbypass.cvirt/kvm/Kconfig
05 ARM64-KVM与virt目录边界详解.md arch/arm64/kvm、VGIC、hyp、pkvm
06 RK3588平台关联与使用场景详解.md CPU 扩展、配置、容器/安卓场景

源码路径

1
2
rk3588/kernel-6.1/virt/
rk3588/kernel-6.1/arch/arm64/kvm/ # RK3588 必读关联树

关联linuxDoc/toolskvm_stat)、Documentation/virt/kvm/arm/

阅读顺序建议

总览01 kvm_main05 ARM64 边界02 中断/VFIO03 内存06 RK3588

速查

需求 入口
打开 /dev/kvm、创建 VM kvm_main.cKVM_CREATE_VM
设备直通 vfio.c + 用户态 QEMU/virtio
GSI/irqfd eventfd.c + irqchip.c
ARM 虚拟 GIC arch/arm64/kvm/vgic/
IRQ bypass(VFIO↔KVM) virt/lib/irqbypass.c

virt 目录作用与 KVM 公共层机制详解

virt 目录作用与 KVM 公共层机制详解

1. 在内核虚拟化栈中的位置

Linux 主机虚拟化(KVM)采用 分层模块

ioctl /dev/kvmirq bypassQEMU / crosvm / Cloud Hypervisorkvm 字符设备 kvm_main.cstruct kvm VMstruct kvm_vcpukvm_arch_* / arch/arm64/kvmEL2 hyp: arch/arm64/kvm/hypVFIO 设备驱动virt/lib/irqbypass.c
  • virt/kvm/:VM 生命周期、内存槽、ioctl 分发、通用 irq 路由、VFIO 桥、脏页环等;
  • arch/<arch>/kvm/:指令仿真、MMU、虚拟中断控制器、hypercall;
  • virt/lib/:IRQ bypass 匹配(producer/consumer),供 KVM 与 VFIO 共用。

virt/ Rockchip 定制(全树 grep 无 rockchip/rk3588)。

2. 目录体量

位置 文件数(约) 说明
virt/ 19 公共 C 源文件 + Kconfig
arch/arm64/kvm/ 98+ RK3588 相关 KVM 实现主体
Documentation/virt/kvm/ 50+ rst API、VGIC、ARM hyp ABI

分析 仅读 virt/ 会遗漏 VGIC、定时器、PSCI、pKVM 等 ARM 关键路径。

3. 构建与链接

virt/Makefile 仅:

1
obj-y += lib/

virt/libCONFIG_IRQ_BYPASS_MANAGER 下编译 irqbypass.o

KVM 模块对象由架构 Makefile 聚合,例如 ARM64:

1
2
3
4
# arch/arm64/kvm/Makefile
include $(srctree)/virt/kvm/Makefile.kvm
kvm-y += arm.o mmu.o ... vgic/*.o
obj-$(CONFIG_KVM) += kvm.o hyp/

virt/kvm/Makefile.kvm 定义公共对象:

1
2
3
4
5
kvm-y := kvm_main.o eventfd.o binary_stats.o
kvm-$(CONFIG_KVM_VFIO) += vfio.o
kvm-$(CONFIG_HAVE_KVM_IRQ_ROUTING) += irqchip.o
kvm-$(CONFIG_HAVE_KVM_DIRTY_RING) += dirty_ring.o
...

最终 kvm.ko(或 built-in)= virt/kvm/* + arch/arm64/kvm/* + hyp/ 子构建。

4. 用户态接口(概念)

节点/操作 实现位置
/dev/kvm miscdevice kvm_main.c
KVM_CREATE_VM kvm_dev_ioctl_create_vmanon_inode kvm-vm
KVM_CREATE_VCPU kvm_vm_ioctl
KVM_RUN kvm_vcpu_ioctlkvm_arch_vcpu_ioctl_run(ARM 在 arm.c
KVM_SET_USER_MEMORY_REGION 内存槽 → mmu_notifier
KVM_IRQFD / routing eventfd.c + irqchip.c + arch VGIC

API 文档:Documentation/virt/kvm/api.rst

5. 核心数据结构(公共层)

定义于 include/linux/kvm_host.h(不在 virt/ 目录内,但被其广泛使用):

  • struct kvm:VM 实例、memslots、irq 路由表、lock 层级;
  • struct kvm_vcpu:虚拟 CPU、请求位、async_pf 队列;
  • struct kvm_memory_slot:Guest RAM 映射;
  • kvm_io_bus:MMIO/PIO 总线(coalesced mmio 优化)。

锁顺序(kvm_main.c 注释):kvm->lockkvm->slots_lockkvm->irq_lock

6. 弱符号与架构钩子

kvm_main.c 大量 __weak / kvm_arch_* 由架构实现,例如:

  • kvm_arch_mmu_notifier_invalidate_range
  • kvm_flush_remote_tlbs 调用 arch TLB flush
  • kvm_arch_dev_ioctl 处理 KVM_CREATE_VMtype 参数(ARM64 特性)

ARM64 在 arm.cmmu.chandle_exit.c 等提供具体行为。

7. RK3588 语境(摘要)

  • SoC 支持 ARMv8 虚拟化扩展 时,可 CONFIG_VIRTUALIZATION=yCONFIG_KVM=yType-2 hypervisor 主机(容器/Android 模拟器/开发用 VM);
  • 嵌入式产品 默认常关闭 KVM 以减镜像与攻击面;
  • 虚拟中断、计时器、PSCIarch/arm64/kvm,非 virt/
  • 详见 06-RK3588平台关联与使用场景详解.md

8. 进一步阅读

README.md 编号文档;ARM 实现必读 05-ARM64-KVM与virt目录边界详解.md

virt 源码目录与模块索引

virt 源码目录与模块索引

源码根:rk3588/kernel-6.1/virt/19 文件

1. 目录树

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
virt/
├── Makefile # obj-y += lib/
├── kvm/
│ ├── kvm_main.c # KVM 核心 (~6k 行)
│ ├── eventfd.c # irqfd、ioeventfd
│ ├── irqchip.c # GSI 路由、irq routing API
│ ├── vfio.c / vfio.h # KVM-VFIO 桥
│ ├── dirty_ring.c # 脏页环
│ ├── pfncache.c # PFN 缓存
│ ├── async_pf.c / .h # 异步 page fault
│ ├── coalesced_mmio.c/.h # MMIO 合并环
│ ├── binary_stats.c # 二进制统计导出
│ ├── kvm_mm.h # MMU 辅助宏/声明
│ ├── Makefile.kvm # 公共 kvm-y 对象列表
│ └── Kconfig # HAVE_KVM_* 能力开关
└── lib/
├── irqbypass.c # IRQ bypass 管理器
├── Kconfig # IRQ_BYPASS_MANAGER
└── Makefile

2. Makefile.kvm → 对象映射

对象 Kconfig 条件 源文件
始终 kvm_main.o, eventfd.o, binary_stats.o
VFIO CONFIG_KVM_VFIO vfio.o
MMIO CONFIG_KVM_MMIO coalesced_mmio.o
异步 PF CONFIG_KVM_ASYNC_PF async_pf.o
IRQ 路由 CONFIG_HAVE_KVM_IRQ_ROUTING irqchip.o
脏页环 CONFIG_HAVE_KVM_DIRTY_RING dirty_ring.o
PFN cache CONFIG_HAVE_KVM_PFNCACHE pfncache.o

ARM64 arch/arm64/kvm/KconfigKVM 菜单下 select 多项 HAVE_KVM_*

3. kvm_main.c 功能块索引

主题 大致内容
模块参数 halt_poll_ns* 停机轮询优化
全局 kvm_lock, vm_list, kvm_debugfs_dir
VCPU 调度 vcpu_load/put, kvm_make_vcpu_request, IPI kick
MMU 缓存 kvm_mmu_memory_cache_*
mmu_notifier host 内存变更 → guest PTE 失效
memslots 创建/删除/查询 GPA→HVA
ioctl kvm_dev_ioctl, kvm_vm_ioctl, kvm_vcpu_ioctl, kvm_device_ioctl
设备 API kvm_ioctl_create_device 虚拟设备
初始化 kvm_init / kvm_exit, CPU hotplug, reboot 通知

4. 关联架构树(RK3588)

arch/arm64/kvm/ 主要文件:

文件/目录 职责
arm.c 架构初始化、cap、vcpu run 入口
mmu.c Stage-2页表、IPA
handle_exit.c EL2 退出原因分发
vgic/ vGICv2/v3/v4、ITS
hyp/ nVHE/VHE hypervisor 代码
pkvm.c Protected KVM
psci.c 电源状态
arch_timer.c 虚拟定时器
sys_regs.c 系统寄存器陷阱
fpsimd.c SIMD/FP 状态

构建:include virt/kvm/Makefile.kvm + obj-$(CONFIG_KVM) += hyp/

5. 头文件与 UAPI(virt 外)

头文件 用途
include/uapi/linux/kvm.h 用户态 ioctl 定义
include/linux/kvm_host.h 内核 KVM 内部结构
include/linux/kvm_dirty_ring.h 脏页环
include/linux/irqbypass.h bypass 注册 API
arch/arm64/include/asm/kvm_*.h ARM 专用

6. 文档路径

1
2
3
4
Documentation/virt/kvm/api.rst
Documentation/virt/kvm/arm/index.rst
Documentation/virt/kvm/devices/arm-vgic-v3.rst
Documentation/virt/kvm/locking.rst

7. 与 drivers/virt 区分

drivers/virt virt/
ACRN、SEV guest、EFI secret KVM 公共层
独立菜单 Device Drivers → Virtualization drivers arch/*/kvm/Kconfig source

8. 快速定位

问题 查看
VM 创建失败 kvm_create_vm, kvm_arch_init_vm
内存迁移脏页 dirty_ring.c 或 legacy dirty log ioctl
中断注入 kvm_set_irq, arch vgic
直通 GPU/NIC vfio.c + drivers/vfio
ARM VM 不启动 arch/arm64/kvm/hyp, handle_exit.c

Zephyr 总体架构与目录导读

Zephyr 总体架构与目录导读

1. 软件分层

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
应用层
├─ application main()
├─ 应用线程、业务状态机
└─ prj.conf / overlay

系统服务层
├─ logging / shell / settings
├─ networking / fs / storage
├─ DFU / mcumgr / USB / IPC
└─ tracing / debug / PM

驱动与设备模型层
├─ struct device + driver API
├─ GPIO/I2C/SPI/UART/flash/clock/pinctrl/DMA
└─ Devicetree instance + Kconfig implementation selection

内核层
├─ thread / scheduler / timeout
├─ semaphore / mutex / queue / poll / work
├─ heap / slab / memory domain / userspace
└─ SMP / idle / fatal / object core

架构与 SoC 层
├─ arch/: 上下文切换、中断、原子操作、MMU/MPU
├─ soc/: 芯片启动、时钟、中断控制器
└─ boards/: 板级 DTS、defconfig、连接关系

构建描述层
├─ CMake / west / sysbuild
├─ Kconfig
├─ Devicetree + bindings
└─ linker scripts + generators

Zephyr 的硬件描述、功能选择和源码编译高度耦合于编译期。很多看似“运行时注册”的对象,实际是由宏放进 iterable linker section,再由内核按 section 边界遍历。

2. 顶层目录

arch/

CPU 架构实现。完整上游 Zephyr 支持多种架构,但当前裁剪树实际只包含 ARM 和 POSIX 实现(另有 arch/common 公共代码);不能依据残留公共头文件假定 RISC-V、x86、Xtensa 等架构可直接构建。架构层主要职责:

  • reset 后进入 C 环境;
  • interrupt lock/unlock 和异常入口;
  • thread context 构造和切换;
  • syscall trap;
  • cache、MPU/MMU、userspace;
  • SMP CPU 启动和 IPI;
  • 架构专用 linker snippets。

通用内核通过 arch_*() API 调用架构层,例如 arch_kernel_init()arch_switch()arch_irq_lock()

kernel/

与硬件架构无关的内核主体:

  • init.c:系统启动和 init level;
  • sched.cpriority_queues.ctimeslicing.c:调度;
  • thread.cinit_static.cdynamic.c:线程生命周期;
  • timeout.ctimer.c:时间管理;
  • work.csystem_work_q.c:workqueue;
  • sem.cmutex.ccondvar.cevents.c:同步;
  • queue.cmsg_q.cmailbox.cpipes.cpoll.c:通信;
  • kheap.cmem_slab.cmempool.c:内存;
  • userspace.cmem_domain.cuserspace_handler.c:用户态;
  • smp.cipi.ccpu_mask.c:多核;
  • idle.cfatal.cdevice.c:系统基础设施。

include/zephyr/

公共 API 和大量编译期宏。常用入口:

  • kernel.h:内核 API 总入口;
  • device.h:设备模型;
  • devicetree.h:DTS 查询宏;
  • init.hSYS_INIT()
  • drivers/:驱动 class API;
  • sys/:链表、原子操作、byteorder、util、iterable section;
  • logging/net/fs/pm/:子系统 API;
  • internal/:内部生成和 syscall 支撑。

drivers/

按设备类别组织实现,例如:

1
2
3
4
5
adc/ audio/ cache/ can/ charger/ clock_control/
console/ counter/ crypto/ dac/ disk/ dma/ eeprom/ entropy/
ethernet/ flash/ gpio/ hwinfo/ i2c/ i3c/ input/ interrupt_controller/
led/ mbox/ mdio/ modem/ pinctrl/ pwm/ regulator/ reset/
rtc/ sensor/ serial/ spi/ timer/ watchdog/

当前裁剪树没有 Bluetooth 和 USB 的驱动实现目录,尽管部分公共头文件、binding 或 CMake 条件仍有残留。

公共 API 定义在 include/zephyr/drivers/<class>.h,具体实现通常以 compatible、Kconfig 和 CMake 三者共同选择。

subsys/

跨驱动的系统服务,主要包括:

  • loggingshellsettings
  • netfsstoragedisk
  • dfumgmt
  • pmrandomretention
  • ipcmodem
  • debugtracingtimingstats
  • zbusrtioinput
  • testsuite

当前树不包含 subsys/usbsubsys/bluetooth 实现,相关功能需恢复上游源码或通过外部模块提供。

lib/

可复用库和 C 运行时适配:

  • libc 与 POSIX;
  • C++ 支持;
  • heap、hash、CRC、ring buffer、JSON、CBOR;
  • compression、crypto glue、DSP;
  • OS abstraction 和 utilities。

内核机制一般位于 kernel/,不拥有系统线程或复杂策略的算法库通常位于 lib/,提供完整服务/协议的功能通常位于 subsys/

boards/soc/dts/

  • boards/:板级硬件组合、DTS、默认配置、runner;
  • soc/:SoC 系列启动、时钟、中断、pinmux 和内存布局;
  • dts/:架构/厂商 .dtsi、bindings schema、公共 include。

当前 3.7 树同时支持新的 hardware model v2。板描述通过 board.yml 指定 vendor、SoC 和 qualifiers。

cmake/scripts/

  • cmake/:Zephyr package、工具链、board/SoC 查找、Kconfig/DTS、linker、生成规则;
  • scripts/:Kconfiglib、DTS parser、gen_isr_tables、syscall generator、Twister、west commands、签名与 runner。

tests/samples/

  • samples/ 侧重可运行用法和文档示例;
  • tests/ 侧重自动断言、边界、负向和覆盖率;
  • testcase.yaml 描述 Twister 测试场景、平台约束和 tags;
  • subsys/testsuite/ztest 提供测试框架。

modules/share/

  • modules/ 中的 Kconfig/CMake glue 用于接入 workspace 外部模块;
  • share/zephyr-package 支持 find_package(Zephyr)
  • share/sysbuild 支持多 image 联合构建;
  • share/zephyrunsysbuild-package 等提供工具集成。

3. 核心对象关系

Thread

struct k_thread 保存线程架构上下文、调度状态、优先级、stack、timeout、资源池、userspace 权限等。调度器主要操作其内嵌的 struct _thread_base

Kernel

kernel/init.c 定义唯一全局实例:

1
struct z_kernel _kernel

其中包含 CPU 数组、ready queue、timeout queue、当前线程和调度状态。

Device

struct device 保存:

  • name
  • 只读 config
  • 可变 data
  • class-specific api
  • device state;
  • PM 对象和依赖 handle。

设备对象由 DEVICE_DT_DEFINE() 等宏静态生成。

Init entry

struct init_entry 同时承载 SYS_INIT 和 device init。宏把 entry 放到 .z_init_<level><prio>_<subprio>_ section,链接脚本排序后,z_sys_init_run_level() 顺序调用。

4. 编译期与运行时边界

编译期完成:

  • board/SoC/arch 选择;
  • DTS 合并和 binding 校验;
  • Kconfig 依赖求解;
  • 驱动实例生成;
  • syscall 和 userspace object 元数据生成;
  • init entry 排序;
  • ISR table 和 device dependency 生成;
  • section 和内存地址布局。

运行时完成:

  • 初始化硬件和设备;
  • 启动线程和调度;
  • 执行驱动 API;
  • 协议栈、文件系统和服务状态机;
  • runtime PM、动态线程、动态内存。

理解 Zephyr 问题时,应先判断问题发生在“生成期/链接期”还是“运行期”。例如:

  • undefined reference to __device_dts_ord_N:通常是 DTS 有节点但驱动未编译;
  • Kconfig symbol 未生效:配置求解问题;
  • device_is_ready() 为 false:设备 init 运行后失败;
  • API 返回 -ENOSYS:class 或功能路径未实现。

5. 启动总览

体系结构 reset 代码完成最小 CPU/内存准备后调用 z_cstart()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
z_cstart
├─ EARLY init
├─ arch_kernel_init
├─ 初始化日志核心和 dummy thread
├─ z_device_state_init
├─ PRE_KERNEL_1 init
├─ arch_smp_init
├─ PRE_KERNEL_2 init
├─ prepare_multithreading
│ ├─ 创建 idle threads
│ └─ 创建 main thread
└─ switch_to_main_thread
└─ bg_thread_main
├─ POST_KERNEL init
├─ boot banner
├─ z_init_static
├─ APPLICATION init
├─ 启动静态线程
├─ SMP init
└─ main()

6. 源码阅读建议

按以下顺序建立全局认识:

  1. kernel/init.c
  2. include/zephyr/init.hdevice.h
  3. kernel/thread.csched.ctimeout.c
  4. include/zephyr/kernel.h
  5. cmake/modules/zephyr_default.cmake
  6. cmake/modules/dts.cmakescripts/dts/
  7. 一个简单驱动,如 GPIO/UART,再看其 bindings
  8. subsys/loggingshellsettings
  9. 当前目标板 DTS、defconfig 和应用 overlay

Zephyr 构建系统、Kconfig 与设备树

Zephyr 构建系统、Kconfig 与设备树

1. 构建入口

典型应用:

1
2
3
4
cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(my_app)
target_sources(app PRIVATE src/main.c)

find_package(Zephyr) 找到 Zephyr package 后,加载 cmake/modules/zephyr_default.cmake。该文件按严格顺序加载:

1
2
3
4
5
6
7
8
9
10
workspace/application configuration
→ extensions/version/basic_settings
→ west/ccache/root
→ zephyr_module
→ boards/snippets/arch/hwm
→ configuration_files/generated directories
→ dts
→ kconfig
→ arch/soc
→ kernel.cmake

DTS 先于 Kconfig 的主要原因是部分 Kconfig 默认值和依赖可读取设备树信息。

2. west build 到最终镜像

1
2
3
4
5
6
7
8
9
10
11
west build -b <board> <app>
└─ west 调用 CMake configure
├─ 定位 Zephyr package
├─ 发现 modules、board、SoC、arch
├─ 合并并编译 devicetree
├─ 运行 Kconfig
├─ 选择 CMake source
├─ 生成 linker script
├─ 编译 libraries + application
├─ pre0/pre1/final 多阶段链接
└─ objcopy 生成 bin/hex

常见输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
build/
├─ CMakeCache.txt
├─ build.ninja
├─ zephyr/
│ ├─ .config
│ ├─ zephyr.dts
│ ├─ include/generated/zephyr/autoconf.h
│ ├─ include/generated/zephyr/devicetree_generated.h
│ ├─ zephyr_pre0.elf / zephyr_pre1.elf
│ ├─ zephyr.elf
│ ├─ zephyr.map
│ ├─ zephyr.bin
│ └─ zephyr.hex
└─ modules/

3. CMake target 模型

CMakeLists.txt 创建:

  • zephyr_interface:承载全局 include、defines、compile/link options;
  • zephyr:通用 catch-all library;
  • 多个 zephyr_library_named() 子库;
  • app:应用源码 target;
  • zephyr_pre0zephyr_pre1zephyr_final:链接阶段。

Zephyr 不直接把所有源文件堆入一个 target。各目录使用:

1
2
3
4
zephyr_library()
zephyr_library_sources(foo.c)
zephyr_library_sources_ifdef(CONFIG_FEATURE bar.c)
zephyr_library_include_directories(...)

因此“配置为 n”通常意味着源码根本不进入编译。

4. 多阶段链接

CMakeLists.txt 最多执行三阶段:

  1. zephyr_pre0
    • section 地址仍可能变化;
  2. zephyr_pre1
    • section 大小和地址固定;
  3. zephyr_final
    • 最终 ELF。

需要额外阶段的典型功能:

  • CONFIG_DEVICE_DEPS:生成 device dependency handles;
  • CONFIG_GEN_ISR_TABLES:生成 ISR table;
  • CONFIG_USERSPACE:生成 kernel object hash table;
  • userspace application memory partitions。

生成器必须先观察预链接 ELF 中的符号/section,再产生 C/二进制对象参与下一次链接。

5. Kconfig

总入口 Kconfig 只加载 Kconfig.zephyr。后者继续聚合 arch、SoC、board、drivers、subsys、lib 和 application Kconfig。

配置来源通常按以下层次组合:

1
2
3
4
5
6
7
8
SoC/board defconfig
+ application prj.conf
+ CONF_FILE / EXTRA_CONF_FILE
+ shield/snippet/sysbuild 配置
+ 命令行 cache 配置
→ Kconfig 求解
→ build/zephyr/.config
→ autoconf.h

5.1 关键概念

  • config:定义 symbol;
  • default:满足条件时给默认值;
  • depends on:控制 symbol 可见/可选;
  • select:强制另一个 bool 为 y,不检查其依赖,需谨慎;
  • imply:弱建议;
  • choice:互斥选择;
  • menuconfig:可进入子菜单的配置项。

最终真相是 .config,不是 prj.conf。用户请求值可能因依赖不满足而被丢弃,并产生 warning。

5.2 代码使用

1
2
3
4
5
6
7
#if defined(CONFIG_FEATURE)
#endif

if (IS_ENABLED(CONFIG_FEATURE)) {
}

BUILD_ASSERT(CONFIG_VALUE >= 4);

autoconf.h 通过构建系统自动注入,无需应用手动 include。

6. Devicetree 输入合并

设备树输入大致为:

1
2
3
4
5
6
7
8
architecture/SoC .dtsi
→ board .dts
→ revision/shield overlays
→ application overlay
→ DTC preprocessing
→ binding 匹配与 schema 校验
→ zephyr.dts
→ devicetree_generated.h

核心构建逻辑在 cmake/modules/dts.cmake,解析器位于 scripts/dts/,bindings 位于 dts/bindings/

7. Binding 与 compatible

binding YAML 描述:

  • compatible
  • include 的基础 schema;
  • properties 类型、required、enum、default;
  • child-binding
  • bus/child-bus;
  • specifier cells,如 gpio-cellsinterrupt-cells

设备节点:

1
2
3
4
5
sensor@48 {
compatible = "vendor,my-sensor";
reg = <0x48>;
status = "okay";
};

驱动:

1
2
3
4
5
6
7
8
9
#define DT_DRV_COMPAT vendor_my_sensor

#define MY_INIT(inst) \
DEVICE_DT_INST_DEFINE(inst, init_fn, NULL, \
&data_##inst, &config_##inst, \
POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, \
&api);

DT_INST_FOREACH_STATUS_OKAY(MY_INIT)

匹配不是运行时字符串扫描。compatible 经生成头转换为 C 宏,实例宏在编译期展开成静态对象。

8. 常用 DTS 宏

节点定位:

1
2
3
4
5
DT_NODELABEL(name)
DT_ALIAS(name)
DT_CHOSEN(name)
DT_PATH(...)
DT_DRV_INST(inst)

属性读取:

1
2
3
4
5
6
DT_PROP(node, prop)
DT_PROP_OR(node, prop, default)
DT_REG_ADDR(node)
DT_REG_SIZE(node)
DT_IRQN(node)
DT_ENUM_IDX(node, prop)

遍历与条件:

1
2
3
4
DT_NODE_HAS_STATUS(node, okay)
DT_HAS_COMPAT_STATUS_OKAY(compat)
DT_INST_FOREACH_STATUS_OKAY(fn)
DT_FOREACH_CHILD_STATUS_OKAY(node, fn)

设备规格:

1
2
3
4
5
GPIO_DT_SPEC_GET(...)
I2C_DT_SPEC_GET(...)
SPI_DT_SPEC_GET(...)
ADC_DT_SPEC_GET(...)
PWM_DT_SPEC_GET(...)

spec 结构把 controller struct device *、pin/address/channel 和 flags 打包,应用无需重复解析 phandle cells。

9. Board、SoC 与 Arch 选择

-b <board> 触发:

  1. 从 board roots 查找 board.yml
  2. 解析 board、revision、SoC/qualifier;
  3. 加载 board DTS 和 defconfig;
  4. SoC 描述选择 architecture;
  5. 工具链和 linker 根据 architecture/SoC 配置;
  6. board runner 决定 flash/debug 命令。

Hardware model v2 可用类似:

1
board[/soc[/cpucluster]]

一个 board.yml 可以声明多个 board 或多个 SoC 变体。

10. Zephyr modules

west manifest 中的 project 可通过 zephyr/module.yml 注册:

  • CMake;
  • Kconfig;
  • DTS root;
  • board/SoC root;
  • samples/tests;
  • sysbuild。

cmake/modules/zephyr_module.cmake 和脚本扫描 workspace,生成模块列表。模块不一定在 Zephyr 主树中,例如 MCUboot、HAL、crypto 库通常位于相邻目录。

11. Sysbuild

普通 build 只构建一个 Zephyr image;sysbuild 管理多个相互依赖 image:

1
2
3
4
5
sysbuild
├─ bootloader
├─ application
├─ network/radio core image
└─ signing/merge dependencies

每个 image 有独立:

  • CMake cache;
  • .config
  • DTS;
  • build 目录。

SB_CONFIG_* 控制 sysbuild 域,不能与 image 内 CONFIG_* 混用。MCUboot 联合构建会把 slot/footer、签名和依赖参数传递给应用 image。

12. Linker 与 iterable sections

Zephyr linker script 由 arch/SoC linker fragments、公共 section 定义和应用 snippets 组合。

关键用途:

  • .text/.rodata/.data/.bss/noinit
  • init entry;
  • device objects;
  • shell commands;
  • logging metadata;
  • ztest suites;
  • network L2;
  • userspace objects。

STRUCT_SECTION_ITERABLE(type, name) 把对象放入命名 section;运行时使用 STRUCT_SECTION_FOREACH() 遍历。该模式减少显式注册代码和动态分配。

13. 常用构建命令

1
2
3
4
5
6
7
8
west build -b <board> -d build/<name> <app>
west build -d build/<name> -t menuconfig
west build -d build/<name> -t guiconfig
west build -d build/<name> -t rom_report
west build -d build/<name> -t ram_report
west build -d build/<name> -t pristine
west flash -d build/<name>
west debug -d build/<name>

需要完全重新求解 board/DTS/Kconfig 时使用:

1
west build -p always ...

不要通过手改 build/zephyr/.config 或生成头文件保存配置;下次 configure 会覆盖。

14. 构建问题定位

Kconfig warning

检查:

  • symbol 是否真实存在;
  • 依赖是否满足;
  • 是否被 choice 排除;
  • .config 中最终值;
  • menuconfig 中 help 和 dependency。

DTS error

检查:

  • build/zephyr/zephyr.dts
  • 对应 binding;
  • status = "okay"
  • reg、interrupt、clock、pinctrl cells;
  • overlay 是否实际被 CMake 发现。

__device_dts_ord_N

devicetree_generated.h 查 ordinal 对应节点,然后确认:

  • 节点 enabled;
  • 驱动 Kconfig 为 y;
  • 驱动 CMake 加入源文件;
  • compatible 与 DT_DRV_COMPAT 一致;
  • bus/controller device 也已创建。

链接溢出

查看 zephyr.maprom_reportram_report,区分:

  • flash payload;
  • RAM .data/.bss/noinit
  • thread stack;
  • heap;
  • logging buffers;
  • MCUboot header/trailer 保留区。

Zephyr 系统启动、线程与调度器

Zephyr 系统启动、线程与调度器

1. 从 Reset 到 z_cstart

各架构负责:

  1. 设置 CPU mode 和初始 stack;
  2. 必要时复制 .data、清理早期状态;
  3. 初始化最基本的 exception/vector 环境;
  4. 调用通用 C 入口 z_cstart()

具体 reset 入口位于 arch/<arch>/core/ 或 SoC startup。通用内核不假定某一种向量表或上下文格式。

2. z_cstart() 流程

kernel/init.c:z_cstart() 是通用启动主线:

1
2
3
4
5
6
7
8
9
10
11
12
13
z_cstart()
├─ gcov_static_init()
├─ z_sys_init_run_level(EARLY)
├─ arch_kernel_init()
├─ LOG_CORE_INIT()
├─ 初始化 dummy thread
├─ z_device_state_init()
├─ z_sys_init_run_level(PRE_KERNEL_1)
├─ arch_smp_init()
├─ z_sys_init_run_level(PRE_KERNEL_2)
├─ stack canary / early random
├─ prepare_multithreading()
└─ switch_to_main_thread()

PRE_KERNEL 阶段仍在 interrupt/early boot stack 上运行,不能使用会阻塞或依赖完整调度器的内核服务。

3. Main thread

prepare_multithreading()

  • 初始化 CPU 0;
  • 为每个 CPU 创建 idle thread;
  • 使用 z_setup_new_thread() 创建 z_main_thread
  • 入口为 bg_thread_main()
  • priority 为 CONFIG_MAIN_THREAD_PRIORITY
  • stack 为 CONFIG_MAIN_STACK_SIZE
  • 标记 main thread ready。

切换到 main thread 后执行:

1
2
3
4
5
6
7
8
9
10
bg_thread_main()
├─ POST_KERNEL init
├─ boot_banner()
├─ z_init_static()
├─ APPLICATION init
├─ z_init_static_threads()
├─ z_smp_init()
├─ SMP init level
├─ memory management finish
└─ main()

应用 main() 返回后,main thread 正常结束;其他线程和 idle 继续运行。

4. 初始化级别

include/zephyr/init.h 定义:

  1. EARLY
  2. PRE_KERNEL_1
  3. PRE_KERNEL_2
  4. POST_KERNEL
  5. APPLICATION
  6. SMP

SYS_INIT(fn, level, prio) 生成 struct init_entry 并放入:

1
.z_init_<level><priority>_<subpriority>_

链接脚本按 section name 排序。z_sys_init_run_level() 遍历起止符号并调用:

  • 系统 entry:entry->init_fn.sys()
  • device entry:设备初始化包装函数。

同一 level 的 priority 必须是 0–99 的十进制字面量或能直接展开为该字面量的宏,不能是算术表达式。

5. 线程对象

struct k_thread 的重要内容:

  • 架构保存上下文;
  • struct _thread_base 调度字段;
  • stack 信息;
  • entry 参数和返回状态;
  • timeout;
  • resource pool;
  • userspace permission/object metadata;
  • thread name、custom data、runtime stats;
  • SMP CPU mask/当前 CPU;
  • join、abort、suspend 状态。

线程 ID k_tid_t 实际是 struct k_thread *

6. 创建线程

静态线程

1
2
K_THREAD_DEFINE(name, stack_size, entry,
p1, p2, p3, prio, options, delay);

宏生成:

  • stack;
  • struct k_thread
  • _static_thread_data iterable entry。

启动时 z_init_static_threads() 先调用 z_setup_new_thread(),再按 delay 调度。

动态线程

1
2
3
k_thread_create(thread, stack, stack_size,
entry, p1, p2, p3,
prio, options, delay);

主要路径:

1
2
3
4
5
6
7
8
k_thread_create
→ z_impl_k_thread_create
→ setup_thread_stack
→ z_setup_new_thread
→ arch_new_thread
→ thread_schedule_new
→ z_ready_thread
→ z_reschedule

架构层负责把初始 stack 构造成首次 context switch 后能进入 thread entry 的格式。

7. 优先级模型

数值越小,逻辑优先级越高:

1
2
3
4
5
6
7
负数:cooperative priorities
-CONFIG_NUM_COOP_PRIORITIES ... -1

非负:preemptible priorities
0 ... CONFIG_NUM_PREEMPT_PRIORITIES - 1

最低:idle priority

宏:

1
2
3
4
5
K_PRIO_COOP(x)
K_PRIO_PREEMPT(x)
K_HIGHEST_THREAD_PRIO
K_LOWEST_APPLICATION_THREAD_PRIO
K_IDLE_PRIO

Cooperative thread 在主动阻塞、yield、终止或遇到 meta-IRQ thread 前不会被普通线程抢占。Preemptible thread 可被更高优先级 ready thread 抢占。

8. Ready queue 实现

调度策略由 Kconfig 选择:

  • simple/dumb queue:适合极少线程,线性查找;
  • multi-queue:按优先级维护队列;
  • scalable:红黑树,适合较多线程,插入/删除为 O(log n)。

抽象位于 kernel/priority_queues.c 和相关头文件。调度器不需要知道底层容器细节,只操作:

  • queue thread;
  • dequeue thread;
  • best/next thread。

选择依据不是“算法越复杂越好”,而是线程数量、RAM/ROM 和调度延迟需求。

9. 调度核心

kernel/sched.c 关键函数:

  • z_ready_thread():把线程加入 ready queue;
  • z_unready_thread():移除 ready 状态;
  • next_up():选择下一线程;
  • update_cache():更新 cached next thread;
  • z_reschedule():判断是否需要切换;
  • z_swap():完成通用调度状态转换并调用 arch switch;
  • z_pend_curr():当前线程挂入 wait queue;
  • z_unpend_first_thread():唤醒等待者;
  • z_impl_k_yield():同优先级让出。

典型抢占路径:

1
2
3
4
5
6
7
8
ISR/线程使高优先级线程 ready
→ ready_thread()
→ update_cache()
→ z_reschedule()
→ need_swap()
→ z_swap()
→ next_up()
→ arch_switch()

10. Wait queue 与阻塞

信号量、mutex、queue 等对象包含 wait queue。阻塞流程:

1
2
3
4
5
6
检查资源不可用
→ 持有 object/scheduler spinlock
→ add_to_waitq_locked()
→ 设置 timeout
→ 当前线程退出 ready 状态
→ z_swap()

唤醒流程:

1
2
3
4
5
资源变为可用
→ 选择 wait queue 最高优先级线程
→ 取消 timeout
→ z_ready_thread()
→ 必要时抢占当前线程

11. Scheduler lock、IRQ lock 与 Spinlock

  • k_sched_lock():阻止当前 CPU 上普通抢占,不关闭中断;
  • irq_lock():架构级屏蔽/锁定中断,临界区应极短;
  • k_spin_lock():SMP 互斥并保存 IRQ 状态;
  • mutex:线程级可睡眠锁,支持 priority inheritance。

不能用 scheduler lock 替代 SMP 数据保护;另一个 CPU 仍可能并发访问。

12. 时间片

CONFIG_TIMESLICING 启用同优先级 preemptible threads 的轮转:

  • kernel/timeslicing.c 管理 slice;
  • CONFIG_TIMESLICE_SIZE 设置 tick 数;
  • CONFIG_TIMESLICE_PRIORITY 决定应用范围;
  • thread 可配置自定义 time slice callback。

更高优先级唤醒仍立即抢占,时间片只解决同优先级公平性。

13. Timeout

kernel/timeout.c 管理全局 timeout queue。典型 timeout 来源:

  • k_sleep()
  • semaphore/mutex/poll 等限时等待;
  • timer;
  • delayed work;
  • thread delayed start。

timeout 使用 tick 绝对/相对截止时间,并通过 delta queue 或配置的 timeout 容器管理。系统时钟 driver 到期后调用 sys_clock_announce(),推进 timeout 并唤醒对象。

Tickless 模式会让 timer driver 直接编程到最近 deadline,减少空闲 tick 中断。

14. 中断与调度

ISR 中可以调用标记为 ISR-safe 的 API,使线程 ready。是否在 ISR 退出时切换取决于:

  • 是否有更高优先级 ready thread;
  • scheduler lock;
  • 当前线程 cooperative/preemptible;
  • 架构的 interrupt exit/swap 实现。

ISR 不应调用可能睡眠的 API。k_is_in_isr() 可用于防御检查,但正确做法是按 API 文档设计上下文。

15. SMP

CONFIG_SMP 下:

  • _kernel.cpus[] 保存每 CPU 当前线程;
  • shared ready queue 受 scheduler spinlock 保护;
  • thread 可设置 CPU mask;
  • IPI 通知其他 CPU reschedule;
  • idle thread 每 CPU 一个;
  • next_up() 需避免同一 thread 被多个 CPU 同时选中;
  • meta-IRQ threads 提供接近中断底半部的高优先级语义。

SMP 中即使本 CPU 不发生 switch,也可能需要发送 pending IPI。

16. 实时性注意事项

影响 worst-case latency 的因素:

  • 长时间 IRQ lock/spinlock;
  • cooperative 高优先级线程不阻塞;
  • 大量同优先级 ready threads;
  • logging immediate mode;
  • flash erase/write;
  • cache miss 和外部 memory;
  • driver ISR 工作量;
  • userspace syscall/MPU 切换;
  • SMP lock contention。

实时性评估应测量最大值和尾延迟,而不只看平均 context-switch 时间。

Zephyr 内核对象、同步与内存管理

Zephyr 内核对象、同步与内存管理

1. 内核对象的共同模式

多数内核对象包含:

  • 当前状态或资源计数;
  • wait queue;
  • spinlock/全局调度锁保护;
  • iterable/object core 元数据;
  • userspace 权限元数据。

API 的典型结构:

1
2
3
立即路径:资源可用 → 更新状态 → 返回
阻塞路径:资源不可用 → 当前线程 pend + timeout → context switch
唤醒路径:生产资源 → unpend 最高优先级等待者 → 可能触发抢占

2. Semaphore

实现:kernel/sem.c

1
2
3
K_SEM_DEFINE(sem, initial, limit);
k_sem_take(&sem, timeout);
k_sem_give(&sem);
  • count 大于 0 时 take 直接减一;
  • count 为 0 时线程进入 wait queue;
  • give 优先直接唤醒等待者,否则递增 count;
  • ISR 可 givetake 仅能使用不阻塞语义。

适合事件计数和有界资源,不携带数据。

3. Mutex

实现:kernel/mutex.c

1
2
3
K_MUTEX_DEFINE(lock);
k_mutex_lock(&lock, timeout);
k_mutex_unlock(&lock);

特性:

  • 记录 owner;
  • 支持递归 lock count;
  • priority inheritance 缓解优先级反转;
  • 只能在线程上下文使用;
  • owner 才能 unlock。

继承提高 owner 的有效优先级;释放嵌套 mutex 时需要重新计算其可恢复优先级。

4. Condition variable

实现:kernel/condvar.c

condition variable 与 mutex 配合:

1
2
3
4
5
6
lock mutex
while (!predicate) {
k_condvar_wait(cond, mutex, timeout)
}
consume state
unlock mutex

wait 原子释放 mutex 并阻塞;被唤醒后重新获得 mutex。必须循环检查 predicate,不能假定一次 signal 就代表条件仍成立。

5. Events

实现:kernel/events.c

k_event 保存 bitset:

  • post/set/clear bits;
  • 等待 any/all;
  • 可选择 reset;
  • 一次表达多个逻辑事件。

适合控制状态组合;大量数据仍应通过 queue/msgq 传输。

6. Queue、FIFO 与 LIFO

实现:kernel/queue.c

  • k_queue:通用 intrusive queue;
  • k_fifo:尾插头取;
  • k_lifo:头插头取。

入队元素需提供保留 link 字段,或使用 alloc append 让内核分配 wrapper。ISR 可进行非阻塞 put/get,具体限制以 API 为准。

7. Message queue

实现:kernel/msg_q.c

1
2
3
K_MSGQ_DEFINE(q, msg_size, max_msgs, align);
k_msgq_put(&q, &msg, timeout);
k_msgq_get(&q, &msg, timeout);

消息按值复制到固定环形 buffer:

  • 内存上界确定;
  • producer 与 consumer 解耦;
  • 消息大小固定;
  • put/get 均可阻塞。

适合小型控制消息;大 payload 可传 buffer pointer,但必须管理生命周期。

8. Mailbox 与 Pipe

  • kernel/mailbox.c:同步/异步 mailbox message,支持直接或缓冲传递;
  • kernel/pipes.c:字节流 buffer,可读写部分数据并阻塞。

若只需要固定消息,优先 msgq;连续字节流才考虑 pipe/ring buffer。

9. Poll

实现:kernel/poll.c

k_poll() 一次等待多个对象:

  • semaphore available;
  • queue data available;
  • signal raised;
  • ignore。

对象状态变化时通知 poller。复杂事件循环可借助 poll,但应控制 event 数量和并发 registration 生命周期。

10. Workqueue

实现:

  • kernel/work.c
  • kernel/system_work_q.c

对象:

  • k_work:普通 work;
  • k_work_delayable:带 timeout;
  • k_work_poll:poll 触发;
  • k_work_q:自定义 worker thread。

流程:

1
2
3
4
5
k_work_submit()
→ work 标记 queued
→ 放入 workqueue
→ worker thread 取出
→ 清状态并调用 handler

system workqueue 是共享线程。handler 阻塞过久会延迟所有共用者。长期 I/O、flash erase 或不可信 callback 应使用独立 workqueue。

取消 delayable work 时要区分:

  • 尚未到期;
  • 已排队;
  • 正在执行;
  • handler 内重新提交。

需要严格同步可使用 k_work_cancel_sync() / flush API。

11. Timer

实现:kernel/timer.c,底层依赖 timeout。

1
2
K_TIMER_DEFINE(timer, expiry_fn, stop_fn);
k_timer_start(&timer, duration, period);

expiry callback 通常在系统 clock interrupt 或相关内核上下文执行,不能做阻塞操作。复杂处理应提交 work。

k_timer_status_sync() 可在线程中等待到期次数。

12. Heap

kernel/kheap.c 把系统 heap 封装为内核 API:

1
2
k_malloc / k_calloc / k_realloc / k_free
k_heap_alloc / k_heap_free

底层 heap 实现在 lib/heap/,支持按 bucket/size class 管理空闲块。动态分配具有碎片和时延不确定性:

  • 初始化期分配、运行期固定持有较安全;
  • 高频实时路径优先 slab/固定池;
  • 检查 CONFIG_HEAP_MEM_POOL_SIZE
  • userspace thread 可绑定 resource pool。

13. Memory slab

实现:kernel/mem_slab.c

1
2
3
K_MEM_SLAB_DEFINE(slab, block_size, num_blocks, align);
k_mem_slab_alloc(&slab, &ptr, timeout);
k_mem_slab_free(&slab, ptr);

特点:

  • 固定大小 block;
  • 无外部碎片;
  • 分配/释放时间更稳定;
  • 可阻塞等待空闲块。

适合 network packet metadata、消息节点、固定协议对象。

14. Stack 与线程内存

线程 stack 使用 K_THREAD_STACK_DEFINE() 等宏,而非普通 byte array,原因包括:

  • MPU/MMU guard;
  • userspace privilege stack;
  • architecture alignment;
  • stack metadata;
  • sanitizer。

常用诊断:

  • CONFIG_STACK_SENTINEL
  • CONFIG_STACK_CANARIES
  • CONFIG_THREAD_STACK_INFO
  • CONFIG_INIT_STACKS
  • thread analyzer;
  • k_thread_stack_space_get()

stack overflow 可能破坏邻近对象,不能只按平均使用量估算。

15. Userspace

主要文件:

  • kernel/userspace.c
  • kernel/userspace_handler.c
  • kernel/mem_domain.c
  • include/zephyr/syscall.h
  • scripts/build/gen_syscalls.py

启用 CONFIG_USERSPACE 后:

  • user thread 以非特权模式运行;
  • kernel object 有权限位图/哈希表;
  • syscall 进入 verifier;
  • verifier 检查 object type、权限、buffer address;
  • 实际实现为 z_impl_*
  • MPU/MMU memory domain 限制访问范围。

典型 API 生成模式:

1
2
3
4
5
6
public syscall declaration
→ 生成 inline marshalling
→ architecture trap
→ z_vrfy_api()
→ validation
→ z_impl_api()

userspace 提升隔离但增加代码、RAM、syscall 和 context-switch 成本。

16. Memory domain

k_mem_domain 把 memory partitions 分配给 user threads:

  • code/data partition;
  • application shared memory;
  • thread 加入/移出 domain;
  • architecture MPU/MMU 更新。

分区数量受硬件 MPU region 数量约束。动态切换前应确认 alignment 和 region 合并规则。

17. Object core 与 Runtime stats

kernel/obj_core.c 提供统一对象注册/查询基础,可用于:

  • thread/device/kernel object 枚举;
  • runtime statistics;
  • shell/debug 工具;
  • tracing。

CONFIG_OBJ_CORE 及 stats 选项会增加 metadata 和运行期开销。

18. Fatal error

kernel/fatal.c 处理:

  • CPU exception;
  • stack check failure;
  • kernel oops/panic;
  • essential thread abort;
  • unhandled fault。

路径通常为:

1
2
3
arch fault
→ z_fatal_error(reason, esf)
→ k_sys_fatal_error_handler()

应用可覆盖 fatal handler,但生产系统必须避免在状态已损坏时盲目继续。常见策略是记录最小 crash 信息、触发 watchdog/reset,并由 boot/update 策略处理重复崩溃。

19. 对象选择建议

  • 计数/唤醒:semaphore;
  • 保护共享结构:mutex;
  • 等待 predicate:condvar;
  • 多 bit 状态:event;
  • 固定小消息:msgq;
  • 指针对象:fifo/queue;
  • 延后 ISR 工作:workqueue;
  • 固定块内存:slab;
  • 可变大小低频内存:heap;
  • 等待多个来源:poll。

选择错误会导致不必要复制、动态分配、优先级反转或 system workqueue 阻塞。