上 一 篇 主 要 介 绍 了 RT-Thread 线 程 管 理 相 关 的 理 论 知 识 :
RT-Thread 快 速 入 门 -线 程 管 理
这 篇 重 点 介 绍 RT-thread 提 供 的 线 程 管 理 相 关 的 接 口 函 数 , 以 及
实 战 演 示 。
线 程 创 建
在 RT-Thread 中 , 创 建 一 个 线 程 的 方 式 有 两 种 :
动 态 创 建 方 式 ,线 程 的 栈 和 线 程 控 制 块 由 系 统 从 动 态 内 存 堆 上 分 配 。
静 态 创 建 方 式 , 线 程 的 栈 和 线 程 控 制 块 由 用 户 定 义 分 配 。
1. 动 态 创 建 线 程
动 态 创 建 线 程 ,用 户 不 需 要 考 虑 线 程 栈 和 线 程 控 制 块 空 间 分 配 的 问
题 ,全 部 由 系 统 自 动 完 成 分 配 。用 户 只 需 要 关 心 其 他 关 键 的 线 程 属
性 即 可 。
RT-Thread 动 态 创 建 一 个 线 程 的 接 口 函 数 为 rt_thread_c reate(), 其
函 数 原 型 为 :
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)
该 函 数 的 详 细 参 数 在 上 一 篇 文 章 中 做 过 详 细 的 解 释 ,在 此 不 再 赘 述 。
其 中 关 键 的 几 个 参 数 分 别 是 :
线 程 入 口 函 数 指 针 entr y,需 要 用 户 定 义 一 个 函 数 ,创 建 线 程 的 时 候 ,
将 函 数 名 放 在 这 个 参 数 位 置 。
线 程 栈 大 小 stack_s i ze, 单 位 是 字 节 。 根 据 实 际 情 况 设 置 这 个 参 数 ,
后 边 会 分 析 如 何 确 定 这 个 值 。
线 程 优 先 级 priority,根 据 线 程 需 要 完 成 任 务 的 重 要 性 来 决 定 优 先 级
值 , 值 越 小 , 优 先 级 越 高 。
时 间 片 tick, 单 位 为 系 统 时 钟 节 拍 , 如 果 有 相 同 优 先 级 的 线 程 ,
才 会 用 到 此 参 数 。
动 态 创 建 线 程 举 例 :
/* 线程入口函数 */
void thread_entry(void *parameter)
{
...
}
/* 定义线程控制块指针 */
rt_thread_t tid = RT_NULL;
/* 创建线程 */
tid = rt_thread_create("thread_test", thread_entry,
RT_NULL, 512, 10, 5);
首 先 定 义 一 个 线 程 控 制 块 指 针 ( 线 程 句 柄 ) , 然 后 调
用 rt_thread_c reate() 函 数 创 建 线 程 。
此 线 程 的 名 字 为 “thread_test”;线 程 入 口 函 数 thread_entr y;入 口 函 数
的 参 数 为 RT_NULL, 无 入 口 参 数 ; 线 程 栈 的 大 小 为 512 字 节 ; 线
程 优 先 级 为 10; 线 程 时 间 片 为 5。
2. 静 态 创 建 线 程
静 态 方 式 创 建 线 程 ,需 要 用 户 考 虑 的 东 西 多 一 点 :线 程 控 制 块 定 义 、
线 程 栈 空 间 申 请 、 线 程 栈 起 始 地 址 等 。
静 态 创 建 线 程 分 两 步 :
用 户 定 义 线 程 控 制 块 结 构 变 量 , 申 请 线 程 栈 内 存 空 间 。
初 始 化 线 程 控 制 块 , 即 初 始 化 线 程 。
线 程 控 制 块( 线 程 句 柄 )定 义 可 以 通 过 如 下 方 式 完 成 ,即 定 义 struc t
rt_thread 结 构 变 量 :
struct rt_thread thread_static;
线 程 栈 可 以 通 过 定 义 数 组 方 式 的 来 分 配 ,或 者 通 过 动 态 内 存 分 配 的
方 式 来 完 成 :
/* 数组方式确定线程栈,应该定义成全局数组 */
char thread_stack[1024];
/* 动态内存申请方式,确定线程栈 */
char *thread_stack = (char *)rt_malloc(1024);
其 中 rt_mal loc () 函 数 会 在 后 面 内 存 管 理 文 章 做 详 细 讲 解 。
线 程 控 制 块 和 线 程 栈 定 义 完 成 后 ,需 要 对 其 进 行 初 始 化 。RT-Thread
提 供 了 线 程 初 始 化 函 数 接 口 rt_thread_init(), 其 函 数 原 型 定 义 为 :
rt_err_t rt_thread_init(struct rt_thread *thread,
const char *name,
void (*entry)(void *parameter),
void *parameter,
void *stack_start,
rt_uint32_t stack_size,
rt_uint8_t priority,
rt_uint32_t tick)
该 函 数 的 各 个 参 数 解 释 如 下 :
参 数 描 述
thread 线 程 句 柄 , 由 用 户 提 供 , 指 向 线 程 控 制 块 内 存 地 址
参 数 描 述
name 线 程 名 称
entry 线 程 入 口 函 数
parameter 线 程 入 口 函 数 的 参 数
stack_start 线 程 栈 起 始 地 址
stack_size 线 程 栈 大 小 , 单 位 是 字 节 。
priority 线 程 的 优 先 级 。
tick 线 程 的 时 间 片 大 小 。 函 数 执 行 成 功 , 返 回 RT_EOK; 执 行 失 败 , 则 返 回 -RT_EOK。
要 注 意 ,用 户 提 供 的 栈 首 地 址 需 要 做 系 统 对 齐 ,例 如 ARM 架 构 的
CPU 上 需 要 做 4 字 节 对 齐 。
静 态 创 建 线 程 举 例 :
/* 线程栈起始地址做内存对齐 */
ALIGN(RT_ALIGN_SIZE)
char thread_stack[1024];
/* 定义线程控制块 */
struct rt_thread thread;
/* 线程入口函数 */
void thread_entry(void *parameter)
{
...
}
/* 初始化线程控制块 */
rt_thread_init(&thread, "thread_test", thread_entry,
RT_NULL, &thread_stack[0], sizeof(thread_stack),
10, 5);
首 先 定 义 线 程 栈 以 及 线 程 控 制 块 , 然 后 对 线 程 控 制 块 初 始 化 。
线 程 句 柄 为 线 程 控 制 块 thread 的 地 址 &thread ; 线 程 名 称
为 "thread_tes t"; 线 程 入 口 函 数 为 thread_entr y; 入 口 函 数 的 参 数 为
RT_NULL;线 程 栈 起 始 地 址 为 定 义 的 数 组 的 起 始 地 址 ;线 程 栈 大 小
为 数 组 的 字 节 数 ; 优 先 级 为 10; 时 间 片 为 5。
线 程 关 键 参 数 确 定
创 建 一 个 线 程 有 几 个 关 键 的 参 数 需 要 用 户 确 定 :
线 程 栈 大 小
线 程 优 先 级
线 程 时 间 片
对 于 初 学 者 来 说 ,这 几 个 参 数 的 确 定 不 好 把 握 ,或 者 说 ,不 知 道 设
置 多 大 合 适 。
其 实 这 些 参 数 的 确 定 ,没 有 统 一 的 标 准 ,需 要 根 据 实 际 的 应 用 ,具
体 分 析 来 做 决 定 。
1. 线 程 栈 大 小 的 确 定
在 基 于 RTOS 的 程 序 设 计 中 , 每 个 线 程 ( 任 务 ) 都 需 要 自 己 的 栈
空 间 ,每 个 线 程 需 要 的 栈 ,根 据 应 用 的 不 同 ,栈 大 小 也 会 随 之 不 同 。
需 要 用 到 栈 空 间 的 内 容 如 下 :
函 数 调 用 需 要 用 到 栈 空 间 的 项 目 为 :函 数 局 部 变 量 、函 数 形 参 、函
数 返 回 地 址 、 函 数 内 部 状 态 。
线 程 切 换 的 上 下 文 。 线 程 切 换 需 要 用 到 的 寄 存 器 需 要 入 栈 。
任 务 执 行 过 程 , 发 生 中 断 。 寄 存 器 需 要 入 栈 。
实 际 应 用 中 将 这 些 加 起 来 ,可 以 粗 略 得 到 栈 的 最 小 需 求 ,但 是 计 算
很 麻 烦 。在 实 际 分 配 栈 大 小 的 时 候 ,可 以 粗 略 计 算 一 个 值 后 ,取 其
二 倍 , 比 较 保 险 。
2. 线 程 优 先 级 分 配
在 RT-Thread 中 ,线 程 优 先 级 数 值 越 小 ,其 优 先 级 越 高 。空 闲 任
务 的 优 先 级 最 低 。
线 程 优 先 级 的 分 配 ,没 有 具 体 的 标 准 。一 般 是 根 据 具 体 的 应 用 情 况
来 配 置 。
为 了 能 够 使 得 某 项 事 件 得 到 及 时 处 理 ,可 以 将 处 理 此 事 件 的 线 程 设
置 为 较 高 优 先 级 。比 如 ,按 键 检 测 、触 摸 检 测 、串 口 数 据 处 理 等 等 。
而 对 于 那 些 实 时 处 理 不 是 很 高 的 线 程 ,则 可 以 配 置 较 低 优 先 级 。比
如 , LED 闪 烁 、 界 面 显 示 等 等 。
3. 线 程 时 间 片 分 配
具 有 相 同 优 先 级 的 线 程 调 度 ,线 程 时 间 片 分 配 的 长 ,则 该 线 程 执 行
时 间 长 。
可 以 根 据 实 际 应 用 情 况 ,如 果 某 个 线 程 完 成 某 项 事 务 ,耗 时 比 较 长 ,
可 以 给 其 分 配 较 大 的 时 间 片 。耗 时 较 短 的 线 程 ,分 配 较 小 的 时 间 片 。
如 果 应 用 程 序 中 , 没 有 相 同 优 先 级 的 线 程 , 则 此 参 数 不 起 作 用 。
线 程 睡 眠
在 RTOS 中 , 如 果 需 要 延 时 等 待 一 会 儿 , 千 万 不 能 用 普 通 的 延 时
等 待 ( CPU 空 转 ) , 应 该 调 用 RTOS 提 供 的 延 时 等 待 函 数 。 如
果 用 普 通 的 延 时 ,那 么 RTOS 失 去 了 实 时 性 ,浪 费 了 CPU 资 源 。
RT-Thread 提 供 了 系 统 函 数 , 用 于 让 当 前 线 程 延 迟 一 段 时 间 , 在
指 定 的 时 间 结 束 后 ,重 新 运 行 线 程 。线 程 睡 眠 可 以 使 用 以 下 三 个 函
数 :
rt_err_t rt_thread_sleep(rt_tick_t tick); /* 睡眠时间,单位为 时钟节拍 */
rt_err_t rt_thread_delay(rt_tick_t tick); /* 延时,单位为 时钟节拍 */
rt_err_t rt_thread_mdelay(rt_int32_t ms); /* 单位为 毫秒 */
这 三 个 函 数 的 作 用 相 同 ,调 用 它 们 可 以 使 得 当 前 线 程 进 入 挂 起 状 态 ,
并 持 续 一 段 指 定 的 时 间 。这 个 时 间 到 达 后 ,线 程 会 被 唤 醒 并 再 次 进
入 就 绪 状 态 。
rt_thread_s leep/delay () 的 参 数 tic k,单 位 为 1 个 系 统 时 钟 节 拍( OS
tick) 。
rt_thread_mdelay () 的 参 数 ms, 单 位 为 1ms。
函 数 的 返 回 值 为 RT_EOK。
使 得 线 程 进 入 休 眠 , 即 调 用 这 三 个 函 数 中 的 一 个 , 也 是 让 出 CPU
权 限 的 一 种 方 式 , 可 以 让 低 优 先 级 的 线 程 能 够 得 到 执 行 。
如 果 高 优 先 级 的 线 程 没 有 让 出 CPU 的 操 作 ,那 么 低 优 先 级 的 线 程
永 远 得 不 到 CPU 执 行 权 限 , 从 而 引 发 问 题 出 现 。
因 此 ,高 优 先 级 线 程 ,要 么 等 待 某 项 系 统 资 源 不 可 用 而 进 入 挂 起 状
态 ,要 么 调 用 这 三 个 睡 眠 函 数 进 入 挂 起 状 态 ,从 而 给 低 优 先 级 线 程
执 行 的 机 会 。
线 程 创 建 示 例
此 处 用 于 演 示 如 何 使 用 上 面 介 绍 的 线 程 创 建 函 数 :
#include
#define THREAD_PRIORITY 25
#define THREAD_STACK_SIZE 512
#define THREAD_TIMESLICE 5
static rt_thread_t tid1 = RT_NULL;
/* 线程 1 的入口函数 */
static void thread1_entry(void *parameter)
{
rt_uint32_t count = 0;
while (1)
{
/* 线程 1 采用低优先级运行,一直打印计数值 */
rt_kprintf("thread1 count: %d\n", count ++);
/* 延时 500ms */
rt_thread_mdelay(500);
}
}
ALIGN(RT_ALIGN_SIZE)
static char thread2_stack[1024];
static struct rt_thread thread2;
/* 线程 2 入口 */
static void thread2_entry(void *param)
{
rt_uint32_t count = 0;
/* 线程 2 拥有较高的优先级,以抢占线程 1 而获得执行 */
for (count = 0; count < 10 ; count++)
{
/* 线程 2 打印计数值 */
rt_kprintf("thread2 count: %d\n", count);
}
rt_kprintf("thread2 exit\n");
/* 线程 2 运行结束后也将自动被系统脱离 */
}
int main()
{
/* 创建线程 1,名称是 thread1,入口是 thread1_entry */
tid1 = rt_thread_create("thread1",
thread1_entry, RT_NULL,
THREAD_STACK_SIZE,
THREAD_PRIORITY, THREAD_TIMESLICE);
/* 如果获得线程控制块,启动这个线程 */
if (tid1 != RT_NULL)
{
rt_thread_startup(tid1);
}
/* 初始化线程 2,名称是 thread2,入口是 thread2_entry */
rt_thread_init(&thread2,
"thread2",
thread2_entry,
RT_NULL,
&thread2_stack[0],
sizeof(thread2_stack),
THREAD_PRIORITY - 1, THREAD_TIMESLICE);
rt_thread_startup(&thread2);
}
这 个 例 子 用 两 种 方 式 创 建 线 程 :静 态 方 式 和 动 态 方 式 。一 个 线 程 在
运 行 完 毕 后 自 动 被 系 统 删 除 , 另 一 个 线 程 一 直 打 印 计 数 。
编 译 运 行 , 结 果 如 下 所 示
部分文件列表
文件名 |
大小 |
RT-Thread快速入门-线程管理(下).pdf |
263K |
全部评论(0)