1. 程式人生 > >RTthread學習筆記————第3章 核心基礎

RTthread學習筆記————第3章 核心基礎

  • 核心基礎 

核心是作業系統最基礎也是最重要的部分。圖 3-1 為 RT-Thread 核心架構圖,核心處於硬體層
之上,核心部分包括核心庫、實時核心實現。

  • RT-Thread 核心及底層結構 

核心庫是為了保證核心能夠獨立執行的一套小型的類似 C 庫 [1] 的函式實現子集。這部分根據編
譯器自帶 C 庫的情況會有些不同,當使用 GNU GCC 編譯器時,會攜帶更多的標準 C 庫實現。

實時核心的實現包括:物件管理、執行緒管理及排程器、執行緒間通訊管理、時鐘管理及記憶體管理
等等,核心最小的資源佔用情況是 3KB ROM,1.2KB RAM。

 注: 

C 庫:也叫 C 執行庫(C Runtime Library),它提供了類似“strcpy”、“memcpy”等函式,
有些也會包括“printf”、“scanf”函式的實現。RT-Thread Kernel Service Library 僅提供核心用到
的一小部分 C 庫函式實現,為了避免與標準 C 庫重名,在這些函式前都會新增上 rt_字首。

  • RTthread啟動流程

一般瞭解一份程式碼大多從啟動部分開始,同樣這裡也採用這種方式,先尋找啟動的源頭,因為
MDK-ARM 的使用者程式入口為 main()函式,位於 main.c 檔案中。系統啟動後先從彙編程式碼
startup_stm32f103xe.s

開始執行,然後跳轉到 C 程式碼,進行 RT-Thread 系統功能初始化,最後進入
使用者程式入口 main()。
為了在進入main()之前完成RT-Thread系統功能初始化,我們使用了MDK的擴充套件功能$Sub$$
$Super$$。可以給 main 新增$Sub$$的字首符號作為一個新功能函式$Sub$$main,這個$Sub$$main
可以先呼叫一些要補充在 main 之前的功能函式(這裡新增 RT-Thread 系統初始化功能),再呼叫
$Super$$main 轉到 main()函式執行,這樣可以讓使用者不用去管 main()之前的系統初始化操作。
關於$Sub$$和$Super$$擴充套件功能的使用,詳見 ARM® Compiler v5.06 for µVision® armlink User
Guide。

在這裡$Sub$$main 函式僅僅呼叫了 rtthread_startup()函式。RT-Thread 支援多種平臺和多種編
譯器,而 rtthread_startup()函式是 RT-Thread 規定的統一入口點,所以$Sub$$main 函式只需呼叫
rtthread_startup()函式即可(例如採用 GNU GCC 編譯器編譯的 RT-Thread,就是直接從彙編啟動代
碼部分跳轉到 rtthread_startup()函式中,並開始第一個 C 程式碼的執行)。

流程圖 

  • RT-Thread 自動初始化機制

自動初始化機制是指初始化函式不需要被顯式呼叫,只需要在函式定義處通過巨集定義的方式進
行申明,就會在系統啟動過程中被執行。
例如在串列埠驅動中呼叫一個巨集定義告知系統初始化需要呼叫的函式,程式碼如下:

示例程式碼最後的 INIT_BOARD_EXPORT(rt_hw_usart_init)表示使用自動初始化功能,按照這種
方式,rt_hw_usart_init()函式就會被系統自動呼叫,那麼它是在哪裡被呼叫的呢?

流程框圖中有兩個函式:rt_components_board_init()與 rt_components_init(),其後的帶底色方
框內部的函式表示被自動初始化的函式,其中:

rt_components_board_init()函式執行的比較早,主要初始化相關硬體環境,執行這個函式時將
會遍歷通過 INIT_BOARD_EXPORT(fn) 申明的初始化函式表,並呼叫各個函式。
rt_components_init()函式會在作業系統執行起來之後建立的 main 執行緒裡被呼叫執行,這個時候
硬體環境和作業系統已經初始化完成,可以執行應用相關程式碼。rt_components_init()函式會遍歷通
過剩下的其他幾個巨集申明的初始化函式表。 

  1.  “board init functions”為所有通過 INIT_BOARD_EXPORT(fn) 申明的初始化函式。
  2.  “pre-initialization functions”為所有通過 INIT_PREV_EXPORT(fn) 申明的初始化函式。
  3.  “device init functions”為所有通過 INIT_DEVICE_EXPORT(fn) 申明的初始化函式。
  4.  “components init functions”為所有通過 INIT_COMPONENT_EXPORT(fn) 申明的初始化函式。
  5.  “enviroment init functions”為所有通過 INIT_ENV_EXPORT(fn) 申明的初始化函式。
  6.  “application init functions”為所有通過 INIT_APP_EXPORT(fn) 申明的初始化函式。

  • RT-Thread 核心物件模型

RT-Thread 核心物件是為系統物件(執行緒、訊號量、郵箱)維護的一些資料結構,這些資料構
儲存了與系統級物件相關的資訊。在 RT-Thread 核心物件中分為兩類:靜態核心物件和動態核心
物件。靜態核心物件通常放在 RW 段和 ZI 段中,在系統啟動後在程式中初始化;動態核心物件則
是從記憶體堆中建立的,而後手工做初始化。

靜態物件和動態物件示例

/* 執行緒 1 的物件和執行時用到的棧 */
static struct rt_thread thread1;
static rt_uint8_t thread1_stack[512];
/* 執行緒 1 入口 */
void thread1_entry(void* parameter)
{
    int i;
    while (1)
    {
        for (i = 0; i < 10; i ++)
        {
            rt_kprintf("%d\n", i);
            /* 延時 100 個 OS Tick */
            rt_thread_delay(100);
        }
    }
}
/* 執行緒 2 入口 */
void thread2_entry(void* parameter)
{
    int count = 0;
    while (1)
    {
        rt_kprintf("Thread2 count:%d\n", ++count);
        /* 延時 50 個 OS Tick */
        rt_thread_delay(50);
    }
}
/* 執行緒例程初始化 */
int thread_sample_init()
{
    rt_thread_t thread2_ptr;
    rt_err_t result;
    /* 初始化執行緒 1 */
    /* 執行緒的入口是 thread1_entry,引數是 RT_NULL
    * 執行緒棧是 thread1_stack
    * 優先順序是 200,時間片是 10 個 OS Tick
    */
    result = rt_thread_init(&thread1,
                    "thread1",
                    thread1_entry, RT_NULL,
                    &thread1_stack[0], sizeof(thread1_stack),
                      200, 10);
    /* 啟動執行緒 */
    if (result == RT_EOK) rt_thread_startup(&thread1);
    /* 建立執行緒 2 */
    /* 執行緒的入口是 thread2_entry, 引數是 RT_NULL
    * 棧空間是 512,優先順序是 250,時間片是 25 個 OS Tick
    */
    thread2_ptr = rt_thread_create("thread2",
    thread2_entry, RT_NULL,
            512, 250, 25);
    /* 啟動執行緒 */
    if (thread2_ptr != RT_NULL) rt_thread_startup(thread2_ptr);
    return 0;
}

在這個例子中,thread1 是一個靜態執行緒物件,而 thread2 是一個動態執行緒物件。thread1 物件的記憶體空間,包括執行緒控制塊 thread1 與棧空間 thread1_stack 都是編譯時決定的,因為程式碼中都不
存在初始值,都統一放在未初始化資料段中。thread2 執行中用到的空間都是動態分配的,包括線
程控制塊(thread2_ptr 指向的內容)和棧空間。
靜態物件會佔用 RAM 空間,不依賴於記憶體堆管理器,記憶體分配時間確定。動態物件則依賴於
記憶體堆管理器,執行時申請 RAM 空間,當物件被刪除後,佔用的 RAM 空間被釋放。這兩種方式
各有利弊,可以根據實際環境需求選擇具體使用方式。

  • 核心物件管理架構

在物件管理模組中,定義了通用的資料結構,用來儲存各種物件的共同屬性,各種具體物件只
需要在此基礎上加上自己的某些特別的屬性,就可以清楚的表示自己的特徵。
這種設計方法的優點有:
(1)提高了系統的可重用性和擴充套件性,增加新的物件類別很容易,只需要繼承通用物件的屬
性再加少量擴充套件即可。
(2)提供統一的物件操作方式,簡化了各種具體物件的操作,提高了系統的可靠性。
圖中由物件控制塊 rt_object 派生出來的有:執行緒物件、記憶體池物件、定時器物件、裝置物件
和 IPC 物件(IPC:Inter-Process Communication,程序間通訊。在 RT-Thread 實時作業系統中,IPC
物件的作用是進行執行緒間同步與通訊);由 IPC 物件派生出訊號量、互斥量、事件、郵箱與訊息
佇列、訊號等物件。

  • 物件操作塊和相關函式

/*核心物件控制塊的資料結構:*/
struct rt_object
{
	/* 核心物件名稱 */
	char name[RT_NAME_MAX];
	/* 核心物件型別 */
	rt_uint8_t type;
	/* 核心物件的引數 */
	rt_uint8_t flag;
	/* 核心物件管理連結串列 */
	rt_list_t list;
};

/*目前核心物件支援的型別*/
enum rt_object_class_type
{
	RT_Object_Class_Thread = 0, /* 物件為執行緒型別 */
	#ifdef RT_USING_SEMAPHORE
		RT_Object_Class_Semaphore, /* 物件為訊號量型別 */
	#endif
	#ifdef RT_USING_MUTEX
		RT_Object_Class_Mutex, /* 物件為互斥量型別 */
	#endif
	#ifdef RT_USING_EVENT
		RT_Object_Class_Event, /* 物件為事件型別 */
	#endif
	#ifdef RT_USING_MAILBOX
		RT_Object_Class_MailBox, /* 物件為郵箱型別 */
	#endif
	#ifdef RT_USING_MESSAGEQUEUE
		RT_Object_Class_MessageQueue, /* 物件為訊息佇列型別 */
	#endif
	#ifdef RT_USING_MEMPOOL
		RT_Object_Class_MemPool, /* 物件為記憶體池型別 */
	#endif
	#ifdef RT_USING_DEVICE
		RT_Object_Class_Device, /* 物件為裝置型別 */
	#endif
	RT_Object_Class_Timer, /* 物件為定時器型別 */
	#ifdef RT_USING_MODULE
		RT_Object_Class_Module, /* 物件為模組 */
	#endif
	RT_Object_Class_Unknown, /* 物件型別未知 */
	RT_Object_Class_Static = 0x80 /* 物件為靜態物件 */
};
/*從上面的型別說明,我們可以看出,如果是靜態物件,那麼物件型別的最高位將是 1(是 RT_
Object_Class_Static 與其他物件型別的與操作),否則就是動態物件,系統最多能夠容納的物件類
別數目是 127 個。*/


/*核心物件容器的資料結構*/
struct rt_object_information
{
	/* 物件型別 */
	enum rt_object_class_type type;
	/* 物件連結串列 */
	rt_list_t object_list;
	/* 物件大小 */
	rt_size_t object_size;
};

函式

 初始化物件(靜態)

void rt_object_init(struct rt_object* object ,
                    enum rt_object_class_type type ,
                    const char* name)

 脫離物件(靜態)

void rt_object_detach(rt_object_t object);

 分配物件(動態)

rt_object_t rt_object_allocate(enum rt_object_class_typetype ,
                                const char* name)

 刪除物件(動態)

void rt_object_delete(rt_object_t object)

辨別物件(判斷指定物件是否是系統物件(靜態核心物件))

rt_err_t rt_object_is_systemobject(rt_object_t object);

  • RT-Thread 核心配置示例

配置巨集定義主要在 rtconfig.h

  • RT-Thread小結

  1. RT-Thread 核心部分的實現包括:執行緒管理及排程器、定時器管理、執行緒間通訊管理及記憶體管理等等,是作業系統的核心。
  2. 系統配置檔案 rtconfig.h 是由配置工具自動生成的,一般情況下無需手動更改。
  3. 使用自動初始化時,需要根據實際需求,安排好各個函式的先後執行順序。

 

注:文章參考培訓教程