7

RT-Thread 内核学习笔记 - 理解defunct僵尸线程

 3 years ago
source link: https://my.oschina.net/u/4428324/blog/5025435
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

RT-Thread 内核学习笔记 - 内核对象rt_object

RT-Thread 内核学习笔记 - 内核对象管理

RT-Thread 内核学习笔记 - 内核对象操作API

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 */

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK