Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
源码分析
RT Thread 源码分析笔记 :线程和调度器
发布于 2025-01-19 12:20:35 浏览:168
订阅该版
[tocm] rt thread的 commit:9afe6a51826fb84de229de220dab2077aa5734b2。 后面的代码均来源于此。 # 理想场景 看源代码,我习惯从繁抽简。并选择一个简单的场景,看项目在这场景下的执行流会是怎么样的。 以文档的[线程时间片轮转调度示例](https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/programming-manual/thread/thread?id=%E7%BA%BF%E7%A8%8B%E5%BA%94%E7%94%A8%E7%A4%BA%E4%BE%8B)应用为例。 ```Objective-C++ #include
#define THREAD_STACK_SIZE 1024 #define THREAD_PRIORITY 20 #define THREAD_TIMESLICE 10 /* 线程入口 */ static void thread_entry(void* parameter) { rt_uint32_t value; rt_uint32_t count = 0; value = (rt_uint32_t)parameter; while (1) { if(0 == (count % 5)) { rt_kprintf("thread %d is running ,thread %d count = %d\n", value , value , count); if(count> 200) return; } count++; } } int timeslice_sample(void) { rt_thread_t tid = RT_NULL; /* 创建线程 1 */ tid = rt_thread_create("thread1", thread_entry, (void*)1, THREAD_STACK_SIZE, THREAD_PRIORITY, THREAD_TIMESLICE); if (tid != RT_NULL) rt_thread_startup(tid); /* 创建线程 2 */ tid = rt_thread_create("thread2", thread_entry, (void*)2, THREAD_STACK_SIZE, THREAD_PRIORITY, THREAD_TIMESLICE-5); if (tid != RT_NULL) rt_thread_startup(tid); return 0; } /* 导出到 msh 命令列表中 */ MSH_CMD_EXPORT(timeslice_sample, timeslice sample); ``` 以该应用为例,最小可用的,面向用户的线程接口集是 `rt_thread_init/rt_thread_create , rt_thread_yield, rt_thread_detach/rt_thread_delete/_thread_exit`。 接下来就会重点关注这五个函数背后的实现,实现中涉及的数据结构(线程和调度器)。 假设硬件架构是单核。 先不考虑锁和中断,而只看调度的实现。 优先级个数为32。 # 数据结构 ## 线程 `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)` 从创造的接口可以看出,线程控制块的必备属性是栈的内存空间,名字,入口(任务),以及入口参数, 时间片,优先级。 时间片和优先级被封装在`sched_thread_ctx`对象内。`rt_timer`自然是需要的,作为线程的当前时间。 所以thread有两个对象成员,`sched_thread_ctx`和`rt_timer`,依赖`scheduler`和`timer`这两个模块。 剩余的成员就直属于线程控制块。 ## 调度器 调度器相关的数据结构在`scheduler_up.c`文件。 `rt_list_t rt_thread_priority_table[RT_THREAD_PRIORITY_MAX];` `rt_uint32_t rt_thread_ready_priority_group;` 多个优先级队列,一个优先级代表一个队列。 `rt_thread_ready_priority_group`是多个队列的位图。每个位代表一个队列是否非空。可通过此位图来快速求出等待调度的线程的最高优先级,而不是遍历每个队列判断是否非空。 ```Objective-C++ struct rt_sched_thread_ctx { rt_list_t thread_list_node; /**< node in thread list */ //?? 这个是做什么的? rt_uint8_t stat; /**< thread status */ rt_uint8_t sched_flag_locked:1; /**< calling thread have the scheduler locked */ rt_uint8_t sched_flag_ttmr_set:1; /**< thread timer is start */ #ifdef ARCH_USING_HW_THREAD_SELF rt_uint8_t critical_switch_flag:1; /**< critical switch pending */ #endif /* ARCH_USING_HW_THREAD_SELF */ #ifdef RT_USING_SMP rt_uint8_t bind_cpu; /**< thread is bind to cpu */ rt_uint8_t oncpu; /**< process on cpu */ rt_base_t critical_lock_nest; /**< critical lock count */ #endif struct rt_sched_thread_priv sched_thread_priv; /**< private context of scheduler */ }; ``` 和调度相关的信息被封装成调度上下文。 调度上下文主要有三部分: 用来串到队列里的节点。(区分thread的节点)。 调度时会涉及的信息(如优先级,时间片,剩余时间)。 线程的状态。 stat的0~2位表示线程状态。在这次场景中涉及的状态有: ```Objective-C++ #define RT_THREAD_INIT 0x00 /**< Initialized status */ #define RT_THREAD_CLOSE 0x01 /**< Closed status */ #define RT_THREAD_READY 0x02 /**< Ready status */ #define RT_THREAD_RUNNING 0x03 /**< Running status */ #define RT_THREAD_STAT_YIELD 0x08 /**< indicate whether remaining_tick has been reloaded since last schedule */ ``` # 调用链 ## 创建 当创建一个线程时,也会调用scheduler和timer模块的创建方法。  最后线程状态处于init状态。 线程模块会把线程控制块加入对应的内核容器。 调度器模块则不会加入任何队列。 初始化栈时会配置一个caller栈,父函数是实现回收thread的`_thread_exit`。 ## 启动 这里完全就是调度器模块的主场。 调度器模块会先把线程的调度上下文修改为挂起状态,(不加入任何挂起队列!) 再把线程的调度上下文修改为就绪状态。 最后在rt_schedule进行调度。  ## 调度 当线程的时间片用完,中断handler会调用`rt_thread_yield`。 和启动一节类似。 先让调度上下文的状态附带一个`RT_THREAD_STAT_YIELD`。在`rt_schedule`中作为调度的一个依据。 最后开始调度。  ## 调度策略 根据`rt_schedule`,`rt_sched_insert_thread`可知,调度策略是无论在任何情况,先选择优先级最高的线程,在同样优先级的多个线程中,按先来先服务进行调度。 当线程因其他原因而被中断,回到就绪队列时,会放到就绪队列的头部,即下次调度优先考虑此线程。 ## 回收 `delete`和`detach`的函数实现都一样的。 调度器模块只是让调度上下文脱离当前队列,修改调度上下文的状态为close。 线程模块再让其线程加入defunct队列,再等空闲线程做回收/释放工作。这样做可以确保线程任务的实时性。  当线程执行完毕后会跳到`_thread_exit`函数。其实现就是*thread_*detach,只不过多了一步调度。  # 涉及硬件的功能 `rt_hw_stack_init`,`rt_hw_context_switch` 初始化线程时需要初始化栈,涉及到栈的布局,而调度需要进行上下文切换,涉及到CPU和寄存器。这两者是依赖芯片架构的功能。具体实现在`libcpu`目录 # 小疑惑 这里的注释考虑的不全面。因为目标线程状态可能是close,ready,init,suspend。这就意味着调度上下文可能在多个队列里,也有可能不在队列里。  在`rt_scheduler`,不太懂为什么要声明个flag,直接判断from和to线程是否相等就行了呀。  # todo: - [ ] 哪些临界区需要考虑锁和中断的影响? - [ ] 第一个线程的创建过程 - [ ] 多核调度和单核调度的区别? - [ ] 了解从接收到定时器中断到开始调度的过程 # 收尾 可以看到rt thread是相当模块化的。相关的状态改动由对应的模块进行改动。哪怕是调度和线程这两个紧密相关的部分,也进行了隔离。
0
条评论
默认排序
按发布时间排序
登录
注册新账号
关于作者
catcatbing
这家伙很懒,什么也没写!
文章
8
回答
0
被采纳
0
关注TA
发私信
相关文章
1
kawaii mqtt 严谨使用注意事项,求助
2
关于阅读 device.c 的几点疑惑
3
rt thread studio软件中怎么设置添加pc lint工具
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
FinSH
ART-Pi
DMA
USB
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
rt-smart
FAL
I2C_IIC
UART
ESP8266
cubemx
WIZnet_W5500
ota在线升级
PWM
BSP
flash
freemodbus
packages_软件包
潘多拉开发板_Pandora
定时器
ADC
GD32
flashDB
socket
编译报错
中断
Debug
rt_mq_消息队列_msg_queue
keil_MDK
ulog
SFUD
msh
C++_cpp
MicroPython
本月问答贡献
RTT_逍遥
10
个答案
3
次被采纳
xiaorui
3
个答案
2
次被采纳
winfeng
2
个答案
2
次被采纳
三世执戟
8
个答案
1
次被采纳
KunYi
8
个答案
1
次被采纳
本月文章贡献
catcatbing
3
篇文章
5
次点赞
lizimu
2
篇文章
8
次点赞
swet123
1
篇文章
4
次点赞
Days
1
篇文章
4
次点赞
YZRD
1
篇文章
2
次点赞
回到
顶部
发布
问题
投诉
建议
回到
底部