摘要:本文會給讀者介紹鴻蒙輕內核M核源碼中重要的數據結構,任務基於優先級的就緒隊列Priority Queue。
本文分享自華爲雲社區《鴻蒙輕內核M核源碼分析系列三 數據結構-任務就緒隊列》,原文做者:zhushy 。git
本文會給讀者介紹鴻蒙輕內核M核源碼中重要的數據結構,任務基於優先級的就緒隊列Priority Queue。segmentfault
在講解時,會結合數據結構相關繪圖,培養讀者們的數據結構的平面想象能力,幫助更好的學習和理解這些數據結構的用法。本文中所涉及的源碼,以OpenHarmony LiteOS-M內核爲例,都可以在開源站點https://gitee.com/openharmony... 獲取。數組
在任務調度模塊,就緒隊列是個重要的數據結構。任務建立後即進入就緒態,並放入就緒隊列。在鴻蒙輕內核中,就緒隊列是一個雙向循環鏈表數組,每一個數組元素就是一個鏈表,相同優先級的任務放入同一個鏈表。數據結構
任務就緒隊列Priority Queue主要供內部使用,用戶進行業務開發時不涉及,因此並未對外提供接口。雙向循環鏈表數組可以更加方便的支持任務基於優先級進行調度。任務就緒隊列的核心代碼在kernel\src\los_task.c文件中。函數
在kernel\src\los_task.c文件中定義了和任務就緒隊列相關的主要變量。源碼分析
源碼以下:學習
⑴ LITE_OS_SEC_BSS LOS_DL_LIST *g_losPriorityQueueList = NULL; ⑵ static LITE_OS_SEC_BSS UINT32 g_priqueueBitmap = 0; ⑶ #define PRIQUEUE_PRIOR0_BIT (UINT32)0x80000000 ⑷ #define OS_PRIORITY_QUEUE_PRIORITYNUM 32
其中⑴表示任務就緒隊列,是一個雙向鏈表數組,後文初始化該數組時會將數組長度設置爲⑷處定義的OS_PRIORITY_QUEUE_PRIORITYNUM;⑵表示優先級位圖,標識了任務就緒隊列中已掛載的就緒任務所在的優先級;⑶表示優先級爲0的比特位;⑷表示任務就緒隊列支持的優先級個數32,因此鴻蒙輕內核優先級的取值範圍爲0-31,數值越小優先級越大。spa
優先級位圖g_priqueueBitmap的bit位和優先級的關係爲bit=31-priority,優先級數組g_losPriorityQueueList[priority]包含了OS_PRIORITY_QUEUE_PRIORITYNUM個數組元素,每一個數組元素都是一個雙向鏈表,同一優先級的處於就緒狀態的全部任務都會掛載到對應優先級的雙向鏈表中。code
示意圖以下:
blog
任務就緒隊列初始化函數爲OsPriQueueInit(),系統初始化階段被調用,調用路徑爲:main.c:main() --> kernel\src\los_init.c:LOS_KernelInit() --> kernel\src\los_task.c:OsTaskInit() --> OsPriqueueInit()。
源碼以下:
STATIC UINT32 OsPriqueueInit(VOID) { UINT32 priority; ⑴ UINT32 size = OS_PRIORITY_QUEUE_PRIORITYNUM * sizeof(LOS_DL_LIST); g_losPriorityQueueList = (LOS_DL_LIST *)LOS_MemAlloc(m_aucSysMem0, size); if (g_losPriorityQueueList == NULL) { return LOS_NOK; } for (priority = 0; priority < OS_PRIORITY_QUEUE_PRIORITYNUM; ++priority) { ⑵ LOS_ListInit(&g_losPriorityQueueList[priority]); } retu
rn LOS_OK;
}
⑴處計算就緒隊列數組須要的內存大小,而後爲任務就緒隊列申請內存,佔用內存爲OS_PRIORITY_QUEUE_PRIORITYNUM個雙向鏈表所須要的內存大小,運行期間該內存不會釋放,爲系統常駐內存。⑵處代碼將每個數組元素都初始化爲雙向循環鏈表。
任務就緒隊列插入函數爲OsPriqueueEnqueue(),該函數把就緒狀態的任務插入任務就緒隊列的尾部。在任務就緒隊列中,先調用隊列頭部的任務,最後調用隊列尾部的任務。
源碼以下:
STATIC VOID OsPriqueueEnqueue(LOS_DL_LIST *priqueueItem, UINT32 priority) { ⑴ if (LOS_ListEmpty(&g_losPriorityQueueList[priority])) { ⑵ g_priqueueBitmap |= (PRIQUEUE_PRIOR0_BIT >> priority); } ⑶ LOS_ListTailInsert(&g_losPriorityQueueList[priority], priqueueItem); }
⑴處先判斷指定優先級priority的任務就緒隊列是否爲空,若是爲空,則在⑵處更新優先級位圖,將第31-prioritybit位設置爲1。⑶處把就緒狀態的任務插入任務就緒隊列的尾部,進行排隊。
從任務就緒隊列中刪除的函數爲OsPriqueueDequeue()。任務被刪除、進入suspend阻塞狀態、優先級調整等場景中,都須要調用該函數把任務從任務就緒隊列中刪除。
源碼以下:
STATIC VOID OsPriqueueDequeue(LOS_DL_LIST *priqueueItem) { LosTaskCB *runningTask = NULL; ⑴ LOS_ListDelete(priqueueItem); ⑵ runningTask = LOS_DL_LIST_ENTRY(priqueueItem, LosTaskCB, pendList); ⑶ if (LOS_ListEmpty(&g_losPriorityQueueList[runningTask->priority])) { ⑷ g_priqueueBitmap &= ~(PRIQUEUE_PRIOR0_BIT >> runningTask->priority); } }
⑴把任務從任務就緒隊列中刪除。⑵獲取被刪除任務的任務控制塊信息,以獲取任務的優先級。刪除完任務後隊列可能成爲空隊列,因此⑶處代碼判斷任務就緒隊列是否爲空,若是爲空,則須要執行⑷處代碼,更新優先級位圖,將第31-prioritybit位設置爲0。
獲取任務就緒隊列中優先級最高的鏈表節點的函數爲OsPriQueueTop()。
源碼以下:
STATIC LOS_DL_LIST *OsPriqueueTop(VOID) { UINT32 priority; ⑴ if (g_priqueueBitmap != 0) { ⑵ priority = CLZ(g_priqueueBitmap); ⑶ return LOS_DL_LIST_FIRST(&g_losPriorityQueueList[priority]); } return (LOS_DL_LIST *)NULL; }
⑴處判斷優先級位圖g_priqueueBitmap是否爲0,若是爲0則直接返回NULL,說明任務就緒隊列中沒有任何就緒狀態的任務。 ⑵處計算g_priqueueBitmap以二進制表示時高位爲0的位數,其值就是任務的優先級priority,以此方法獲得的優先級就是任務就緒隊列中全部優先級裏最高的。而後⑶處從該優先級的隊列&g_losPriorityQueueList[priority]中獲取第一個鏈表節點,獲取的就是任務就緒隊列中優先級最高的任務。
獲取任務就緒隊列中指定優先級的任務數量的函數爲OsPriqueueSize()。
源碼以下:
STATIC UINT32 OsPriqueueSize(UINT32 priority) { UINT32 itemCnt = 0; LOS_DL_LIST *curPQNode = (LOS_DL_LIST *)NULL; ⑴ LOS_DL_LIST_FOR_EACH(curPQNode, &g_losPriorityQueueList[priority]) { ⑵ ++itemCnt; } return itemCnt; }
⑴處代碼使用宏LOS_DL_LIST_FOR_EACH定義的for循環遍歷指定優先級priority的雙向鏈表,若是獲取到新節點則表示該優先級下有一個就緒任務,而後執行⑵處代碼,對計數進行加1操做,返回的結果就是指定優先級下有多少個就緒任務。
掌握鴻蒙輕內核的優先級就緒隊列Priority Queue這一重要的數據結構,會給進一步學習、分析鴻蒙輕內核源代碼打下了基礎,讓後續的學習更加容易。後續也會陸續推出更多的分享文章,敬請期待,也歡迎你們分享學習、使用鴻蒙輕內核的心得,有任何問題、建議,均可以留言給咱們: https://gitee.com/openharmony... 。爲了更容易找到鴻蒙輕內核代碼倉,建議訪問 https://gitee.com/openharmony... ,關注Watch、點贊Star、並Fork到本身帳戶下,謝謝。