Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
RT-Thread一般讨论
rt-thread 系统优化系列(五) 之 线程销毁谜题
1.00
发布于 2021-11-08 11:19:14 浏览:2248
订阅该版
[tocm] ## 前言 不知不觉,优化系列到第五篇了。但是,精简代码的速度远远赶不上堆砌速度,愿诸君砥砺前行,同心齐力! 今天讲一个 rt-thread 提供的特性,但是大家从来不使用。我们讲讲它怎么用,目前它存在的隐患,以及怎么修补。 ## 线程清理 一个线程有创建也有销毁,线程启动前可能申请了很多资源,线程退出前需要释放自己申请的资源,打扫自己的战场。 ### 线程清理的内核层实现 我们看 rt-thread 的源码,从 rtdef.h 头文件中可以找到如下说明 ``` 2010-11-10 Bernard add cleanup callback function in thread exit. ``` 当线程退出时的清理回调函数。继续往下找到线程控制块 `struct rt_thread` 定义中有一个 cleanup 的函数指针成员,这个是回调函数指针。 ``` void (*cleanup)(struct rt_thread *tid); /**< cleanup function when thread exit */ ``` 切换到 thread.c 文件中,继续查看和 'cleanup' 函数指针相关的代码。嘿嘿,果然有调用回调函数的操作: ``` /* invoke thread cleanup */ if (thread->cleanup != RT_NULL) thread->cleanup(thread); ``` 继续找找初始化 'cleanup' 或者指定回调函数实体的操作。可以找到如下代码 ``` /* initialize cleanup function and user data */ thread->cleanup = 0; thread->user_data = 0; ``` 再找, 仅此而已了。 ### 线程清理的用户层实现 说起线程退出清理线程现场,多数使用场景可以在线程入口回调函数里进行。 因为我们都知道想让一个线程永久运行下去,线程入口回调函数内必然有个 `while(1)` 死循环。如果想退出线程,从 `while(1)` 死循环里跳出来,线程入口函数返回退出就行了。 那么,跳出 while 循环之后,退回函数之前,也可以添加我们的清理工作。如下所示: ``` void thread_xxx_entry(void *parameter) { while (1) { ... } // cleanup here } ``` 无论是 `thread_xxx_entry` 函数内部局部变量申请的资源,或者是全局变量资源,把清理工作放这里,应用层的逻辑也会很清晰。 ## 让人迷惑的线程退出 ### `_thread_exit` 先看一下 `_thread_exit` 函数的实现(省去部分无关代码) ``` static void _thread_exit(void) { ... _thread_cleanup_execute(thread); ... if (rt_object_is_systemobject((rt_object_t)thread) == RT_TRUE) { rt_object_detach((rt_object_t)thread); } else { /* insert to defunct thread list */ rt_thread_defunct_enqueue(thread); } /* switch to next task */ rt_schedule(); ... } ``` 1. `_thread_exit` 调用 `_thread_cleanup_execute`; `_thread_cleanup_execute` 执行 `cleanup` 回调函数。如果当前线程是动态创建的,调用 `rt_thread_defunct_enqueue` ,把当前线程放到僵尸线程列表里。 2. `_thread_cleanup_execute` 清理工作函数里还做了两件事:一、如果开启的模块,销毁线程中的模块;二、如果开启的信号signal(非信号量semaphore),释放信号(rt_thread_free_sig)。 3. 如果当前线程是静态对象,调用 `rt_object_detach` 使得线程变成游离态;如果当前线程是动态对象,回收所有线程申请的堆内存。 当前线程进入僵尸线程的后续操作是什么?请看 idle 线程。 ### idle 线程 #### `rt_defunct_execute` 函数实现 idle 线程的一项重要工作就是清理僵尸线程,在 4.0.3 版本叫 `rt_thread_idle_excute`,4.0.4 版本改名字叫 `rt_defunct_execute`。这里贴出来 4.0.4 版本 `rt_defunct_execute` 函数的全部代码。 ``` static void rt_defunct_execute(void) { /* Loop until there is no dead thread. So one call to rt_defunct_execute * will do all the cleanups. */ while (1) { rt_base_t lock; rt_thread_t thread; void (*cleanup)(struct rt_thread *tid); #ifdef RT_USING_MODULE struct rt_dlmodule *module = RT_NULL; #endif RT_DEBUG_NOT_IN_INTERRUPT; /* disable interrupt */ lock = rt_hw_interrupt_disable(); #ifdef RT_USING_MODULE /* check whether list is empty */ if (!_idle_has_defunct_thread()) { rt_hw_interrupt_enable(lock); break; } /* get defunct thread */ thread = rt_list_entry(_rt_thread_defunct.next, struct rt_thread, tlist); module = (struct rt_dlmodule*)thread->module_id; if (module) { dlmodule_destroy(module); } /* remove defunct thread */ rt_list_remove(&(thread->tlist)); #else thread = rt_thread_defunct_dequeue(); if (!thread) { rt_hw_interrupt_enable(lock); break; } #endif /* invoke thread cleanup */ cleanup = thread->cleanup; if (cleanup != RT_NULL) { rt_hw_interrupt_enable(lock); cleanup(thread); lock = rt_hw_interrupt_disable(); } #ifdef RT_USING_SIGNALS rt_thread_free_sig(thread); #endif /* if it's a system object, not delete it */ if (rt_object_is_systemobject((rt_object_t)thread) == RT_TRUE) { /* detach this object */ rt_object_detach((rt_object_t)thread); /* enable interrupt */ rt_hw_interrupt_enable(lock); } else { rt_hw_interrupt_enable(lock); #ifdef RT_USING_HEAP /* release thread's stack */ RT_KERNEL_FREE(thread->stack_addr); /* delete thread object */ rt_object_delete((rt_object_t)thread); #endif } } } ``` #### `rt_defunct_execute` 函数工作概述 从以上源码中可以看出来,`rt_defunct_execute` 函数的工作有如下几部分: 1. 如果启用了模块,销毁线程中的定义的模块。 2. 调用线程控制块的 cleanup 回调函数。 3. 如果使用了信号signal,调用 `rt_thread_free_sig` 销毁线程信号。 4. 如果当前线程是静态对象,调用 `rt_object_detach` 使得线程变成游离态;如果当前线程是动态对象,回收所有线程申请的堆内存。 咦?!恍惚中,有些东西好像在哪儿见过! ***`_thread_exit` 和 `rt_defunct_execute` 的工作几乎完全重合!!!*** ***类似的,还有 `rt_thread_detach` `rt_thread_delete` 两个函数*** ### 优化方案 沿用 4.0.4 更新的逻辑,得出一下更新方案 1. 保留 `rt_defunct_execute` 中的操作,删掉 `_thread_cleanup_execute` 函数以及对其的所有调用。 2. 无论静态线程对象还是动态线程对象,都应该加入到僵尸线程列表,交给 idle 线程进行回收。 3. 由 idle 线程中 `rt_defunct_execute` 函数决定静态线程执行 detach 操作,动态线程执行 delete 操作。`_thread_exit` `rt_thread_detach` 中一律执行 `rt_thread_defunct_enqueue`。 > 如果 4.0.4 的更新是伪操作,请忽略以上方案。 恕我眼拙,idle.c 文件中的 `_idle_has_defunct_thread` `rt_thread_defunct_dequeue` 两个函数实现是不是有异曲同工之妙? ``` #ifdef RT_USING_MODULE /* Return whether there is defunctional thread to be deleted. */ rt_inline int _idle_has_defunct_thread(void) { /* The rt_list_isempty has prototype of "int rt_list_isempty(const rt_list_t *l)". * So the compiler has a good reason that the _rt_thread_defunct list does * not change within rt_thread_defunct_exceute thus optimize the "while" loop * into a "if". * * So add the volatile qualifier here. */ const volatile rt_list_t *l = (const volatile rt_list_t *)&_rt_thread_defunct; return l->next != l; } #endif /* RT_USING_MODULE */ rt_thread_t rt_thread_defunct_dequeue(void) { rt_thread_t thread = RT_NULL; rt_list_t *l = &_rt_thread_defunct; if (l->next != l) { thread = rt_list_entry(l->next, struct rt_thread, tlist); rt_list_remove(&(thread->tlist)); } return thread; } ``` ## 结束语 很高兴看到从 4.0.3 到 4.0.4 的改动,代码更清晰了。但同时也引入了新的问题。 也不知是提交更新者不知道原来的代码的存在,以为自己添加了某些新操作;还是提交更新者本意想把这些操作挪到 idle 线程下进行,但忘记了删除原来的代码。 PS: 本次所提到的修改,经测试后,近期会提交到我的仓库里,敬请期待 相关文章: [rt-thread 系统优化系列(一) 之 关中断](https://club.rt-thread.org/ask/article/2931.html) [rt-thread 系统优化系列(二) 之 线程间同步和通信对中断的影响](https://club.rt-thread.org/ask/article/2939.html) [rt-thread 系统优化系列(三) 之 软定时器](https://club.rt-thread.org/ask/article/2967.html) [ rt-thread 系统优化系列(四) 之 再谈 ipc 中的 bug](https://club.rt-thread.org/ask/article/3044.html) [rt-thread 系统优化系列(五) 之 线程销毁谜题](https://club.rt-thread.org/ask/article/3138.html) [rt-thread 系统优化系列(六) 之 让 idle 线程闲下来](https://club.rt-thread.org/ask/article/3141.html)
3
条评论
默认排序
按发布时间排序
登录
注册新账号
关于作者
出出啊
恃人不如自恃,人之为己者不如己之自为也
文章
43
回答
1517
被采纳
342
关注TA
发私信
相关文章
1
有关动态模块加载的一篇论文
2
最近的调程序总结
3
晕掉了,这么久都不见layer2的踪影啊
4
继续K9ii的历程
5
[GUI相关] FreeType 2
6
[GUI相关]嵌入式系统中文输入法的设计
7
20081101 RT-Thread开发者聚会总结
8
嵌入式系统基础
9
linux2.4.19在at91rm9200 上的寄存器设置
10
[转]基于嵌入式Linux的通用触摸屏校准程序
推荐文章
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
USB
DMA
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
FAL
rt-smart
ESP8266
I2C_IIC
UART
WIZnet_W5500
ota在线升级
freemodbus
PWM
flash
cubemx
packages_软件包
BSP
潘多拉开发板_Pandora
定时器
ADC
flashDB
GD32
socket
中断
编译报错
Debug
SFUD
rt_mq_消息队列_msg_queue
msh
keil_MDK
ulog
C++_cpp
MicroPython
本月问答贡献
踩姑娘的小蘑菇
4
个答案
1
次被采纳
红枫
4
个答案
1
次被采纳
张世争
4
个答案
1
次被采纳
Ryan_CW
4
个答案
1
次被采纳
xiaorui
1
个答案
1
次被采纳
本月文章贡献
catcatbing
3
篇文章
5
次点赞
qq1078249029
2
篇文章
2
次点赞
xnosky
2
篇文章
1
次点赞
Woshizhapuren
1
篇文章
5
次点赞
YZRD
1
篇文章
2
次点赞
回到
顶部
发布
问题
投诉
建议
回到
底部