【Zephyr】Zephyr 操作系统基本原理(线程)
【Zephyr】Zephyr 操作系统基本原理(线程)
本文最后更新于56 天前,其中的信息可能已经过时,如有错误请发送邮件到273925452@qq.com

核心速览

线程

进程:系统资源分配的最小单位,拥有独立地址空间
线程:进程内独立执行的最小单位,共享进程资源

线程是一个内核对象,用于处理因太长或太复杂而无法由 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_takek_msgq_getk_pollk_sleepk_mutex_lock(阻塞)
I/O or event completion Waiting → Ready 等待条件满足或超时到期 k_sem_givek_msgq_putk_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 硬件中断/异常处理 避免为每线程预留过大的中断处理空间;统一管理嵌套中断栈深度

为什么需要区分(开启用户态时)

  1. 安全隔离:用户代码不应随意读写内核的调用帧与局部变量。通过切换到独立的特权栈,用户态即便栈溢出也难以破坏内核。
  2. 减少攻击面:系统调用入口只保存/恢复有限上下文到内核栈;用户栈指针在内核态被隔离不用。
  3. 更容易做 MPU / MMU 保护:用户栈可设为用户权限读写;内核栈仅特权访问。
  4. 内核栈尺寸可更小:内核路径较浅(系统调用包装+少量调度/对象操作),不需要和用户函数一样的深度。

配置选项(示例)

配置 作用
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)的小代码块,在中断退出后、任何普通线程恢复运行之前立即得到调度。

调度层级关系(从高到低)

  1. 硬件 ISR(可嵌套)
  2. ISR 退出后就绪的 Meta-IRQ 线程
  3. 最高优先级的 cooperative 线程
  4. 其他 cooperative 线程
  5. 各级 preemptive 线程(普通抢占式)
  6. 空闲线程

核心特性

特性 说明
优先级区间独立 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的小世界 的更多信息

订阅后即可通过电子邮件收到最新文章。

💡本内容采用 CC BY-NC-SA 4.0 协议,非商业转载需注明作者和出处,商业用途请联系作者授权,衍生作品需采用相同协议。
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇

了解 Heiweilu的小世界 的更多信息

立即订阅以继续阅读并访问完整档案。

继续阅读

🎵 背景音乐
点击播放
00:00 00:00