part1 从 0 到 1 写
RT-Thread 内核
3 线程 & 就绪列表 & 调度器
1 线程
(1) 线程栈
线程间独立
线程(独立的)栈空间
预先定义的 全局数组 // 放 RAM
|
|/
sizeMax = 512
存
寄存器
r15(PC) 存 线程函数 ptr
r0 存 线程函数 para
(2) 线程函数
要求
无返回值
(3) 线程控制块 TCB (Thread Control Block)
(便于) `调度`
(视为) 线程 handle
存 线程 所有信息
[1] 线程栈指针
[2] 线程函数 ptr
[3] 线程函数 para
struct rt_thread
{
void* sp; // 线程栈指针
void* entry; // 线程函数 ptr
void* parameter; // 线程函数 para
void* stack_addr; // 线程栈 startAddr
rt_unit32_t stack_size; // 线程栈 size
rt_list_t tlist;
// 线程 链表/Link 结点
}; |
| struct rt_list_node
| {
| struct rt_list_node* next;
| struct rt_list_node* prev;
| }
| typedef struct rt_list_node rt_list_t;
|/
钩子
本身 含 prev/next 2 个指向 本身 的 指针
-> 形成 `钩子 双向链表`
|
| 钩子 作 TCB 的 成员
|/
每个钩子上 挂 1 个 TCB
形成 TCB 的 双向链表
`钩子` 在TCB 内存中 `位置固定`
据 钩子 地址 + 钩子 name => TCB 首 地址
|
| 延伸
|/
据 memPtr + memName => mem 所在 structTypePtr
|
| 强转为 char*
|/
structTypaddr = (char*)memPtr - mem 到 struct 首 的 距离
| |
| | 1] 0 强转为 structType 指针类型 2] 取 mem 3] 取地址
| |
| &(structType*)0->memName
| |
| 强转为 structType 指针类型 | = 以 0 地址 为 基准 的 相对地址:
| | 1> 类型: 为 mem 的指针类型
| | 2> 值 : 强转为 unsigned long = mem 到 struct 首 的 距离
|/ |/
structTypePtr (unsigned long)( &(structTypePtr*)0->memName )
|
| 宏
|/
#define rt_container_of(memPtr, structType, memName)
(structType*) ( (char*)memPtr - (unsigned long)( &(structType*)0->memName ) )
|
| 重命名
|/
#define rt_list_entry(hookPtr, TCBType, hookName) \
rt_container_of(hookPtr, TCBType, hookName)
|
|
|/
启动 调度器 中 == 系统 第1次切换时
(手动)指定 dstThread
= 就绪列表(数组 ) 中
|\
| index = 0
|
`优先级最高` 的 ThreadTCBHood 双向链表 中
rt_thread_priority_table[0]
|\
|
|
第 1 个 Hook 的
next
Hook 相应 的 Thread
|
|
|/
1] 双向链表 只1个 node => next 指向 本 Hood 自身
2] 双向链表 >=2个 node => next 指向 nextHood
register struct rt_thread*
to_thraed = rt_list_entry( rt_thread_priority_table[0].next,
struct rt_thread,
tlist)
(4) 线程 初始化 函数
|
|/
创建
将 线程栈 + 线程函数 + TCB 联系起来
re_err_t
rt_thread_init( struct rt_thread* pTCB,
void(*entry)(void* para),
void* para,
// ...
)
{
// [1] 初始化 TCB 钩子 list => 初始化 TCB 列表
rt_list_init(&pTCB->tlist);
// [2] 初始化 TCB
pTCB->entry = entry;
// ...
// [3] 初始化 线程栈: 各 register 放 线程栈
pTCB->sp
= (void*)rt_hw_stack_init(pTCB->entry,
pTCB->parameter,
(void*)( (char*)pTCB->stack_addr + pTCB->stack_size - 4 ) );
} |
| 线程栈: 栈底 在 高地址
|/
栈底 - 4 (Byte)
|
| 初始: 栈顶指针 = 栈底指针
|/
栈顶
2 就绪列表
[1] 是 `数组`
———————————————————————————————————————
1] elem 是 `TCB 钩子`
|
| 挂 `同优先级` 的 TCB (相应的 thread)
|/
双向链表
———————————————————————————————————————
2] index 表示 TCB 相应线程优先级
———————————————————————————————————————
rt_list_t rt_thread_priority_table[RT_THREAD_PRIORITY_MAX];
|
|/
32
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
| |
_ _|/ _ _ _ _ _ _ _ _ _ |
[index=0] | next - -|- - -->| next - -|- -
| | | |
| ...prev | |...prev |
| _ _ _ _ _ | | _ _ _ _ _ |
Hook 双向链表 中 每个 Hook 所挂 TCB 相应的 Thread 同优先级
=> 任选1个
=> 最简单的选法: [i].next
|
|
|/
1] 初始 [0] 必含 Hook => [0.].next
2] 后续 必须 搜索 第1个 含 Hook 的 index (可能 > 0)
[index = 1]
_ _ _ _ _ _ _ _ _
| | | |
| _|/ _ _ |/_ |
| | next - -|- - - -
| | |
- -| - prev |
| _ _ _ _ _ |
[2] 初始化线程
将 `线程` 的 `TCB` 的 `钩子` 插入 就绪列表
rt_thread_init(...);
3 调度器
(1) 功能
[1] 从 就绪列表 搜索 优先级最高的 线程
[2] 切换 到 该线程 去 执行
|
| 源线程 是/否 存在 ? <=> 是 第1次切换吗 (否 / 是) ?
|/
1] 第1切换: 无 srcThread => 手动指定 dstThread
启动调度器
void rt_system_scheduler_start()
{
// 1> 手动指定 dstThread: 就绪列表中 优先级最高的线程
register struct rt_thread*
to_thraed = rt_list_entry( rt_thread_priority_table[0].next,
struct rt_thread,
tlist)
// 2> 切换过去 执行: 底层用 汇编实现
rt_hw_context_switch_to( (rt_unit32_t)&to_thraed->sp );
|
|/
线程栈指针
}
2] 后续切换: 有 srcThread => 系统调度: 调度器 在 就绪列表中 选 优先级最高的线程/dstThreasd
void rt_schedule()
{
// ...
rt_hw_contet_switch((rt_unit32_t)&from_thread->sp,
(rt_unit32_t)&to_thread->sp, );
}
(2) 实现
scheduler.c
全局变量
当前线程 TCB ptr: rt_current_thread
线程切换函数
(3) 相关函数
[1] 调度器初始化
void rt_system_scheduler_init()
{
// 1] 线程就绪列表 初始化
}
[2] 启动调度器
void rt_system_scheduler_start()
[3] 系统调度
void rt_schedule()
4 临界段保护
临界段
一段 执行时 `不能被打断` 的 `代码段`
|
| `什么情况下`, 1个 代码段 可能会被 打断
|/
———————————————————————————————————————
1] 系统调度
|
| 最终也是产生
|/
PendSV 中断
PendSV Handler 中 实现线程切换
———————————————————————————————————————
2] 中断
———————————————————————————————————————
=> 归结为 `中断` 时, 1个 代码段 可能会被 打断
|
|
|/
RT-Thread 对 临界段 的保护
关中断
2个除外
1] NMI FAULT
2] 硬 FAULT
5 对象容器
1 RT-Thread 中 所有
`数据结构` 都称为 `对象`
[1]
线程
信号量
mutex
事件
消息队列
内存堆
内存池
定时器
|
| rtdef.h 中
|/
枚举定义: 为 每个对象 打上1个 `数字标签 `
enum rt_object_class_type _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
{ |
RT_Object_Class_Thread = 0, // 对象 是 线程 |
RT_Object_Class_Semaphore, |
// ... |
RT_Object_Class_Unknown, // 对象未知 |
RT_Object_Class_Static = 0x80; // 对象是 静态对象 |
}; |
|
[2] 对象 Handle / CB(Control Block) |
|
至少有 4 个成员 |
|
—————————————————————————————————————————————————— |
对象 |
|
1] name : char [RT_NAME_MAX] |
2] type : rt_uint8_t |
3] flag (status) : rt_uint8_t |
4] list (Hook) : rt_list_t |
| |
|/ |
1种 (type) `对象` 靠其 `钩子` |
|
被 挂到 1条 `双向链表` |
| |
| |
|/ |
保存 所有这种对象 的 info |
—————————————————————————————————————————————————— |
|
1) 专门定义成 struct rt_object |
|
2) 线程 对象 Handle / TCB |
|
struct rt_thread |
|
`首部` 添加 这 4个成员 |
|
2 对象 容器 |
|
[1] 存放 系统中 所有对象 |
|
[2] RT-Thread (对象)容器 实现 |
|
全局 `数组` |
|
1] size = OS 中 `对象 种类 ( type ) 数` |
|
= RT_Object_Info_Unknown |
| |
| |
|/ |
容器 index 定义 |
|
枚举 |
|
enum rt_thread_info_type |
{ |
RT_Object_Info_Thread = 0, // 对象 是 线程 |
#ifdef RT_USING_SEMAPHORE |
RT_Object_Info_Semaphore, |
#endif |
// ... |
RT_Object_Info_Unknown, // 对象未知 |
}; | |
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _| |
| |
| 2] 元素 |
| |
| 各种 `创建的` 对象 的 `type + headHook + size` 构成 的 struct |
| | | \
| | 除: 线程对象 必有 |/ |
| |/ |
| `条件编译 + 宏` struct rt_thread_information __ _ _ _ _
| { | |
| 控制 是否有 某种 (type) 对象 enum rt_object_class_type type; |
| rt_list_t object_list; |
| rt_size_t object_size; |
| }; |
| |
| static struct rt_thread_information _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
| rt_object_container(RT_Object_Info_Unknown) = { , {}, };
|_ _ _ _ _ _ _ _{ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|
{
RT_Object_Class_Thread,
_OBJ_CONTAINER_LIST_INIT(RT_Object_Class_Thread),
sizeof(struct rt_thread)
},
#ifdef RT_USING_SEMAPHORE
{/* */},
#endif
/* */
};
[3] 作用
便于 OS Kernel `管理` 各 内核对象
|
|/
查状态
3 容器接口
[1] get 指定 type 的 对象 info
strutc rt_thread_information*
rt_object_get_information(enum rt_object_class_type type);
数组中搜索
[2] 对象初始化
1] 对象 Handle/CB 中 相关 mem 初始化
2] 将 对象 `插入` 对象容器 中
void rt_object_init( struct rt_object* p_rt_object
enum rt_object_class_type type,
const char* name);
|
| 被 `谁` 调用
| |
| 线程 初始化函数
|/
rt_err_t
rt_thread_init( struct rt_thread* pTCB,
const char* name,
void(*entry)(void* para),
void* para,
// ...
)
rt_thread_init(..., const char* name, ...)
{
rt_object_init((struct rt_object *)pTCB, RT_Object_Class_Thread, name);
}
6 空闲线程 与 阻塞延时
[1] 延时机制
1] 软件延时
让 `CPU 空等` 来达到延时
|
|
|/
浪费 CPU 资源
|
| RTOS: 充分发挥 CPU 性能
|/
2] `阻塞延时`
线程
1> `需要延时` 时,
`放弃 CPU 使用权` => 进入 `阻塞态`
CPU 可以去做 otherThing
|
| 若 无 otherThread 可以运行
|/
RTOS 创建1个 `空闲线程` 让 CPU 运行
|
|/
(1) 优先级 最低
(2) 主要工作
————————————————————————————————
1) 系统 memory 清理
————————————————————————————————
2) 让 系统进入 `休眠 或 低功耗`
————————————————————————————————
2> 延时时间到 时,
重新获取 CPU 使用权
[2] 阻塞延时 实现
——————————————————————————————————————
1] 取 当前 TCB: 全局变量
——————————————————————————————————————
2] TCB 成员 remaining_tick 成员
置 为 需要延时的 tick / 时间
——————————————————————————————————————
3] 调 系统调度 切换出去
——————————————————————————————————————
void rt_thread_delay(rt_tick_t tick)
{
struct rt_thread* pTCB = rt_current_thread;
pTCB->remaining_tick = tick;
rt_schedule();]
}
7 多优先级
1 RT-Thread 多优先级 实现
靠
[1] 就绪列表 (是 数组) / 线程优先级表
|
|/
index 即 Thread 优先级: index 越小, 优先级越高
rt_thread_priority_table[RT_THREAD_PRIORITY_MAX];
[2] 线程优先级 group
1] 目的
`快速找到` 就绪列表 中 插/删 position
|
| 若不用
|/
要对 就绪列表 搜索
2] 是 32位 整数
每位 对应 1 个 优先级
=> 只能表示 32 个优先级
=> 若想支持 > 32 个优先级,
用 线程优先级 group 的 数组
位 i(0~31): 优先级 i
函数
`搜索` (从低位开始) 32 位 整数 `第1个置1` 的 位号/index
|
|/
优先级最高 的 线程
|
|/
在 就绪列表 中 相应 index 上
|
| `基于位图` 的 `最高优先级 搜索算法`
|
| O(1) => RT-Thread 实时性 体现
|
| 思想
| ————————————————————————————
| 1] 逐 Byte(从低到高) 检测
| ————————————————————————————
| 2] 位运算 + 基于 位图
| ————————————————————————————
|/
RT-Thread
据 `优先级调度` 的 `抢占式` RTOS
2 本章之前 (不支持优先级) 相应 代码 要改为 支持优先级 的
8 定时器
1 目的
`更快地` 实现 线程的 `阻塞延时`
|
|
|/
阻塞延时 `机制`
[1] `判 延时时间到` 的方法
————————————————————————————————————————————————————————
| 每次 `时基中断 (SysTick 中断)` 来临
————————————————————————————————————————————————————————
1] 没用定时器 | 要 `扫描 所有线程`
| |
本章之前 |
————————————————————————————————————————————————————————
2] 定时器 | `只扫描` 定时器列表 `第1个定时器` /_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
———————————————————————————————————————————————————————— |
|
[2] TCB 内置 + 线程需要延时 时 操作 |
|
———————————————————————————————————————————————————————————————————————————————— |
线程需要延时 时 |
———————————————————————————————————————————————————————————————————————————————— |
`TCB 内置` |
———————————————————————————————————————————————————————————————————————————— |
1] 延时变量/ 1] remaining_tick 设为 需要延时的时间 |
剩余时间片 2] 将线程 `挂起` |
remaining_tick | |
|/ |
线程 在 `就绪线程优先级 group` 中 对应 `位 清 0` |
———————————————————————————————————————————————————————————————————————————— |
2] `定时器` 1] 将线程 `挂起` |
|
2] `启动` 内置 `定时器` |
|
3] 定时器 `插入` global 系统 |
|
`定时器 双向列表 rt_timer_list` |
| |
|/ |
`按 延时大小 升序 sort` |
| |
|/ |
第1个 node(timer) 延时最小 => 最早到期 _ _ _ _ _ _ |
————————————————————————————————————————————————————————————————————————————————
9 时间片
>= 2个 线程 优先级相同, 启用 时间片(轮转) 功能
可指定 线程 持续运行1次的 时间, 单位为 tick
调度选出 优先级最高 的 线程
|
| 比如有 2 个
|/
线程 1: tick = 2
线程 2: tick = 3
先执行 线程1,
时间片 消耗完, 再执行 线程 2
1 时间片 实现
TCB 中 增加 `成员`
————————————————————————————————
1] 初始时间片 init_tick
2] 剩余时间片 remaining_tick
————————————————————————————————
part2 RT-Thread 内核 应用开发
14章 线程管理
RT-Thread 中
1 线程
(1) 可以
————————————————————————————
1] 使用 或 等待 CPU
2] 使用 内存空间 等 资源
3] 独立于 otherThread 运行
————————————————————————————
|
| 任一时刻
|/
只有1个线程 运行: 宏观上看, 所有线程 都在运行
调度器 决定
运行、停止 哪个线程
(2) 调度机制
————————————————————————————————————————————————————————
[1] `抢占式` 调度
|
| 3种 `不可抢占`
|/
————————————————————————
1] `中断处理函数`
————————————————————————
2] `调度器 加锁`
————————————————————————
3] `禁止 中断` 的代码
————————————————————————
系统 中
当 有 比 当前线程 `优先级更高` 的 `线程 就绪` 时,
当前线程 将 `立即被换出`
高优先级 线程 (会) 立即 `抢占 CPU` 运行
————————————————————————————————————————————————————————
[2] `时间片` 轮转 (同时支持, 同优先级 有 多个线程时)
————————————————————————————————————————————————————————
(3)
————————————————————————————————————————————————————————————————————
1] 高优先级 线程 | 可 `打断/抢占` 低优先级线程
2] 低优先级 线程 | 必须在 高优先线程 `阻塞 或 结束` 后, 才能得到 调度
————————————————————————————————————————————————————————————————————
2 `实时性` 如何体现
`基于位图` 的 `最高优先级 搜索算法`
时间复杂度 O(1)
与 就绪线程数 无关
3 线程状态 转移图
运行态 - - - - - - -> 关闭态
/| \ |\
| \ 先就绪 |
| \ 再运行 |
| \ |
|/ \/ |
初始态 - - -> 就绪态 <- - -> 挂起态
挂起态 不能直接换到 运行态
4 挂起
1] 主动挂起: 放弃 CPU 使用权
线程调 rt_thread_delay() rt_thread_suspend()
2] 被动挂起
资源不可使用
15 消息队列
类似 << C++11 深入应用 与 工程级应用>> 中
半同步半异步线程池
消息队列
本身 同步
下层的 任务处理 `异步`
|
|/
独立
消息队列
1] 线程 与 线程 间
2] 中断 与 线程 间
传递 消息
结构
——————————————————————————————————————————————————————————————————
sender | 消息队列 Handle/CB 消息队列 | receiver
——————————————————————————————————————————————————————————————————
线程 | 空闲链表 head | 等待线程
| |
中断 | 消息链表 head 指针 |
| |
| 消息链表 尾 指针 |
——————————————————————————————————————————————————————————————————
16 信号量
剩下的 可被占用的 资源数
作用
[1] 互斥
信号量创建后,
可用 信号量 个数 `满`
线程 需使用 `临界资源` 时
`获取 信号量 使其变空`
=> 其他线程 ... 阻塞
问题: 优先级反转
[2] 同步
信号量创建后,
可用 信号量 个数 `空`
1] 线程 1 取信号量 => 阻塞
2] otherThread 在某 `条件(cv)/事件` 发生后,
`释放 信号量`
线程 1
1> 进入 `就绪态`
ra
2> 若 优先最高, 切换到 线程1 => 线程1 与 otherThread 同步
17 互斥量
1 `互斥` 型 `二值`信号量
与 信号量 区别
1] 互斥量 ownship
2] 递归访问
3] 防止优先级反转
2 应用
————————————————————————————————————
1] 互斥量 保护 资源的互锁
2] 二值信号量 同步
————————————————————————————————————
18 事件
线程间 同步 机制
与 信号量不同
可实现 一对一、多对多 同步
网友评论