天天看點

【RT-Thread筆記】對象容器與雙連結清單

前言

在我們嵌入式中,可能會有些人認為資料結構與算法相關知識沒什麼用,很少用得上。

以前,我也是這麼認為的,那東西那麼難學,好像又用不上,學了有什麼用,幹脆就不學了。

直到後面深入學習一些東西之後發現,原來那些知識并不是沒有用,隻是當時我們的認知還不足。廢話不多說,下面進入正題:

對象容器與雙連結清單

1、RT-Thread中的對象容器

RT-Thread

 核心對象包括:線程,信号量,互斥量,事件,郵箱,消息隊列和定時器,記憶體池,裝置驅動等。

對象容器中包含了每類核心對象的資訊,包括對象類型,大小等。

對象容器給每類核心對象配置設定了一個連結清單,所有的核心對象都被連結到該連結清單上, RT-Thread 的核心對象容器及連結清單如下圖所示:

【RT-Thread筆記】對象容器與雙連結清單

這個對象容器對應到代碼上是一個結構體數組,這個結構體數組在

object.c

裡定義,其内容如下(已做删減):

static struct rt_object_information 
rt_object_container[RT_Object_Info_Unknown] =
{
    /* 初始化對象容器 - 線程 */
    {
      RT_Object_Class_Thread,
      _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Thread),
      sizeof(struct rt_thread)
    },
	
#ifdef RT_USING_SEMAPHORE
    /* 初始化對象容器 - 信号量 */
    {
      RT_Object_Class_Semaphore,
      _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Semaphore),
      sizeof(struct rt_semaphore)
    },
#endif

    /* 省略部分代碼...*/
	
    /* 初始化對象容器 - 定時器 */
    {
      RT_Object_Class_Timer,
      _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Timer),
      sizeof(struct rt_timer)
    },

    /* 省略部分代碼...*/
};
           

其中,核心對象資訊結構體  

struct rt_object_information

 的定義如下:

struct rt_object_information
{
  enum rt_object_class_type type; /**< 對象類型 */
  rt_list_t object_list; /**< 對象連結清單節點頭 */
  rt_size_t object_size; /**< 對象大小 */
};
           

RT-Thread 的核心對象的繼承關系如:

【RT-Thread筆記】對象容器與雙連結清單

在代碼閱讀器

Source Insight

中也可以看出這樣的關系:

【RT-Thread筆記】對象容器與雙連結清單

每個核心對象的初始化函數裡都有調用對象初始化函數

rt_object_init

而對象初始化函數裡做了什麼呢?看其内部實作(已做删減):

void rt_object_init(struct rt_object         *object,
                    enum rt_object_class_type type,
                    const char               *name)
{
  register rt_base_t temp;
  struct rt_object_information *information;

  /* 擷取對象資訊,即從容器裡拿到對應對象清單頭指針 */
  information = rt_object_get_information(type);
  RT_ASSERT(information != RT_NULL);

  /* 設定對象類型為靜态 */
  object->type = type | RT_Object_Class_Static;

  /* 拷貝名字 */
  rt_strncpy(object->name, name, RT_NAME_MAX);

  /* 關中斷 */
  temp = rt_hw_interrupt_disable();

  /* 把對象資訊插入到對象連結清單中 */
  rt_list_insert_after(&(information->object_list), &(object->list));

  /* 開中斷 */
  rt_hw_interrupt_enable(temp);
}
           

這裡用到的

rt_list_insert_after

函數就是雙連結清單插入函數。

2、RT-Thread中雙連結清單的操作

RT-Thread的對象容器是依賴于雙連結清單(

雙向循環連結清單

)的,其雙連結清單的相關操作在檔案

rtservice.h

中:

其節點結構體為:

struct rt_list_node
{
    struct rt_list_node *next;         /**< 指向後一個節點 */
    struct rt_list_node *prev;         /**< 指向後一個節點 */
};
typedef struct rt_list_node rt_list_t;                 
           

下面介紹幾個基本的操作接口(截圖來自野火的RT-Thread書籍):

(1)初始化連結清單節點:

【RT-Thread筆記】對象容器與雙連結清單
rt_inline void rt_list_init(rt_list_t *l)
{
  l->next = l->prev = l;/* 節點的next指針與prev指針都指向其本身 */
}
           

(2)  在雙向連結清單表頭後面插入一個節點:

【RT-Thread筆記】對象容器與雙連結清單
rt_inline void rt_list_insert_after(rt_list_t *l, rt_list_t *n)
{
  l->next->prev = n; /* 第(1)步 */
  n->next = l->next; /* 第(2)步 */

  l->next = n;      /* 第(3)步 */
  n->prev = l;      /* 第(4)步 */
}
           

(3)在雙向連結清單表頭前面(表尾後面)插入一個節點:

【RT-Thread筆記】對象容器與雙連結清單
rt_inline void rt_list_insert_before(rt_list_t *l, rt_list_t *n)
{
  l->prev->next = n; /* 第(1)步 */
  n->prev = l->prev; /* 第(2)步 */

  l->prev = n;       /* 第(3)步 */
  n->next = l;	     /* 第(4)步 */
}
           

(4)從雙向連結清單删除一個節點:

【RT-Thread筆記】對象容器與雙連結清單
rt_inline void rt_list_remove(rt_list_t *n)
{
  n->next->prev = n->prev; /* 第(1)步 */
  n->prev->next = n->next; /* 第(2)步 */

  n->next = n->prev = n;   /* 第(3)步 */
}
           

初始化對象清單節點頭裡面的 next 和 prev 兩個節點指針分别指向自身,如:

【RT-Thread筆記】對象容器與雙連結清單

若建立兩個led線程對象,則對象容器裡變為:

【RT-Thread筆記】對象容器與雙連結清單

順便提一個關于連結清單的一個名詞上的疑惑,是

節點

還是

結點

?我之前也看過一些書,有的書寫為節點,有的書寫為結點,我也不知道哪個更準确。不糾結了,知道這麼一回事就可以了,不影響我們學習。

最後

看到了吧,資料結構在嵌入式中還是很有用的吧,更多的細節可以閱讀其源碼。

參考資料:

1、官方的《RT-THREAD 程式設計指南》

2、野火的《RT-Thread 核心實作與應用開發實戰指南》

繼續閱讀