進互聯網公司操做系統和網絡庫是基礎技能,面試過不去的看,這裏基於嵌入式操做系統分幾章來總結一下任務調度、內存分配和網絡協議棧的基礎原理和代碼實現。linux
處理器上電時會產生一個復位中斷,接下來會執行復位中斷服務函數,這纔是軟件執行的起始點。復位函數前後調用SystemInit和__main函數,SystemInit是處理器自帶的庫函數,通常執行各類時鐘和外設的初始化;__main函數執行C語言運行環境的初始化,包括將目標程序從flash搬運到RAM、初始化全局變量等內存段初值,初始化C語言庫函數等操做,最後跳轉到main函數,執行用戶程序。不一樣型號的處理器啓動流程不太一致,若是不是須要設計bootloader,能夠不必關心。面試
mian的主要工做大體爲:內存初始化、硬件中斷初始化,此外還會分配基礎的資源如鎖、信號量等,最後建立idletask。idletask任務優先級最低,裏面通常循環執行WFI指令使芯片保持低功耗狀態。算法
任務相關結構初始化很簡單,主要是分配任務塊空間,並掛接在freelist鏈表上。後續分配任務時,從freelist鏈表上取就行。編程
接下來看看任務建立的步驟:數組
首先將須要回收的任務資源掛在freetasklist上去。這裏是儘可能延遲了任務的回收時間,不是在任務該回收的時候而是在新任務建立的時候回收舊任務。這樣能夠儘可能減小CPU佔用。因此每次新建任務,都是從freetasklist鏈表上取一個TCB下來而後根據用戶需求分配棧大小,設置任務優先級和入口函數等。這裏要注意操做freetasklist的過程當中要關閉中斷,防止操做過程當中來了中斷引發任務調度,致使crash。這裏寫代碼須要注意由於參數檢查、分配內存等流程都有可能出錯,在設計程序的時候最好統一收口,全部錯誤有一個統一的出口處理,這樣能夠防止遺忘開中斷等重要操做。大體流程以下:微信
從freelist頭部摘取一個任務塊,並分配任務棧空間、設置優先級等,新建立的任務須要掛接到priqueue鏈表數組中等待任務調度:網絡
這裏有個小知識點,通常的鏈表指向的都是結構體的首部,這樣能夠將鏈表指針直接強制轉換成對應的數據結構(這裏是任務塊)。而在任務隊列中鏈表位於任務塊結構體中間,須要用宏來獲取到鏈表指針對應的任務塊首地址,這個宏的實現大同小異,各個操做系統都是借鑑Linux來實現的,詳情百度:list_entry。數據結構
接下來就要說說操做系統是怎麼作到常數級的任務切換時間的。初學者寫的操做系統任務調度功能時可能會出現一種場景,那就是任務量少的時候任務切換時間快,任務量多的時候任務切換時間變慢,這種任務切換耗時不肯定對用戶任務影響時很是大的。咱們來看看ucos系統是如何設計來保證常數級任務切換時間的。函數
一個全局的priqueue鏈表數組,上面掛接ready狀態的任務隊列,一個全局int32數,用於標記某優先級是否有須要調度的隊列。要擴展到128位優先級也很是方便,設置4個int32數組便可。每次取優先級最高的任務,直接用CLZ彙編命令從bitmap中讀出須要調度的最高優先級任務。職業規劃
插入ready任務的時候須要將bitmap對應位置1,任務從ready狀態移除的時候須要檢查該優先級是否還有須要調度的任務,若是沒有了,bitmap對應位須要置0。講完嵌入式操做系統的進程調度,再來看看Linux的CFS的基礎原理,就好理解多了,嵌入式系統低優先級隊列可能會存在餓死現象,Linux的CFS調度算法給每一個優先級分配了不一樣權重,根據就緒隊列裏全部任務的權重之和來分配任務的時間,來保證徹底公平調度。
內存分配
前面分配任務塊、分配任務棧等都用到了內存分配動做,具體的內存分配算法有:best-fit算法、TLSF算法、LWIP中的最快匹配算法、夥伴算法等,基礎原理相似,下期再分析。
時鐘中斷
適配操做系統首要任務就是把時鐘tick給起起來,這是芯片的心跳,全部的任務切換和延時操做都是基於時鐘tick中斷驅動的。
以ARM芯片的Cortex-M3核爲例,啓動時鐘中斷主要是調用osSetVector將tick回調函數設置進中斷向量表裏面的15號中斷:
中斷向量表長這樣子:
前面15箇中斷號屬於系統中斷,後面預留中斷號可供用戶配置,M3是240個,M0是32個。
結合PendSV中斷,能夠在tick中斷中完成別的事物(如定時器處理等),經過低優先級的PendSV中斷來執行任務切換動做,從而減小中斷響應時間。具體的分析在以前文章中:嵌入式操做系統的任務調度
在設置tick中斷的時候還須要配置systick定時器的中斷間隔。systick定時器是個簡單的向下計數的24位計數器,寄存器位置位於系統控制空間基址SysTick_BASE偏移16字節,將須要的頻率數值寫入便可。systick寄存器的內部細節爲:
詳情可參考《ARM Cortex-M3 Cortex-M4權威指南》9.5節systick相關部分,其他相關linux中斷知識可參考:Linux中斷編程
順帶說一下,若是職業規劃是往互聯網方向轉,那麼寄存器硬件知識瞭解便可。若是想在物聯網嵌入式領域深耕,不一樣ARM芯片之間的區別是必定要掌握的。
咱們能夠給tick中斷配置爲每10ms中斷一次,防止過多的任務上下文切換佔用CPU資源。中斷到來後系統都要處理哪些事務呢?一般狀況下會維護一個全局計數器,該中斷到來時變量自增,而後會處理定時器任務和超時任務。
超時任務是什麼呢?好比用戶任務等待某個資源(鎖、信號量等),若是獲取不到就會設置超時時間阻塞等待,直到資源可用或者任務超時。所以每次時鐘中斷到來都須要判斷是否有任務超時。
定時器任務就比較簡單了,可使用全局鏈表有序掛接定時任務,每次只須要判斷鏈表頭的任務是否到時,到時了摘取下來執行對應的回調函數便可。
不一樣的操做系統可能會在tick中斷裏面作一些別的事情,好比定時器對齊等。
貌似還有不少沒寫完,下週末再來總結。最後推薦嵌入式操做系統入門的三本書:《嵌入式操做系統內核調度--底層開發者手冊》、《嵌入式網絡那些事--STM32物聯實戰》、《ARM Cortex-M3 Cortex-M4權威指南》
本文分享自微信公衆號 - 機械猿(on_ourway)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。