在 RT-Thread 中 , 最 基 本 的 调 度 单 位 是 线 程 , 其 他 RTOS 也 叫
任 务 。如 果 学 习 过 或 者 了 解 过 RTOS,任 务 这 种 叫 法 是 最 为 熟 知 的 。
本 篇 文 章 来 学 习 一 下 RT-Thread 线 程 方 面 的 内 容 。对 于 初 学 者 来
说 , 转 换 一 下 思 维 , 建 立 多 任 务 ( 线 程 ) 的 编 程 思 想 。
引 言
对 于 裸 机 编 程 ,整 个 软 件 系 统 只 有 一 个 线 程( 任 务 )在 执 行 ,实 现
方 式 是 通 过 一 个 大 循 环 完 成 的 。应 用 程 序 是 一 个 无 限 循 环 ,循 环 中
调 用 各 个 功 能 模 块 的 函 数 , 完 成 相 应 的 操 作 。
RTOS 是 一 个 多 任 务 系 统 ,可 以 把 总 体 功 能 划 分 为 多 个 小 模 块 ,每
个 小 模 块 独 立 运 行 完 成 某 项 功 能 ,即 将 任 务 分 解 。这 样 使 得 复 杂 项
目 的 实 现 变 得 简 单 了 。
关 于 为 什 么 要 用 RTOS, 或 者 说 RTOS 的 优 点 , 网 上 资 料 很 多 ,
在 此 不 再 赘 述 。
线 程 基 础
1. 线 程 调 度
对 于 一 款 RTOS 来 说 , 最 核 心 的 部 分 就 是 线 程 ( 任 务 ) 调 度 器 。
调 度 器 的 作 用 是 根 据 调 度 算 法 来 决 定 当 前 需 要 执 行 的 线 程 。
RT-Thread 的 线 程 调 度 器 是 抢 占 式 的 , 基 于 优 先 级 对 线 程 进 行 调
度 。每 个 线 程 均 具 有 一 个 优 先 级 ,调 度 器 的 主 要 工 作 是 ,从 就 绪 线
程 列 表 中 查 找 最 高 优 先 级 线 程 , 然 后 将 CPU 的 使 用 权 分 配 给 它 。
" 可 抢 占 " 意 味 着 , 如 果 高 优 先 级 线 程 就 绪 或 者 满 足 运 行 条 件 ,
RT-Thread 马 上 将 CPU 的 控 制 权 交 给 高 优 先 级 线 程 。
2. 线 程 优 先 级
RT-Thread 线 程 的 优 先 级 表 示 线 程 被 调 度 的 优 先 程 度 。 每 个 线 程
都 具 有 优 先 级 ,对 于 重 要 的 线 程 ,应 该 赋 予 其 高 优 先 级 ,这 样 才 能
保 证 线 程 被 优 先 调 度 。
RT-Thread 最 大 支 持 256 个 优 先 级 ( 0~255) , 数 值 越 小 的 线 程
优 先 级 越 高 。 0 为 最 高 优 先 级 。 最 低 优 先 级 默 认 分 配 给 空 闲 线 程 ,
用 户 一 般 不 用 。
可 以 根 据 实 际 情 况 配 置 优 先 级 个 数 , 对 于 ARM Cortex-M 系 列 ,
普 遍 采 用 32 个 优 先 级 ( 0~31) 。
3. 时 间 片
RT-Thread 允 许 多 个 线 程 具 有 相 同 的 优 先 级 , 相 同 优 先 级 的 线 程
之 间 采 用 时 间 片 轮 转 的 方 式 进 行 调 度 。创 建 线 程 的 时 候 ,可 以 配 置
线 程 的 时 间 片 参 数 。 时 间 片 仅 对 优 先 级 相 同 的 就 绪 线 程 有 效 。
时 间 片 的 作 用 是 约 束 线 程 单 次 运 行 的 时 长 ,其 单 位 是 系 统 时 钟 节 拍
( OS Tick) 。
4. 线 程 栈
RT-Thread 线 程 具 有 独 立 的 栈 , 当 进 行 线 程 切 换 时 , 会 将 当 前 线
程 的 上 下 文 存 在 栈 中 ,当 线 程 要 恢 复 运 行 时 ,再 从 栈 中 读 取 上 下 文
信 息 , 进 行 恢 复 。
线 程 栈 还 用 来 存 放 函 数 中 的 局 部 变 量 。当 一 个 函 数 调 用 另 外 一 个 函
数 时 , 函 数 中 的 局 部 变 量 会 暂 时 存 放 在 栈 中 。
线 程 栈 的 增 长 方 向 由 芯 片 架 构 决 定 的 :由 高 地 址 向 低 地 址 增 长 、由
低 地 址 向 高 地 址 增 长 。这 两 种 增 长 方 式 RT-Thread 均 支 持 。对 于
ARM Cortex-M 架 构 , 线 程 栈 构 造 如 下 图 所 示 :
5. 线 程 的 入 口 函 数
入 口 函 数 是 线 程 实 现 预 期 功 能 的 函 数 。线 程 的 入 口 函 数 由 用 户 设 计 ,
一 般 有 以 下 两 种 形 式 :
无 线 循 环 模 式
这 种 线 程 会 一 直 被 系 统 循 环 调 度 , 执 行 任 务 , 不 会 删 除 。
void thread_entry(void *parameter)
{
while(1)
{
/* 线程处理 */
}
}
顺 序 执 行 或 有 限 次 循 环 模 式
这 类 线 程 不 会 循 环 或 者 不 会 永 久 循 环 ,执 行 完 毕 之 后 ,线 程 将 被 系
统 自 动 删 除 。
void thread_entry(void *parameter)
{
/* 处理任务 */
...
}
线 程 状 态
对 于 单 CPU 来 说 ,系 统 运 行 过 程 中 ,同 一 时 刻 只 有 一 个 线 程 在 处
理 器 运 行 。 在 运 行 过 程 中 , 线 程 有 多 种 不 同 的 运 行 状 态 :
初 始 状 态 ,线 程 刚 创 建 还 未 开 始 运 行 时 处 于 的 状 态 ,此 状 态 下 ,线
程 不 参 与 调 度 。
就 绪 状 态 , 线 程 具 备 运 行 条 件 的 状 态 , 等 待 被 调 度 器 调 度 执 行 。
运 行 状 态 , 线 程 正 在 运 行 。
挂 起 状 态 ,也 称 为 阻 塞 状 态 。由 于 资 源 不 可 用 或 线 程 主 动 延 时 一 段
时 间 , 而 进 入 的 状 态 。 线 程 不 能 执 行 。
关 闭 状 态 。 线 程 运 行 结 束 处 于 的 状 态 。 此 时 线 程 不 参 与 调 度 。
下 图 是 各 个 状 态 之 间 的 转 换 图 ,通 过 此 图 可 以 对 RT-Thread 线 程
的 运 行 状 态 有 一 个 整 体 的 认 识 。
图 中 涉 及 到 的 系 统 调 用 函 数 ,在 后 面 的 学 习 会 进 行 详 细 讲 解 。此 处
进 行 简 单 的 说 明 :
rt_thread_create/init() 创 建 或 初 始 化 一 个 线 程 , 此 线 程 处 于 初 始
状 态 。
rt_thread_startup() 函 数 使 得 初 始 化 状 态 的 线 程 进 入 到 就 绪 状 态 。
rt_thread_delay(),rt_sem_take(), rt_mutex_take() 等 函 数 使 得
运 行 状 态 的 线 程 进 入 到 挂 起 状 态 。
rt_thread_resume(), rt_sem_release() 等 函 数 使 得 挂 起 状 态 的 线
程 返 回 到 就 绪 状 态 。
rt_thread_delete/detach() 函 数 将 挂 起 状 态 的 线 程 更 改 为 关 闭 状
态 。
rt_thread_exit(),处 于 运 行 状 态 的 线 程 ,运 行 结 束 ,在 线 程 的 最 后
部 分 调 用 此 函 数 , 将 状 态 更 改 为 关 闭 状 态 。
线 程 控 制 块
在 RT-Thread 中 , 线 程 控 制 块 由 结 构 体 struct rt_thread 表 示 。
线 程 控 制 块 是 操 作 系 统 用 于 管 理 线 程 的 一 个 数 据 结 构 ,它 会 存 放 线
程 的 一 些 信 息 , 例 如 优 先 级 、 线 程 名 称 、 线 程 状 态 等 。
线 程 控 制 块 也 包 含 线 程 与 线 程 之 间 连 接 用 的 链 表 结 构 ,线 程 等 待 事
件 集 合 等 。
详 细 定 义 如 下 , 列 出 了 关 键 结 构 成 员 , 并 加 入 了 注 释 , 了 解 即 可 :
struct rt_thread
{
/* rt 对象 */
char name[RT_NAME_MAX]; /* 线程名称 */
rt_uint8_t type; /* 对象类型 */
rt_uint8_t flags; /* 标志位 */
rt_list_t list; /* 对象列表 */
rt_list_t tlist; /* 线程列表 */
/* 栈指针与入口指针 */
void *sp; /* 栈指针 */
void *entry; /* 线程入口函数指针 */
void *parameter; /* 参数 */
void *stack_addr; /* 栈地址指针 */
rt_uint32_t stack_size; /* 栈大小 */
/* 错误代码 */
rt_err_t error; /* 线程错误代码 */
rt_uint8_t stat; /* 线程状态 */
....
/* 优先级 */
rt_uint8_t current_priority; /* 当前优先级 */
rt_uint8_t init_priority; /* 初始优先级 */
rt_uint32_t number_mask;
......
rt_ubase_t init_tick; /* 线程初始化计数值 */
rt_ubase_t remaining_tick; /* 线程剩余计数值 */
struct rt_timer thread_timer; /* 内置线程定时器 */
void (*cleanup)(struct rt_thread *tid); /* 线程退出清楚函数指针 */
...
rt_uint32_t user_data; /* 用户数据 */
};
typedef struct rt_thread *rt_thread_t;
创 建 线 程
RT-Thread 提 供 了 先 管 理 相 关 的 系 统 函 数 : 包 含 : 创 建 / 初 始 化
线 程 、 启 动 线 程 、 运 行 线 程 、 删 除 / 脱 离 线 程 等 。
此 处 只 讲 解 如 何 创 建 一 个 线 程 。
在 RT-Thread 中 ,要 创 建 一 个 线 程 ,并 使 得 它 能 够 被 执 行 ,需 要
两 步 :
创 建 线 程 , 此 时 一 个 新 线 程 被 创 建 , 并 处 于 初 始 状 态 。
启 动 线 程 ,此 时 线 程 由 初 始 状 态 进 入 就 绪 状 态 ,可 以 被 调 度 器 执 行 。
1. 创 建 线 程
创 建 一 个 线 程 的 函 数 rt_thread_create() 定 义 如 下 :
rt_thread_t rt_thread_create(const char *name,
void (*entry)(void *parameter),
void *parameter,
rt_uint32_t stack_size,
rt_uint8_t priority,
rt_uint32_t tick)
调 用 这 个 函 数 时 ,系 统 会 从 动 态 内 存 堆 中 分 配 一 个 线 程 句 柄( 线 程
控 制 块 ),并 按 照 参 数 中 指 定 的 栈 大 小 从 动 态 内 存 堆 中 分 配 相 应 的
空 间 。
函 数 的 各 个 参 数 解 释 如 下 :
部分文件列表
文件名 |
大小 |
RT-Thread快速入门-线程管理(上).pdf |
223K |
全部评论(0)