文章目录[隐藏]
核心速览

线程
进程:系统资源分配的最小单位,拥有独立地址空间
线程:进程内独立执行的最小单位,共享进程资源线程是一个内核对象,用于处理因太长或太复杂而无法由 ISR 执行的应用程序。
应用程序可以定义任意数量的线程(仅受可用 RAM 限制)。每个线程都由一个线程 ID引用,该 ID 在线程创建时分配。
线程状态
不存在任何阻止其执行的因素的线程被视为
Ready,并且有资格被选为当前线程。如果有一个或多个因素阻止其执行,则线程将被视为
unReady,并且不能被选为当前线程。

任务/线程状态说明
| 状态 | 描述 | 典型进入方式 | 典型离开方式 |
|---|---|---|---|
New |
已创建控制块但未加入调度器就绪结构 | 创建函数分配 TCB/栈 | start 加入就绪队列 (→ Ready) |
Ready |
可运行,除 CPU 外资源具备 | start;事件完成;resume;被抢占/时间片用尽 (Running→Ready) |
被调度 (dispatch) 获得 CPU (→ Running);suspend (→ Suspended);abort (→ Terminated) |
Running |
正在占用 CPU 执行 | 调度器选择某 Ready 任务 | 阻塞等待 (→ Waiting);被更高优先级抢占 (→ Ready);suspend (→ Suspended);正常结束或 abort (→ Terminated) |
Waiting (Blocked) |
等待某特定同步/IO/时间条件 | 阻塞/等待/睡眠 API (Running→Waiting) | 条件满足/超时 (→ Ready);abort (→ Terminated) |
Suspended |
主动(管理/调试)冻结,不因事件自动恢复 | suspend 从 Running/Ready |
resume (→ Ready);abort (→ Terminated) |
Terminated |
生命周期结束,等待或已完成清理 | 正常返回 / abort |
终态 |
图中转换术语/箭头标注
| 术语 | 来源→目标 | 含义/触发条件 | Zephyr 常见 API/触发场景 |
|---|---|---|---|
| start | New → Ready | 把新建任务纳入调度器 | k_thread_create(调用完成即 Ready) |
| dispatch | Ready → Running | 调度器选择并切换上下文 | 中断返回、优先级变化、k_yield()、时间片用尽 |
| interrupt (抢占) | Running → Ready | 更高优先级任务就绪或时间片耗尽 | ISR 中 k_sem_give 等引入高优先级 Ready;时间片轮转 |
| I/O or event wait | Running → Waiting | 阻塞等待同步对象/事件/定时 | k_sem_take、k_msgq_get、k_poll、k_sleep、k_mutex_lock(阻塞) |
| I/O or event completion | Waiting → Ready | 等待条件满足或超时到期 | k_sem_give、k_msgq_put、k_poll_signal_raise、定时器回调 |
| suspend | Running/Ready → Suspended | 管理性冻结 | k_thread_suspend(thread) |
| resume | Suspended → Ready | 解除冻结 | k_thread_resume(thread) |
| abort | 任意活动态 → Terminated | 强制结束/异常 | k_thread_abort(thread)、致命错误路径 |
| (正常结束) | Running → Terminated | 线程函数执行完毕 | 线程入口函数 return(或 k_thread_exit() 若提供) |
Zephyr 常见 API 映射速查
| 类别 | 操作 | API 示例 | 备注 |
|---|---|---|---|
| 创建/启动 | 创建并 Ready | k_thread_create |
返回前线程已 Ready |
| 主动让出 | 让出 CPU | k_yield() |
同优先级轮转 |
| 休眠/延时 | 进入 Waiting | k_sleep(K_MSEC(n)) |
超时后 Ready |
| 同步等待 | 阻塞等待 | k_sem_take(sem, K_FOREVER) |
资源可用 Ready;可设超时 |
| 释放同步 | 唤醒等待者 | k_sem_give(sem) |
可能触发抢占 |
| 消息队列 | 等待/唤醒 | k_msgq_get / k_msgq_put |
put 唤醒接收者 |
| 事件轮询 | 多源等待 | k_poll(events, n, timeout) |
任一事件触发 |
| 互斥锁 | 获取/释放 | k_mutex_lock / k_mutex_unlock |
优先级继承机制 |
| 挂起 | 冻结线程 | k_thread_suspend |
不因事件恢复 |
| 恢复 | 解冻 | k_thread_resume |
回 Ready 可抢占 |
| 中止 | 强制结束 | k_thread_abort |
清理后终止 |
| 优先级调整 | 可能抢占 | k_thread_priority_set |
提高优先级可能立即运行 |
| 定时器回调 | 间接唤醒 | k_timer_start + 回调中 give |
事件完成 |
| 正常退出 | 结束线程 | 线程函数 return |
进入 Terminated |
记忆要点
| 关键词 | 核心一句话 |
|---|---|
| Ready vs Running | 本质区别:是否占用 CPU |
| Waiting | 等具体条件/时间,条件满足自动 Ready |
| Suspended | 人为冻结,仅 resume 解冻 |
| abort | 强制终止生命周期 |
| dispatch | 调度器执行上下文切换 |
| interrupt (抢占) | 高优先级就绪挤掉当前 Running |
线程堆栈对象
每个线程都需要自己的堆栈缓冲区,以便 CPU 推送上下文。
核心速览
- 在未开启用户态 (
CONFIG_USERSPACE) 的普通配置里:每个线程只有“一段栈”,既是线程栈也是内核执行用栈;谈不上区分。 - 在启用用户态/特权分离时:一个“线程堆栈对象”逻辑上包含(或关联)两段用途不同的栈:用户线程栈(User / Thread Stack)+ 特权/内核栈(Privileged / Kernel Stack)。线程在用户态运行时用用户栈,陷入系统调用或进入内核 特权区段时切换到该线程自己的内核栈。
- 另有“中断栈”(ISR stack) 常是全局独立的一段,与每个线程的两段无直接重叠,用于中断服务例程,目的:减小每个线程预留空间。
术语定义
| 名称 | 也常被称为 | 作用域 | 使用场景 | 主要目的 |
|---|---|---|---|---|
| Thread Stack (用户线程栈) | User Stack / Unprivileged Stack | 该线程私有 | 线程处于用户态执行普通函数、库代码 | 提供用户态栈帧、局部变量、用户函数调用深度 |
| Kernel Stack (特权栈) | Privileged Stack / 内核栈 | 同一线程私有 | 线程执行系统调用、陷入特权服务、内核需要在该线程上下文里运行时(非中断) | 隔离内核关键数据与返回地址,不暴露给用户态;满足特权模式栈安全 |
| ISR Stack | 中断栈 | 全局或每 CPU | 硬件中断/异常处理 | 避免为每线程预留过大的中断处理空间;统一管理嵌套中断栈深度 |
为什么需要区分(开启用户态时)
- 安全隔离:用户代码不应随意读写内核的调用帧与局部变量。通过切换到独立的特权栈,用户态即便栈溢出也难以破坏内核。
- 减少攻击面:系统调用入口只保存/恢复有限上下文到内核栈;用户栈指针在内核态被隔离不用。
- 更容易做 MPU / MMU 保护:用户栈可设为用户权限读写;内核栈仅特权访问。
- 内核栈尺寸可更小:内核路径较浅(系统调用包装+少量调度/对象操作),不需要和用户函数一样的深度。
配置选项(示例)
| 配置 | 作用 |
|---|---|
| CONFIG_USERSPACE | 启用用户态/内核态分离(触发双栈模型) |
| CONFIG_PRIVILEGED_STACK_SIZE | 每线程内核(特权)栈大小 |
| CONFIG_ISR_STACK_SIZE | 全局 ISR 栈大小 |
| CONFIG_STACK_SENTINEL | 在栈尾放置哨兵检测溢出 |
| CONFIG_INIT_STACKS | 初始化栈填充固定字节,便于事后统计使用峰值 |
| CONFIG_THREAD_STACK_INFO | 允许查询并调试输出栈利用情况 |
线程栈静态与动态实例化宏速查
| 用途 | 宏 | 适用场景 | 说明 |
|---|---|---|---|
| 定义单个静态线程栈 | K_THREAD_STACK_DEFINE(name, size) |
最常用 | 生成符号 name(类型 k_thread_stack_t[]),供 k_thread_create |
| 定义线程栈数组 | K_THREAD_STACK_ARRAY_DEFINE(name, count, size) |
同尺寸多线程 | 产生 name[i] |
| 在结构体内嵌成员栈 | K_THREAD_STACK_MEMBER(name, size) |
封装线程控制块与栈 | 用于把线程控制块 + 栈打包 |
| 仅内核专用栈(无需用户态部分) | K_KERNEL_STACK_DEFINE(name, size) |
特权线程、协程、内部用途 | 不含用户态/特权分离额外逻辑 |
| 内核专用栈数组 | K_KERNEL_STACK_ARRAY_DEFINE(name, count, size) |
批量内核栈 | 同上 |
| 取静态定义的“可传入 create 的尺寸” | K_THREAD_STACK_SIZEOF(name) |
搭配上述 DEFINE | 返回实际可用长度(含对齐/附加区) |
| 计算动态分配所需原始字节数 | K_THREAD_STACK_LEN(size) |
动态分配 | 输入“期望逻辑线程栈大小”(用户栈),输出需要 malloc 的真实大小 |
| 取得底层缓冲区指针(调试/填充) | K_THREAD_STACK_BUFFER(name) |
可选 | 返回 uint8_t * 指向栈起始(不常用) |
注意:开启
CONFIG_USERSPACE时,给出的size表示“用户线程栈”目标大小;实际占用会再加上特权栈、保护区等,因此需要用K_THREAD_STACK_SIZEOF(静态)或K_THREAD_STACK_LEN(动态)获取最终字节数。
快速记忆:
静态:K_THREAD_STACK_DEFINE + K_THREAD_STACK_SIZEOF
动态:K_THREAD_STACK_LEN 计算,再 k_malloc + k_thread_create
放结构体:K_THREAD_STACK_MEMBER
特权专用:K_KERNEL_STACK_DEFINE
线程优先级
线程的优先级是一个整数值,可以是负数,也可以是非负数。数值较低的优先级优先于数值较高的优先级。
调度程序根据每个线程的优先级区分两类线程。
- 协作线程的优先级为负数。一旦成为当前线程,协作线程将一直保持当前线程状态,直到执行使其变为非就绪状态的操作。
-
可抢占线程具有非负优先级值。一旦成为当前线程,只要协作线程、更高或同等优先级的可抢占线程就绪,它就可能随时被取代。
线程启动后,其初始优先级可以调高或调低。因此,通过改变优先级,可抢占线程可以变为协作线程,反之亦然。
Meta-IRQ 优先级
Meta-IRQ 优先级是 Zephyr 中介于 “真正的硬件中断 (ISR)” 和 “普通线程” 之间的一类特殊线程优先级层次。它让一段必须在“几乎与中断一样快”执行、又需要线程上下文(允许使用比 ISR 更宽松的 API)的小代码块,在中断退出后、任何普通线程恢复运行之前立即得到调度。
调度层级关系(从高到低)
- 硬件 ISR(可嵌套)
- ISR 退出后就绪的 Meta-IRQ 线程
- 最高优先级的 cooperative 线程
- 其他 cooperative 线程
- 各级 preemptive 线程(普通抢占式)
- 空闲线程
核心特性
| 特性 | 说明 |
|---|---|
| 优先级区间独立 | 由 CONFIG_NUM_METAIRQ_PRIORITIES 配置;0 表示禁用 |
| 抢占能力 | 可抢占所有 cooperative / preemptive 普通线程 |
| 线程上下文 | 可用大部分线程 API(应避免阻塞) |
| 获得执行时机 | 中断退出时若其就绪 → 立即运行 |
| 目的 | 承载“中断下半部 / 软中断”处理:比普通线程快、比 ISR 更宽松 |
与其他上下文对比
| 维度 | ISR | Meta-IRQ 线程 | Cooperative 线程 | Preemptive 线程 |
|---|---|---|---|---|
| 来源 | 硬件事件 | 调度器(中断后) | 调度器 | 调度器 |
| 可用 API | 极少(不可阻塞) | 大部分线程 API(应避免阻塞) | 全部线程 API | 全部线程 API |
| 是否可被抢占 | 可被更高/同级中断 | 仅被 ISR 抢占 | 不被 preemptive 抢占 | 可被更高优先级 preemptive 抢占 |
| 是否抢占 cooperative | 不适用 | 可以 | N/A | 不会(cooperative 优先级固定) |
| 适合代码长度 | 极短 | 很短 | 中等 | 任意 |
| 典型用途 | 采样/寄存器快照 | 协议解析、快速调度后续任务 | 主循环、控制逻辑 | 后台/批量处理 |
配置项关键
| 配置 | 作用 |
|---|---|
CONFIG_NUM_METAIRQ_PRIORITIES |
指定 Meta-IRQ 可用优先级数量;0=禁用 |
CONFIG_THREAD_METAIRQ_PRIO(若存在辅助宏) |
有些版本提供便捷宏表示该层级 |
CONFIG_THREAD_PRIORITIES |
系统总优先级数量(包含 Meta-IRQ 区段) |
创建线程
- 创建线程:
k_thread_create() - 静态分配堆栈:
K_THREAD_STACK_DEFINE(name, size)使用普通线程栈(放在线程上下文)K_KERNEL_STACK_DEFINE(name, size)分配“内核专用”栈(仅在需要单独内核栈的配置下使用)
- 动态分配堆栈:
k_thread_stack_alloc(size):运行时分配线程栈k_thread_stack_free(ptr):释放动态栈,在线程结束后调用
运行时创建(静态栈)示例
#define MY_STACK_SIZE 500 /* 线程栈字节数(需≥最小栈要求,调试可加大) */
#define MY_PRIORITY 5 /* 数值越小优先级越高;确认调度策略 */
extern void my_entry_point(void *, void *, void *); /* 线程入口,固定三个 void* 参数 */
K_THREAD_STACK_DEFINE(my_stack_area, MY_STACK_SIZE); /* 定义一块可用于普通线程的栈内存 */
struct k_thread my_thread_data; /* 线程控制块(TCB),需保持存活 */
k_tid_t my_tid = k_thread_create(&my_thread_data, /* 线程控制块指针 */
my_stack_area, /* 栈指针(宏定义区域名) */
K_THREAD_STACK_SIZEOF(my_stack_area), /* 计算实际可用大小(含填充) */
my_entry_point, /* 入口函数 */
NULL, NULL, NULL, /* 传递给入口的 p1,p2,p3 */
MY_PRIORITY, /* 线程优先级 */
0, /* 线程选项 flags(如必要可传 K_INHERIT_PERMS 等) */
K_NO_WAIT); /* 启动延时:K_NO_WAIT 立即就绪 */
说明:
- 使用该方式可以在运行时按需创建多个线程,需自行管理其生命周期。
- 若线程需要手动结束,可在线程函数内 return,或外部 k_thread_abort(my_tid)。
编译期定义(宏自动打包)
#define MY_STACK_SIZE 500
#define MY_PRIORITY 5
extern void my_entry_point(void *, void *, void *);
K_THREAD_DEFINE(my_tid, /* 线程符号(同时也是 k_tid_t) */
MY_STACK_SIZE, /* 栈大小 */
my_entry_point, /* 入口函数 */
NULL, NULL, NULL, /* p1,p2,p3 参数 */
MY_PRIORITY, /* 优先级 */
0, /* 线程选项 */
0); /* 启动延时:0 == 立即调度 */
说明:
- 宏自动生成:栈、TCB、线程句柄并放入指定段,系统启动时创建并启动。
- 适合固定常驻线程,减少手写样板代码。
动态分配栈 + 等待结束 + 释放
#define MY_PRIORITY 5
extern void my_entry_point(void *, void *, void *);
struct k_thread my_thread_data;
k_tid_t my_tid;
void *my_stack_area;
/* 动态分配一块线程栈;大小通常使用配置项或自定义值 */
my_stack_area = k_thread_stack_alloc(CONFIG_DYNAMIC_THREAD_STACK_SIZE);
if (my_stack_area == NULL) {
/* 分配失败的错误处理 */
}
my_tid = k_thread_create(&my_thread_data,
my_stack_area,
CONFIG_DYNAMIC_THREAD_STACK_SIZE,
my_entry_point,
NULL, NULL, NULL,
MY_PRIORITY, 0, K_NO_WAIT);
/* 等待线程结束(需要启用 CONFIG_THREAD_JOINABLE / 或自动支持 join 的配置) */
k_thread_join(my_tid, K_FOREVER);
/* 线程已结束,回收动态栈内存 */
k_thread_stack_free(my_stack_area);
说明:
- 适合线程数量/栈需求“波动”场景,释放后可降低内存占用峰值。
- 一定要在确认线程结束(join 成功)后再 free 栈,否则会出现悬空访问。
- 若使用超短生命周期线程,考虑线程池或 workqueue 以降低频繁创建销毁开销。
快速对照
| 方式 | 栈来源 | 生命周期管理 | 典型用途 |
|---|---|---|---|
K_THREAD_DEFINE |
编译期固定 | 系统启动自动 | 常驻任务 |
k_thread_create + 静态栈 |
静态预留 | 手动 | 少量可控线程 |
k_thread_create + 动态栈 |
运行时分配 | 手动 + 需 join/free | 大量/可变并发 |
参考链接:
[1] 深入调度器
[2] 官方文档
了解 Heiweilu的小世界 的更多信息
订阅后即可通过电子邮件收到最新文章。







