1、內(nèi)核簡介
內(nèi)核是操作系統(tǒng)的核心,是操作系統(tǒng)最基礎(chǔ)也是最重要的部分,主要負(fù)責(zé)系統(tǒng)的線程、線程間通信、系統(tǒng)時(shí)鐘、中斷以及內(nèi)存等。其架構(gòu)圖如下:
2、線程調(diào)度
線程是RT-Thread操作系統(tǒng)中最小的調(diào)度單位
,線程調(diào)度算法的基于優(yōu)先級的全搶占式多線程調(diào)度算法
,即在系統(tǒng)中除了中斷處理函數(shù)、調(diào)度器上鎖部分的代碼和禁止中斷的代碼外是不可搶占的,其他都可以搶占,包括線程調(diào)度器自身。線程調(diào)度算法支持256個(gè)線程優(yōu)先級,0
優(yōu)先級代表最高優(yōu)先級,最低優(yōu)先級留給空閑線程使用;對于相同優(yōu)先級的線程,采用時(shí)間片的輪轉(zhuǎn)調(diào)度算法進(jìn)行調(diào)度,使每個(gè)線程運(yùn)行相應(yīng)時(shí)間;調(diào)度器在尋找那些處于就緒態(tài)的具有最高優(yōu)先級的線程時(shí),所經(jīng)歷的時(shí)間是恒定的,系統(tǒng)不限制線程數(shù)量的多少,線程數(shù)目只和硬件平臺的具體內(nèi)存相關(guān),這個(gè)具體的內(nèi)存要去掉實(shí)際程序中已經(jīng)用掉的內(nèi)存,實(shí)際的代碼會用掉一部分內(nèi)存,因此不能只看硬件的參數(shù),還需要考慮代碼對內(nèi)存的占用情況。
3、 時(shí)鐘管理
RT-Thread的時(shí)鐘管理是以時(shí)鐘節(jié)拍為基礎(chǔ)的,時(shí)鐘節(jié)拍的RT-Thread中最小的時(shí)鐘單位。RT-Thread提供兩種定時(shí)器的機(jī)制:一是單次觸發(fā)定時(shí)器
,此定時(shí)器在啟動之后只會觸發(fā)一次定時(shí)器事件,然后定時(shí)器自動停止;二是周期觸發(fā)定時(shí)器
,這類定時(shí)器會周期性的觸發(fā)定時(shí)器事件,直到用戶手動的停止定時(shí)器,否則將永遠(yuǎn)持續(xù)執(zhí)行下去。
根據(jù)超時(shí)函數(shù)執(zhí)行時(shí)的上下文,RT-Thread的定時(shí)器可以設(shè)置為HARD_TIMER模式和SOFT_TIMER模式。
4、線程間同步
RT-Thread采用信號量
、互斥量
與事件
實(shí)現(xiàn)線程間同步。線程通過對信號量、互斥量的獲取與釋放進(jìn)行同步;互斥量采用優(yōu)先級繼承的方式解決了實(shí)時(shí)系統(tǒng)常見的優(yōu)先級反轉(zhuǎn)問題。線程同步機(jī)制支持線程按優(yōu)先級等待方式獲取信號量或互斥量。線程通過對事件的發(fā)送與接受進(jìn)行同步;事件集支持多事件的“或觸發(fā)”和“與觸發(fā)”,適合于線程等待多個(gè)事件的情況。
5、線程間通信
RT-Thread 支持郵箱
和消息隊(duì)列
等通信機(jī)制。郵箱中一封郵件的長度固定為 4 字節(jié)大小;消息隊(duì)列能夠接收不固定長度的消息,并把消息緩存在自己的內(nèi)存空間中。郵箱效率較消息隊(duì)列更為高效。郵箱和消息隊(duì)列的發(fā)送動作可安全用于中斷服務(wù)例程中。通信機(jī)制支持線程按優(yōu)先級等待方式獲取。
6、內(nèi)存管理
RT-Thread支持靜態(tài)內(nèi)存池管理
和動態(tài)內(nèi)存堆管理
。當(dāng)靜態(tài)內(nèi)存池具有可用內(nèi)存時(shí),系統(tǒng)對內(nèi)存塊分配的時(shí)間是恒定的;當(dāng)靜態(tài)內(nèi)存池為空時(shí),系統(tǒng)將申請內(nèi)存塊的線程掛起或者阻塞掉(線程等待一段時(shí)間后仍未獲得內(nèi)存塊就放棄申請并返回,或者立刻返回。等待的時(shí)間取決去申請內(nèi)存塊時(shí)設(shè)置的等待時(shí)間參數(shù)),當(dāng)其他線程釋放內(nèi)存塊到內(nèi)存池時(shí),如有掛起的待分配的線程存在的話,則系統(tǒng)會將這個(gè)線程喚醒。
動態(tài)內(nèi)存堆管理模塊在系統(tǒng)資源不同的情況下,分別提供了面向小內(nèi)存管理算法及面向大內(nèi)存系統(tǒng)的SLAB內(nèi)存管理算法。
另一種動態(tài)內(nèi)存堆管理叫做memheap
,適用于系統(tǒng)含有多個(gè)地址且不連續(xù)的內(nèi)存堆。使用memheap可以將多個(gè)內(nèi)存堆“粘貼”在一起,讓用戶操作起來像是在操作一個(gè)內(nèi)存堆。
7、I/O設(shè)備管理
RT-Thread 將 PIN、I2C、SPI、USB、UART 等作為外設(shè)設(shè)備,統(tǒng)一通過設(shè)備注冊完成。實(shí)現(xiàn)了按名稱訪問的設(shè)備管理子系統(tǒng),可按照統(tǒng)一的 API 界面訪問硬件設(shè)備。在設(shè)備驅(qū)動接口上,根據(jù)嵌入式系統(tǒng)的特點(diǎn),對不同的設(shè)備可以掛接相應(yīng)的事件。當(dāng)設(shè)備事件觸發(fā)時(shí),由驅(qū)動程序通知給上層的應(yīng)用程序。
8、RT-Thread啟動流程
RT-Thread 支持多種平臺和多種編譯器,而 rtthread_startup() 函數(shù)是 RT-Thread 規(guī)定的統(tǒng)一啟動入口。一般執(zhí)行順序是:系統(tǒng)先從啟動文件開始運(yùn)行,然后進(jìn)入 RT-Thread 的啟動函數(shù) rtthread_startup() ,最后進(jìn)入用戶入口函數(shù) main(),如下圖所示:rtthread_startup()
函數(shù),其中 rtthread_startup()
函數(shù)的代碼如下所示:
int rtthread_startup(void)
{
rt_hw_interrupt_disable();
/* 板級初始化:需在該函數(shù)內(nèi)部進(jìn)行系統(tǒng)堆的初始化 */
rt_hw_board_init();
/* 打印 RT-Thread 版本信息 */
rt_show_version();
/* 定時(shí)器初始化 */
rt_system_timer_init();
/* 調(diào)度器初始化 */
rt_system_scheduler_init();
#ifdef RT_USING_SIGNALS
/* 信號初始化 */
rt_system_signal_init();
#endif
/* 由此創(chuàng)建一個(gè)用戶 main 線程 */
rt_application_init();
/* 定時(shí)器線程初始化 */
rt_system_timer_thread_init();
/* 空閑線程初始化 */
rt_thread_idle_init();
/* 啟動調(diào)度器 */
rt_system_scheduler_start();
/* 不會執(zhí)行至此 */
return 0;
}
這部分啟動代碼,大致可以分為四個(gè)部分:
(1)初始化與系統(tǒng)相關(guān)的硬件;
(2)初始化系統(tǒng)內(nèi)核對象,例如定時(shí)器、調(diào)度器、信號;
(3)創(chuàng)建 main 線程,在 main 線程中對各類模塊依次進(jìn)行初始化;
(4)初始化定時(shí)器線程、空閑線程,并啟動調(diào)度器。
啟動調(diào)度器之前,系統(tǒng)所創(chuàng)建的線程在執(zhí)行 rt_thread_startup()
后并不會立馬運(yùn)行,它們會處于就緒狀態(tài)等待系統(tǒng)調(diào)度;待啟動調(diào)度器之后,系統(tǒng)才轉(zhuǎn)入第一個(gè)線程開始運(yùn)行,根據(jù)調(diào)度規(guī)則,選擇的是就緒隊(duì)列中優(yōu)先級最高的線程。rt_hw_board_init()
中完成系統(tǒng)時(shí)鐘設(shè)置,為系統(tǒng)提供心跳、串口初始化,將系統(tǒng)輸入輸出終端綁定到這個(gè)串口,后續(xù)系統(tǒng)運(yùn)行信息就會從串口打印出來。
9、RT-Thread程序內(nèi)存分布
一般MCU包含的存儲空間有:片內(nèi)Flash與片內(nèi)RAM,RAM相當(dāng)于內(nèi)存,F(xiàn)lash相當(dāng)于硬盤。編譯器會將程序分為幾個(gè)部分,分別存儲在MCU不同的位置。
Keil 工程在編譯完之后,會有相應(yīng)的程序所占用的空間提示信息,如下所示:
linking...
Program Size: Code=54872 RO-data=8656 RW-data=764 ZI-data=21812
After Build - User command #1: fromelf --bin .\build\rtthread-stm32.axf --output rtthread.bin
".\build\rtthread-stm32.axf" - 0 Error(s), 0 Warning(s).
Build Time Elapsed: 00:00:14
上面提到的 Program Size 包含以下幾個(gè)部分:
1)Code:代碼段,存放程序的代碼部分;
2)RO-data:只讀數(shù)據(jù)段,存放程序中定義的常量;
3)RW-data:讀寫數(shù)據(jù)段,存放初始化為非 0 值的全局變量;
4)ZI-data:0 數(shù)據(jù)段,存放未初始化的全局變量及初始化為 0 的變量;
編譯完工程會生成一個(gè).map 的文件,該文件說明了各個(gè)函數(shù)占用的尺寸和地址,在文件的最后幾行也說明了上面幾個(gè)字段的關(guān)系:文章來源:http://www.zghlxwxcb.cn/news/detail-727341.html
==============================================================================
Total RO Size (Code + RO Data) 63528 ( 62.04kB)
Total RW Size (RW Data + ZI Data) 22576 ( 22.05kB)
Total ROM Size (Code + RO Data + RW Data) 63676 ( 62.18kB)
==============================================================================
1)RO Size 包含了 Code 及 RO-data,表示程序占用 Flash 空間的大小;
2)RW Size 包含了 RW-data 及 ZI-data,表示運(yùn)行時(shí)占用的 RAM 的大??;
3)ROM Size 包含了 Code、RO-data 以及 RW-data,表示燒寫程序所占用的 Flash 空間的大??;
程序運(yùn)行之前,需要有文件實(shí)體被燒錄到 STM32 的 Flash 中,一般是 bin 或者 hex 文件,該被燒錄文件稱為可執(zhí)行映像文件。如下圖左邊部分所示,是可執(zhí)行映像文件燒錄到 STM32 后的內(nèi)存分布,它包含 RO 段和 RW 段兩個(gè)部分:其中 RO 段中保存了 Code、RO-data 的數(shù)據(jù),RW 段保存了 RW-data 的數(shù)據(jù),由于 ZI-data 都是 0,所以未包含在映像文件中。
STM32 在上電啟動之后默認(rèn)從 Flash 啟動,啟動之后會將 RW 段中的 RW-data(初始化的全局變量)搬運(yùn)到 RAM 中,但不會搬運(yùn) RO 段,即 CPU 的執(zhí)行代碼從 Flash 中讀取,另外根據(jù)編譯器給出的 ZI 地址和大小分配出 ZI 段,并將這塊 RAM 區(qū)域清零。
其中動態(tài)內(nèi)存堆為未使用的 RAM 空間,應(yīng)用程序申請和釋放的內(nèi)存塊都來自該空間。而一些全局變量則是存放于 RW 段和 ZI 段中,RW 段存放的是具有初始值的全局變量(而常量形式的全局變量則放置在 RO 段中,是只讀屬性的),ZI 段存放的系統(tǒng)未初始化的全局變量。文章來源地址http://www.zghlxwxcb.cn/news/detail-727341.html
到了這里,關(guān)于RT-Thread內(nèi)核——內(nèi)核基礎(chǔ)(上)的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!