RT-Thread進階筆記之內核架構

1、RT-Thread 軟件架構

在這裏插入圖片描述

2、RT-Thread 內核結構

在這裏插入圖片描述

3、 預備知識

3.1鏈表

鏈表是通過節點把離散的數據鏈接成一個表,通過對節點的插入和刪除操作從而實現對數據的存取。而數組是通過開闢一段連續的內存來存儲數據,這是數組和鏈表最大的區別。數組的每個成員對應鏈表的節點,成員和節點的數據類型可以是標準的C類型或者是用戶自定義的結構體。數組有起始地址和結束地址,而鏈表是一個圈,沒有頭和尾之分,但是爲了方便節點的插入和刪除操作會人爲的規定一個根節點。
單向鏈表:
在這裏插入圖片描述
在這裏插入圖片描述
雙向鏈表:
在這裏插入圖片描述
在這裏插入圖片描述

3.2 節點

3.2.2 節點初始化

rt_inline void rt_list_init(rt_list_t *l)
{
    l->next = l->prev = l;
}

在這裏插入圖片描述

3.2.3 在雙向鏈表表頭後面插入一個節點

rt_inline void rt_list_insert_after(rt_list_t *l, rt_list_t *n)
{
    l->next->prev = n;
    n->next = l->next;

    l->next = n;
    n->prev = l;
}

在這裏插入圖片描述

3.2.4 在雙向鏈表表頭前面插入一個節點

rt_inline void rt_list_insert_before(rt_list_t *l, rt_list_t *n)
{
    l->prev->next = n;
    n->prev = l->prev;

    l->prev = n;
    n->next = l;
}

在這裏插入圖片描述

3.2.5 從雙向鏈表刪除一個節點

rt_inline void rt_list_remove(rt_list_t *n)
{
    n->next->prev = n->prev;
    n->prev->next = n->next;

    n->next = n->prev = n;
}

在這裏插入圖片描述

3.2.6 從節點中獲取所在結構體的首地址

#define rt_list_entry(node, type, member) \
    rt_container_of(node, type, member)
#define rt_list_for_each(pos, head) \
    for (pos = (head)->next; pos != (head); pos = pos->next)

node 表示一個節點的地址, type 表示該節點所在的結構體的類型,
member 表示該節點在該結構體中的成員名稱。
rt_container_of()的實現算法具體見圖:
在這裏插入圖片描述
我們知道了一個節點 tlist 的地址 ptr,現在要推算出該節點所在的 type 類型的結構體的起始地址 f_struct_ptr。我們可以將 ptr 的值減去圖中灰色部分的偏移的大小就可以得到 f_struct_ptr 的地址,現在的關鍵是如何計算出灰色部分的偏移大小。這裏採取的做法是將 0 地址強制類型類型轉換爲 type,即(type *)0,然後通過指針訪問結構體成員的方式獲取到偏移的大小,即(&((type *)0)->member), 最後即可算出 f_struct_ptr = ptr -(&((type *)0)->member)。

3.3 面向對象編程思想

3.3.1 封裝

C/C++面向對象編程之封裝

3.3.2 繼承

C/C++面向對象編程之繼承

3.3.3 多態

C/C++面向對象編程之多態

4 內核對象管理架構

RT-Thread 採用內核對象管理系統來訪問 / 管理所有內核對象,內核對象包含了內核中線程,信號量,互斥量,事件,郵箱,消息隊列和定時器,內存池,設備驅動等。對象容器中包含了每類內核對象的信息,包括對象類型,大小等。對象容器給每類內核對象分配了一個鏈表,所有的內核對象都被鏈接到該鏈表上,如圖 RT-Thread 的內核對象容器及鏈表如下圖所示:
在這裏插入圖片描述
下圖則顯示了 RT-Thread 中各類內核對象的派生和繼承關係:
在這裏插入圖片描述

4.1 內核對象數據結構

4.1.1 對象結構

在這裏插入圖片描述

4.1.2 對象容器結構

在這裏插入圖片描述

4.1.3 初始化對象容器

容 器 是 一 個 全 部 變 量 的 數 組 , 數 據 類 型 爲 struct rt_object_information, 這是一個結構體類型, 包含對象的三個信息,分別爲對象類型、對象列表節點頭和對象的大小。

static struct rt_object_information rt_object_container[RT_Object_Info_Unknown] =
{
    /* initialize object container - thread */
    {RT_Object_Class_Thread, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Thread), sizeof(struct rt_thread)},
#ifdef RT_USING_SEMAPHORE
    /* initialize object container - semaphore */
    {RT_Object_Class_Semaphore, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Semaphore), sizeof(struct rt_semaphore)},
#endif
#ifdef RT_USING_MUTEX
    /* initialize object container - mutex */
    {RT_Object_Class_Mutex, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Mutex), sizeof(struct rt_mutex)},
#endif
#ifdef RT_USING_EVENT
    /* initialize object container - event */
    {RT_Object_Class_Event, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Event), sizeof(struct rt_event)},
#endif
#ifdef RT_USING_MAILBOX
    /* initialize object container - mailbox */
    {RT_Object_Class_MailBox, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_MailBox), sizeof(struct rt_mailbox)},
#endif
#ifdef RT_USING_MESSAGEQUEUE
    /* initialize object container - message queue */
    {RT_Object_Class_MessageQueue, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_MessageQueue), sizeof(struct rt_messagequeue)},
#endif
#ifdef RT_USING_MEMHEAP
    /* initialize object container - memory heap */
    {RT_Object_Class_MemHeap, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_MemHeap), sizeof(struct rt_memheap)},
#endif
#ifdef RT_USING_MEMPOOL
    /* initialize object container - memory pool */
    {RT_Object_Class_MemPool, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_MemPool), sizeof(struct rt_mempool)},
#endif
#ifdef RT_USING_DEVICE
    /* initialize object container - device */
    {RT_Object_Class_Device, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Device), sizeof(struct rt_device)},
#endif
    /* initialize object container - timer */
    {RT_Object_Class_Timer, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Timer), sizeof(struct rt_timer)},
#ifdef RT_USING_MODULE
    /* initialize object container - module */
    {RT_Object_Class_Module, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Module), sizeof(struct rt_dlmodule)},
#endif
};

_OBJ_CONTAINER_LIST_INIT()是一個帶參宏,用於初始化一個
節點 list,在 object.c 中定義

#define _OBJ_CONTAINER_LIST_INIT(c)     \
    {&(rt_object_container[c].object_list), &(rt_object_container[c].object_list)}

在這裏插入圖片描述

4.1.4 從容器中獲取指定類型的對象信息

struct rt_object_information *
rt_object_get_information(enum rt_object_class_type type)
{
    int index;

    for (index = 0; index < RT_Object_Info_Unknown; index ++)
        if (rt_object_container[index].type == type) return &rt_object_container[index];

    return RT_NULL;
}

4.1.5 初始化對象

每創建一個對象,都需要先將其初始化,主要分成兩個部分的工作,首先將對象控制塊裏面與對象相關的成員初始化,然後將該對象插入到對象容器中。

/**
 * This function will initialize an object and add it to object system
 * management.
 *
 * @param object the specified object to be initialized.
 * @param type the object type.
 * @param name the object name. In system, the object's name must be unique.
 */
void rt_object_init(struct rt_object         *object,
                    enum rt_object_class_type type,
                    const char               *name)
{
    register rt_base_t temp;
    struct rt_list_node *node = RT_NULL;
    struct rt_object_information *information;
#ifdef RT_USING_MODULE
    struct rt_dlmodule *module = dlmodule_self();
#endif

    /* get object information */
    information = rt_object_get_information(type);
    RT_ASSERT(information != RT_NULL);

    /* check object type to avoid re-initialization */

    /* enter critical */
    rt_enter_critical();
    /* try to find object */
    for (node  = information->object_list.next;
            node != &(information->object_list);
            node  = node->next)
    {
        struct rt_object *obj;

        obj = rt_list_entry(node, struct rt_object, list);
        if (obj) /* skip warning when disable debug */
        {
            RT_ASSERT(obj != object);
        }
    }
    /* leave critical */
    rt_exit_critical();

    /* initialize object's parameters */
    /* set object type to static */
    object->type = type | RT_Object_Class_Static;
    /* copy name */
    rt_strncpy(object->name, name, RT_NAME_MAX);

    RT_OBJECT_HOOK_CALL(rt_object_attach_hook, (object));

    /* lock interrupt */
    temp = rt_hw_interrupt_disable();

#ifdef RT_USING_MODULE
    if (module)
    {
        rt_list_insert_after(&(module->object_list), &(object->list));
        object->module_id = (void *)module;
    }
    else
#endif
    {
        /* insert object into information object list */
        rt_list_insert_after(&(information->object_list), &(object->list));
    }

    /* unlock interrupt */
    rt_hw_interrupt_enable(temp);
}

在這裏插入圖片描述

4.1.6 線程結構

在這裏插入圖片描述
線程狀態切換:
在這裏插入圖片描述
RT-Thread 中,實際上線程並不存在運行狀態,就緒狀態和運行狀態是等同的。

4.1.7 初始化線程

在線程初始化之後,線程通過自身的 list 節點將自身掛到容器的對象列表中,對象初始化函數在線程初始化函數裏面被調用。

rt_err_t rt_thread_init(struct rt_thread *thread,
                        const char       *name,
                        void (*entry)(void *parameter),
                        void             *parameter,
                        void             *stack_start,
                        rt_uint32_t       stack_size,
                        rt_uint8_t        priority,
                        rt_uint32_t       tick)
{
    /* thread check */
    RT_ASSERT(thread != RT_NULL);
    RT_ASSERT(stack_start != RT_NULL);

    /* initialize thread object */
    rt_object_init((rt_object_t)thread, RT_Object_Class_Thread, name);

    return _rt_thread_init(thread,
                           name,
                           entry,
                           parameter,
                           stack_start,
                           stack_size,
                           priority,
                           tick);
}

在這裏插入圖片描述

4.1.8 初始化線程棧

在動態創建線程和初始化線程的時候,會使用到內部的線程初始化函數_rt_thread_init(),_rt_thread_init() 函數會調用棧初始化函數 rt_hw_stack_init(),在棧初始化函數裏會手動構造一個上下文內容,這個上下文內容將被作爲每個線程第一次執行的初始值。上下文在棧裏的排布如下圖所示:
在這裏插入圖片描述

rt_uint8_t *rt_hw_stack_init(void       *tentry,
                             void       *parameter,
                             rt_uint8_t *stack_addr,
                             void       *texit)
{
    struct stack_frame *stack_frame;
    rt_uint8_t         *stk;
    unsigned long       i;

    /* 對傳入的棧指針做對齊處理 */
    stk  = stack_addr + sizeof(rt_uint32_t);
    stk  = (rt_uint8_t *)RT_ALIGN_DOWN((rt_uint32_t)stk, 8);
    stk -= sizeof(struct stack_frame);

    /* 得到上下文的棧幀的指針 */
    stack_frame = (struct stack_frame *)stk;

    /* 把所有寄存器的默認值設置爲 0xdeadbeef */
    for (i = 0; i < sizeof(struct stack_frame) / sizeof(rt_uint32_t); i ++)
    {
        ((rt_uint32_t *)stack_frame)[i] = 0xdeadbeef;
    }

    /* 根據 ARM  APCS 調用標準,將第一個參數保存在 r0 寄存器 */
    stack_frame->exception_stack_frame.r0  = (unsigned long)parameter;
    /* 將剩下的參數寄存器都設置爲 0 */
    stack_frame->exception_stack_frame.r1  = 0;                 /* r1 寄存器 */
    stack_frame->exception_stack_frame.r2  = 0;                 /* r2 寄存器 */
    stack_frame->exception_stack_frame.r3  = 0;                 /* r3 寄存器 */
    /* 將 IP(Intra-Procedure-call scratch register.) 設置爲 0 */
    stack_frame->exception_stack_frame.r12 = 0;                 /* r12 寄存器 */
    /* 將線程退出函數的地址保存在 lr 寄存器 */
    stack_frame->exception_stack_frame.lr  = (unsigned long)texit;
    /* 將線程入口函數的地址保存在 pc 寄存器 */
    stack_frame->exception_stack_frame.pc  = (unsigned long)tentry;
    /* 設置 psr 的值爲 0x01000000L,表示默認切換過去是 Thumb 模式 */
    stack_frame->exception_stack_frame.psr = 0x01000000L;

    /* 返回當前線程的棧地址       */
    return stk;
}

擴展閱讀:
RT-Thread代碼啓動過程與線程切換的實現
RT-Thread學習筆記之設備框架
RT-Thread學習筆記之FinSH組件
RT-Thread學習筆記之網絡框架

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章