rt-thread 优化系列(六)启动流程重构

描述

前言

去年此时,笔者刚接触 rt-thread 的时候,被它的初始化过程深深折服了。第一次打开一个 rt-thread 的项目,竟然没找到多线程在哪儿初始化的,"main" 函数里没有!
进而发现,"main" 函数不是我们认识的 "main" 函数了。在 rt-thread 里,程序进入 "main" 函数之前还有很长一段路。

目前的初始化流程

见下图吧,一些主要的过程已经被列出来了。
 

RT-Thread

从图中可以看出来:
1. 任务调度器启动前,有很多的初始化工作。
2. `rt_components_board_init` 里还有很多隐含的外设初始化操作。这个时候没有进入多线程环境。
3. 先创建了三个默认线程(如果启用了软定时器),但并未立马运行,任务调度器启动后从中选择优先级最高的那个运行(可能是 "main" 可能是 "timer")。
4. "main" 线程启动后,还有很多初始化工作,这个时候已经进入多线程环境。

这个流程存在的问题:
1. 堆申请被设计成多线程安全访问,内部有加锁动作,而锁也是限于在线程上下文使用的。串口驱动申请缓存内存却是在任务调度器启动前!
2. 开启 ulog 后,也有可能出现在任务调度器启动之先使用锁的现象。
3. 还有线程安全版 `rt_kprintf` ,这个也是考虑只在线程上下文使用,如果开启了它,任务调度器启动前的 `rt_kprintf` 需求被忽略了。

`rt_components_board_init` `rt_components_init` 两个函数可能有很多隐含操作。但是因为 `rt_components_board_init` 处于非多任务环境中,它比 `rt_components_init` 更容易出问题。

例如,不能使用加锁操作、不能使用 `rt_thread_mdelay` `rt_thread_delay` 等延时操作。

在之前的一篇文章里,[rt-thread 驱动篇(六)serialX弊端及解决方法]( http://www.elecfans.com/d/1850548.html ) 比较全面的梳理了一下串口终端设备怎么用中断发送模式。首先,任务调度器启动前是不会有中断的,用中断发送,可能不会输出数据,如果是阻塞模式,可能会永久卡死到这里;如果是非阻塞模式,可能有很多信息被扔掉。

**所以,任务调度器启动前,串口终端输出要么用轮询输出,要么给个很大的输出缓存**,至于多大合适,这个谁也说不好。

还有一个折中的方法是降低任务调度器启动前的打印输出需求。怎么降低?调整初始化流程,先启动一个无依赖的 idle 线程,把其它操作放到线程中。

新初始化流程设想

RT-Thread

这样一来,初始化注册外设设备的过程也是在线程上下文环境了,这个时候想用加锁操作也可以,申请堆内存也不会出现 `Function[rt_sem_take] shall not be used before scheduler start`

如果不开启 RT_DEBUG,堆、pin、UART、控制台初始化部分也可以放到 idle 线程里。一年前,笔者在 rt-thread 论坛上提出过这个设想——[
rt-thread 系统启动及 SysTick 初始化流程优化可行性分析]( https://club.rt-thread.org/ask/article/ab1c5556dd9f186a.html )。
稍作修改,如下:
1. 配置系统时钟,同时配置 SysTick(同前);
2. 初始化 rtt 系统调度器,定时器链表等全局变量;
3. 创建 idle 线程;
4. 启动 idle 线程并启动 rtt 系统调度;
5. 由 idle 线程启用 SysTick 中断;
6. 由 idle 线程初始化调试串口;
7. 由 idle 线程初始化内存堆;
8. 由 idle 线程调用执行 rt_components_board_init 初始化板级外设配置;
9. 由 idle 线程创建 main 和 soft timer 线程。
10. main 线程进行组件初始化配置,以及创建其它应用线程。

和上面链接里提到的流程,修改了开启 systick 中断节点。
同时如果放弃在初始化配置系统时钟时调试输出信息的想法,放弃创建 idle 线程过程中调试输出错误信息的想法,放弃启动系统任务调度器过程调试输出错误信息的想法后,控制台输出信息也就全处于线程上下文了。

最起码,我们有以下几点收获:
1. 任务调度器启动前,不需要开全局中断。
2. 申请堆内存的操作都可以是在线程上下文。
3. 控制台串口输出数据可以使用中断或者 DMA 任意模式。

缺点:idle 线程栈可能比之前要大一些。而且,这些内存只有一次使用机会,切换到 “main” 线程后多了些“闲置”内存。

实践验证

目前,笔者在自己的项目上,使用 NUC970 系列芯片,验证了上述想法,目前系统启动过程未发现出现异常。

结束语

很多问题是时序的原因,打乱了某件东西本该有的操作 [#5584]( https://github.com/RT-Thread/rt-thread/issues/5584 )。
对系统启动流程做些修改,我们发现很多概念可以理直气壮的讲出来,比如,锁只能在线程上下文使用。当我们通过补丁的方式,使得在任务调度器启动前进行了锁操作而不出异常,那么这样就让人迷惑了。
 

  审核编辑:汤梓红
 
打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分