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 阻塞。

文章互动

阅读 --

留言

0 条留言

正在加载留言…