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

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

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

RT-Thread 内核学习笔记 - 内核对象初始化链表组织方式

RT-Thread 内核学习笔记 - 内核对象链表结构深入理解

RT-Thread 内核学习笔记 - 设备模型rt_device的理解

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

背景

  • 最近在看RT-Thread内核的源码,内核对象使用链表组织。
  • RT-Thread内部有完整的【双向链表】与【单向链表】的操作API与实际用例
  • 内核对象,内核派生的如线程、定时器、设备等,都是由链表链起来管理的。

链表介绍

/**
 * Double List structure
 */
struct rt_list_node
{
    struct rt_list_node *next;                          /**< point to next node. */
    struct rt_list_node *prev;                          /**< point to prev node. */
};
typedef struct rt_list_node rt_list_t;                  /**< Type for lists. */
  • 链表是一种数据结构,跟其他的结构体类似,初始化后,本身占用内存空间,自身有内存地址。
  • 一般双向链表,内部的成员,是链表本身结构体的指针,注意,指针的指向,初始化后,并没有确定。
  • RT-Thread 的双向链表,初始化时,内部的指针,指向自己本身的地址,也就是给链表的成员,赋好了初值(指针内容,一般是地址)。
  • 链表使用,节点一般都是【全局结构体变量】,全局静态初始化,或动态内存申请(全局)。
  • 不用全局的链表节点,注意不要链入链表结构,否则节点地址因为【生命周期】结束内存释放了,地址就不对了,就无法管理各个链表的节点了。

双向链表API

  • 这里注要提一下链表节点的插入【次序】,因为遇到了一点小困惑,所以深入的研究了下。
  • 理解:【最新节点】【前一个节点】【最早节点】
  • 链表的头的问题:内核对象使用【对象容器】,全局的,对象初始化后,用了 rt_list_insert_after
/* 来自:object.c :rt_object_init */
        /* insert object into information object list */
        rt_list_insert_after(&(information->object_list), &(object->list));
  • 注意,rt_list_insert_after,插入的位置,【不是链表的尾部插入,是第一个链表节点【第一个参数】的后面。】
  • 也就是说,如果创建了3个内核对象,默认的排序如下:
不是:【容器head】 --- [obj1] --- [obj2] --- [obj2]
而是:【容器head】 --- [obj3] --- [obj2] --- [obj1] 
  • 如果把链表插入到【尾部】后面,就要先把链表指针移到尾部,再执行:rt_list_insert_after.

查看内核对象

  • 其实,thread,device等对象,都是内核对象【派生】出来的。
  • RT-Thread 提供list_thread、list_device等,查看内核的对象。
  • 查看线程初始化【次序】,看看最后打印的线程,就是【最先】首个创建的线程。
  • 一般从链表【头部】开始遍历各个链表节点。如下:最后一个节点是:main线程.
  • 其实,main线程,是第一个创建的。
msh />list_thread
thread   pri  status      sp     stack size max used left tick  error
-------- ---  ------- ---------- ----------  ------  ---------- ---
persim    16  suspend 0x000001ec 0x0000c000    08%   0x00000003 000
sens      28  suspend 0x000000d8 0x00001000    13%   0x00000019 000
hws       28  suspend 0x000000d8 0x00000800    10%   0x00000032 000
dcm_tpo   10  suspend 0x00000090 0x00000800    14%   0x00000004 000
dcm_tpo   10  suspend 0x00000090 0x00000800    14%   0x00000002 000
dcm_tpo   10  suspend 0x00000090 0x00000800    15%   0x00000004 000
tshell    20  running 0x000001fc 0x00001000    26%   0x0000000a 000
touch     16  suspend 0x00000098 0x00000800    18%   0x00000013 000
usbd       8  suspend 0x000000ac 0x00001000    04%   0x00000014 000
at_clnt    9  suspend 0x000000c0 0x00000600    12%   0x00000002 000
ulog_asy  30  suspend 0x00000084 0x00000c00    09%   0x00000006 000
mmcsd_de  22  suspend 0x000000a0 0x00000400    48%   0x00000014 000
alarmsvc  10  suspend 0x000000a8 0x00000800    27%   0x00000003 000
rils      12  suspend 0x000000b0 0x00000800    08%   0x0000001e 000
tidle0    31  ready   0x00000058 0x00000800    04%   0x0000001d 000
timer      4  suspend 0x00000074 0x00000800    08%   0x00000009 000
main      10  suspend 0x00000120 0x00000800    41%   0x00000012 000  /* 最先创建的线程,最后打印 */
  • 内核对象初始化的链表【次序】:

RT-Thread 内核学习笔记 - 内核对象初始化链表组织方式-LMLPHP

总结

  • 使用链表时,注意插入的次序,尤其是FIFO的缓冲区的读写处理
  • 熟悉RT-Thread 内核对象,对象管理方法。
  • 深入研究基本数据结构的使用:链表
04-20 00:11