RT-Thread 内核学习笔记 - 内核对象rt_object
RT-Thread 内核学习笔记 - 内核对象初始化链表组织方式
RT-Thread 内核学习笔记 - 内核对象链表结构深入理解
RT-Thread 内核学习笔记 - 设备模型rt_device的理解
RT-Thread 内核学习笔记 - 理解defunct僵尸线程
前言
- 目前大家偶尔会讨论RT-Thread线程退出的问题,如main线程,return后,怎么处理的?RAM等资源,是否得到释放。
- 最近在看线程相关的内核源码,基于内核对象rt_object管理方法,梳理一下线程退出后的处理流程。
- rt_thread_defunct,僵尸线程的链表结构,为什么叫【僵尸】,我查的字典。作用,回收删除的线程的内存(堆)资源。
线程初始化与创建
- rt_thread_init:静态初始化一个线程,线程结构体、线程栈,都是全局的变量。rt_thread_detach后,这个线程的内核对象从内核容器链表里移除,【但】线程结构体、线程栈,因为是静态全局的,无法释放。若下次再想初始化并使用这个线程,依旧可以使用这个detach后的现有的线程结构体、线程栈进行初始化。静态线程的特点:初始化后,内存的占用,就不会改变。
- rt_thread_create:动态创建一个线程。需要使能:RT_USING_HEAP,堆管理。创建的线程【结构体】与【线程栈】都是动态申请出来的。删除这个线程时,需要调用rt_thread_delete。删除后,线程的【结构式】与【线程栈】占用的内存(堆)空间,可以释放。删除后,这个线程不存在了,想再次开启使用,需要重新创建。动态线程的特点:删除后,内存资源可以释放。
线程的资源回收
- RT-Thread 只回收rt_thread_create的线程内存资源。
- 静态初始化或动态创建线程时,会注册:rt_thread_exit,线程退出后,会调用rt_thread_exit。
- rt_thread_exit中,第一步:把线程从调度链表移除。第二步:静态的线程,会调用:rt_object_detach,从内核对象容器里移除线程内核对象;动态线程,会把线程的结构体指针(操作句柄),加入rt_thread_defunct僵尸线程链表中,而不是立即释放线程占用的内存。僵尸线程的操作,是在idle线程中执行。第三步:执行线程调度,切换线程。在idle线程执行,应该是保证这个线程删除后,立即调度切换线程,线程的资源回收不需要太高的优先级。
- idle 线程中: rt_thread_idle_excute 负责查看rt_thread_defunct僵尸线程链表是否为空,如果不为空,则执行内存释放的操作。从线程链表移除、释放线程栈、释放内核结构体。注意,只有动态创建的线程,执行此操作。
/* 来自:rt_thread_idle_excute 片段 */
/* remove defunct thread */
rt_list_remove(&(thread->tlist));
/* release thread's stack */
RT_KERNEL_FREE(thread->stack_addr);
/* delete thread object */
rt_object_delete((rt_object_t)thread);
main线程退出
- 上面梳理了线程的退出,僵尸线程的处理流程,main线程的退出,基本上也可以梳理流程了
- 静态的main线程,未使能:RT_USING_HEAP,退出后,内存资源不会释放。
- 动态的main线程,使能了:RT_USING_HEAP后,退出后,内存资源得到释放。
- 实际对比内存资源的释放大小,发现,动态申请,会额外占用一点内存资源,如12字节,这部分在后面内存管理后再深入的梳理。
动态线程的资源回收:
RT_USING_HEAP
main存在时:线程栈大小为2048
msh >free
total memory: 89568
used memory : 10656
maximum allocated memory: 10656
main不存在时:
msh >free
total memory: 89568
used memory : 8456
maximum allocated memory: 10656
main的线程栈,return后的资源:2200 Bytes
空间
2048 栈空间 + 12Byte(rt_malloc管理占用)
128Byte rt_thread结构体大小,sizeof(struct rt_thread) + 12Byte(rt_malloc管理占用,内核对象)。
合计:2048+12+128+12 = 2200。
总结
- 熟悉RT-Thread线程的初始化、创建、脱离(反初始化)、删除等操作。
- 熟悉动态创建的线程,删除后,加入僵尸线程链表,内存资源回收流程。
- 理解普通线程如main线程的退出流程。
带着问题后续继续研究以下知识点:
- 调度器的工作流程,在哪里执行?如何运作?
- 线程定时器,每个线程都会创建一个,线程删除后,定时器资源的回收流程。
rt_thread_delete /* 线程的定时器,使用detach方式,定时器资源回收流程 */ rt_timer_detach(&(thread->thread_timer)); /* 定时器资源的回收流程 */
- 创建线程时,我把线程的名字(name)改为RT_NULL,依旧可以正常的运行,但这样是否有影响?
- rt_thread 结构体中:内核对象的链表用于加入内核对象容器,成员:tlist的工作流程。
- 历史问题:rt_thread结构体:
/**
* Thread structure
*/
struct rt_thread
{
/* rt object */
char name[RT_NAME_MAX]; /**< the name of thread */
rt_uint8_t type; /**< type of object */
rt_uint8_t flags; /**< thread's flags */
#ifdef RT_USING_MODULE
void *module_id; /**< id of application module */
#endif
rt_list_t list; /**< the object list */
是否可以改为如下:
/**
* Thread structure
*/
struct rt_thread
{
struct rt_object parent; /**< inherit from rt_object */