在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

淺談鴻蒙內核代碼調度隊列

鴻蒙系統HarmonyOS ? 來源:oschina ? 作者:鴻蒙內核發燒友 ? 2020-10-23 11:00 ? 次閱讀

為何單獨講調度隊列?

鴻蒙內核代碼中有兩個源文件是關于隊列的,一個是用于調度的隊列,另一個是用于線程間通訊的IPC隊列。

本文詳細講述調度隊列,詳見代碼: kernel_liteos_a/kernel/base/sched/sched_sq/los_priqueue.c

IPC隊列后續有專門的博文講述,這兩個隊列的數據結構實現采用的都是雙向循環鏈表,LOS_DL_LIST實在是太重要了,是理解鴻蒙內核的關鍵,說是最重要的代碼一點也不為過,源碼出現在 sched_sq模塊,說明是用于任務的調度的,sched_sq模塊只有兩個文件,另一個los_sched.c就是調度代碼。

涉及函數

功能分類 接口 描述
創建隊列 OsPriQueueInit 創建了32個就緒隊列
獲取最高優先級隊列 OsPriQueueTop 查最高優先級任務
從頭部入隊列 OsPriQueueEnqueueHead 從頭部插入某個就緒隊列
從尾部入隊列 OsPriQueueEnqueue 默認是從尾部插入某個就緒隊列
出隊列 OsPriQueueDequeue 從最高優先級的就緒隊列中刪除
OsPriQueueProcessDequeue 從進程隊列中刪除
OsPriQueueProcessSize 用進程查隊列中元素個數
OsPriQueueSize 用任務查隊列中元素個數
OsTaskPriQueueTop 查最高優先級任務
OsDequeEmptySchedMap 進程出列
OsGetTopTask 獲取被調度選擇的task

鴻蒙內核進程和線程各有32個就緒隊列,進程隊列用全局變量存放,創建進程時入隊,任務隊列放在進程的threadPriQueueList中。

映射張大爺的故事:就緒隊列就是在外面排隊的32個通道,按優先級0-31依次排好,張大爺的辦公室有個牌子,類似打籃球的記分牌,一共32個,一字排開,隊列里有人時對應的牌就是1,沒有就是0 ,這樣張大爺每次從0位開始看,看到的第一個1那就是最高優先級的那個人。辦公室里的記分牌就是位圖調度器。

位圖調度器

//*kfy 0x80000000U = 10000000000000000000000000000000(32位,1是用于移位的,設計之精妙,點贊) 
#define PRIQUEUE_PRIOR0_BIT   0x80000000U 

#ifndef CLZ
#define CLZ(value)                                  (__clz(value)) //匯編指令
#endif

LITE_OS_SEC_BSS LOS_DL_LIST *g_priQueueList = NULL; //所有的隊列 原始指針
LITE_OS_SEC_BSS UINT32 g_priQueueBitmap; // 位圖調度
// priority = CLZ(bitmap); // 獲取最高優先級任務隊列 調度位

整個los_priqueue.c就只有兩個全部變量,一個是LOS_DL_LIST *g_priQueueList是32個進程就緒隊列的頭指針,在就緒隊列中會講另一個UINT32 g_priQueueBitmap 估計很多人會陌生,是一個32位的變量,叫位圖調度器。怎么理解它呢?

鴻蒙系統的調度是搶占式的,task分成32個優先級,如何快速的知道哪個隊列是空的,哪個隊列里有任務需要一個標識,而且要極高效的實現?答案是:位圖調度器。

簡單說就是一個變量的位來標記對應隊列中是否有任務,在位圖調度下,任務優先級的值越小則代表具有越高的優先級,每當需要進行調度時,從最低位向最高位查找出第一個置 1 的位的所在位置,即為當前最高優先級,然后從對應優先級就緒隊列獲得相應的任務控制塊,整個調度器的實現復雜度是 O(1),即無論任務多少,其調度時間是固定的。

進程就緒隊列機制

CPU執行速度是很快的,其運算速度和內存的讀寫速度是數量級的差異,與硬盤的讀寫更是指數級。鴻蒙內核默認一個時間片是 10ms,資源很寶貴,它不斷在眾多任務中來回的切換,所以絕不能讓CPU等待任務,CPU時間很寶貴,沒準備好的任務不要放進來。這就是進程和線程就緒隊列的機制,一共有32個任務就緒隊列,因為線程的優先級是默認32個, 每個隊列中放同等優先級的task.

隊列初始化做了哪些工作?詳細看代碼

#define OS_PRIORITY_QUEUE_NUM 32

UINT32 OsPriQueueInit(VOID)
{
    UINT32 priority;

    /* system resident resource */
    g_priQueueList = (LOS_DL_LIST *)LOS_MemAlloc(m_aucSysMem0, (OS_PRIORITY_QUEUE_NUM * sizeof(LOS_DL_LIST)));
    if (g_priQueueList == NULL) {
        return LOS_NOK;
    }

    for (priority = 0; priority < OS_PRIORITY_QUEUE_NUM; ++priority) {
        LOS_ListInit(&g_priQueueList[priority]);
    }
    return LOS_OK;
}

因TASK 有32個優先級,在初始化時內核一次性創建了32個雙向循環鏈表,每種優先級都有一個隊列來記錄就緒狀態的tasks的位置,g_priQueueList分配的是一個連續的內存塊,存放了32個LOS_DL_LIST,再看一下LOS_DL_LIST結構體,因為它太重要了!越簡單越靈活

typedef struct LOS_DL_LIST {
    struct LOS_DL_LIST *pstPrev; /**< Current node's pointer to the previous node */
    struct LOS_DL_LIST *pstNext; /**< Current node's pointer to the next node */
} LOS_DL_LIST;

幾個常用函數

還是看入隊和出隊的源碼吧,注意bitmap的變化!

從代碼中可以知道,調用了LOS_ListTailInsert(&priQueueList[priority], priqueueItem); 注意是從循環鏈表的尾部插入的,也就是同等優先級的TASK被排在了最后一個執行,只要每次都是從尾部插入,就形成了一個按順序執行的隊列。鴻蒙內核的設計可謂非常巧妙,用極少的代碼,極高的效率實現了隊列功能。

VOID OsPriQueueEnqueue(LOS_DL_LIST *priQueueList, UINT32 *bitMap, LOS_DL_LIST *priqueueItem, UINT32 priority)
{
    /*
     * Task control blocks are inited as zero. And when task is deleted,
     * and at the same time would be deleted from priority queue or
     * other lists, task pend node will restored as zero.
     */
    LOS_ASSERT(priqueueItem->pstNext == NULL);

    if (LOS_ListEmpty(&priQueueList[priority])) {
        *bitMap |= PRIQUEUE_PRIOR0_BIT >> priority;//對應優先級位 置1
    }

    LOS_ListTailInsert(&priQueueList[priority], priqueueItem);
}

VOID OsPriQueueEnqueueHead(LOS_DL_LIST *priQueueList, UINT32 *bitMap, LOS_DL_LIST *priqueueItem, UINT32 priority)
{
    /*
     * Task control blocks are inited as zero. And when task is deleted,
     * and at the same time would be deleted from priority queue or
     * other lists, task pend node will restored as zero.
     */
    LOS_ASSERT(priqueueItem->pstNext == NULL);

    if (LOS_ListEmpty(&priQueueList[priority])) {
        *bitMap |= PRIQUEUE_PRIOR0_BIT >> priority;//對應優先級位 置1
    }

    LOS_ListHeadInsert(&priQueueList[priority], priqueueItem);
}

VOID OsPriQueueDequeue(LOS_DL_LIST *priQueueList, UINT32 *bitMap, LOS_DL_LIST *priqueueItem)
{
    LosTaskCB *task = NULL;
    LOS_ListDelete(priqueueItem);

    task = LOS_DL_LIST_ENTRY(priqueueItem, LosTaskCB, pendList);
    if (LOS_ListEmpty(&priQueueList[task->priority])) {
        *bitMap &= ~(PRIQUEUE_PRIOR0_BIT >> task->priority);//隊列空了,對應優先級位 置0
    }
}

同一個進程下的線程的優先級可以不一樣嗎?

請先想一下這個問題。

進程和線程是一對多的父子關系,內核調度的單元是任務(線程),鴻蒙內核中任務和線程是一個東西,只是不同的身份。一個進程可以有多個線程,線程又有各自獨立的狀態,那進程狀態該怎么界定?例如:ProcessA有 TaskA(阻塞狀態),TaskB(就緒狀態) 兩個線程,ProcessA是屬于阻塞狀態還是就緒狀態呢?

先看官方文檔的說明后再看源碼。

進程狀態遷移說明:

Init→Ready:

進程創建或fork時,拿到該進程控制塊后進入Init狀態,處于進程初始化階段,當進程初始化完成將進程插入調度隊列,此時進程進入就緒狀態。

Ready→Running:

進程創建后進入就緒態,發生進程切換時,就緒列表中最高優先級的進程被執行,從而進入運行態。若此時該進程中已無其它線程處于就緒態,則該進程從就緒列表刪除,只處于運行態;若此時該進程中還有其它線程處于就緒態,則該進程依舊在就緒隊列,此時進程的就緒態和運行態共存。

Running→Pend:

進程內所有的線程均處于阻塞態時,進程在最后一個線程轉為阻塞態時,同步進入阻塞態,然后發生進程切換。

Pend→Ready / Pend→Running:

阻塞進程內的任意線程恢復就緒態時,進程被加入到就緒隊列,同步轉為就緒態,若此時發生進程切換,則進程狀態由就緒態轉為運行態。

Ready→Pend:

進程內的最后一個就緒態線程處于阻塞態時,進程從就緒列表中刪除,進程由就緒態轉為阻塞態。

Running→Ready:

進程由運行態轉為就緒態的情況有以下兩種:

有更高優先級的進程創建或者恢復后,會發生進程調度,此刻就緒列表中最高優先級進程變為運行態,那么原先運行的進程由運行態變為就緒態。

若進程的調度策略為SCHED_RR,且存在同一優先級的另一個進程處于就緒態,則該進程的時間片消耗光之后,該進程由運行態轉為就緒態,另一個同優先級的進程由就緒態轉為運行態。

Running→Zombies:

當進程的主線程或所有線程運行結束后,進程由運行態轉為僵尸態,等待父進程回收資源。

注意看上面紅色的部分,一個進程竟然可以兩種狀態共存!

UINT16 processStatus; /**< [15:4] process Status; [3:0] The number of threads currently

                                                            running in the process */

    processCB->processStatus &= ~(status | OS_PROCESS_STATUS_PEND);//取反后的與位運算
    processCB->processStatus |= OS_PROCESS_STATUS_READY;//或位運算

一個變量存兩種狀態,怎么做到的?答案還是按位保存啊。還記得上面的位圖調度g_priQueueBitmap嗎,那可是存了32種狀態的。其實這在任何一個系統的內核源碼中都很常見,類似的還有左移 <<,右移 >>等等

繼續說進程和線程的關系,線程的優先級必須和進程一樣嗎?他們可以不一樣嗎?答案是:可以不一樣,否則怎么會有設置task優先級的函數。

線程調度器

真正讓CPU工作的是線程,進程只是個裝線程的容器,線程有任務棧空間,是獨立運行于內核空間,而進程只有用戶空間,具體在后續的內存篇會講,這里不展開說,但進程結構體LosProcessCB有一個這樣的定義。看名字就知道了,那是跟調度相關的。

    UINT32               threadScheduleMap;            /**< The scheduling bitmap table for the thread group of the
                                                            process */
    LOS_DL_LIST          threadPriQueueList[OS_PRIORITY_QUEUE_NUM]; /**< The process's thread group schedules the
                                                                         priority hash table */

咋一看怎么進程的結構體里也有32個隊列,其實這就是線程的就緒狀態隊列。threadScheduleMap就是進程自己的位圖調度器。具體看進程入隊和出隊的源碼。調度過程是先去進程就緒隊列里找最高優先級的進程,然后去該進程找最高優先級的線程來調度。具體看筆者認為的內核最美函數OsGetTopTask,能欣賞到他的美就讀懂了就緒隊列是怎么管理的。

LITE_OS_SEC_TEXT_MINOR LosTaskCB *OsGetTopTask(VOID)
{
    UINT32 priority, processPriority;
    UINT32 bitmap;
    UINT32 processBitmap;
    LosTaskCB *newTask = NULL;
#if (LOSCFG_KERNEL_SMP == YES)
    UINT32 cpuid = ArchCurrCpuid();
#endif
    LosProcessCB *processCB = NULL;
    processBitmap = g_priQueueBitmap;
    while (processBitmap) {
        processPriority = CLZ(processBitmap);
        LOS_DL_LIST_FOR_EACH_ENTRY(processCB, &g_priQueueList[processPriority], LosProcessCB, pendList) {
            bitmap = processCB->threadScheduleMap;
            while (bitmap) {
                priority = CLZ(bitmap);
                LOS_DL_LIST_FOR_EACH_ENTRY(newTask, &processCB->threadPriQueueList[priority], LosTaskCB, pendList) {
#if (LOSCFG_KERNEL_SMP == YES)
                    if (newTask->cpuAffiMask & (1U << cpuid)) {
#endif
                        newTask->taskStatus &= ~OS_TASK_STATUS_READY;
                        OsPriQueueDequeue(processCB->threadPriQueueList,
                                          &processCB->threadScheduleMap,
                                          &newTask->pendList);
                        OsDequeEmptySchedMap(processCB);
                        goto OUT;
#if (LOSCFG_KERNEL_SMP == YES)
                    }
#endif
                }
                bitmap &= ~(1U << (OS_PRIORITY_QUEUE_NUM - priority - 1));
            }
        }
        processBitmap &= ~(1U << (OS_PRIORITY_QUEUE_NUM - processPriority - 1));
    }

OUT:
    return newTask;
}
映射張大爺的故事:張大爺喊到張全蛋時進場時表演時,張全蛋要決定自己的哪個節目先表演,也要查下他的清單上優先級,它同樣也有個張大爺同款記分牌,就這么簡單。
編輯:hfy
聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 調度器
    +關注

    關注

    0

    文章

    98

    瀏覽量

    5249
  • 鴻蒙系統
    +關注

    關注

    183

    文章

    2634

    瀏覽量

    66344
收藏 人收藏

    評論

    相關推薦

    鴻蒙內核源碼Task/線程技術分析

    、使用內存空間等系統資源,并獨立于其它線程運行。 鴻蒙內核每個進程內的線程獨立運行、獨立調度,當前進程內線程的調度不受其它進程內線程的影響。 鴻蒙
    的頭像 發表于 10-18 10:42 ?2218次閱讀
    <b class='flag-5'>鴻蒙</b><b class='flag-5'>內核</b>源碼Task/線程技術分析

    (轉)HarmonyOS(鴻蒙OS)發布,聊聊操作系統的調度

    內核,但不是這篇。 本文想再談談關于人機交互操作系統本身以及微內核調度等操作系統比較核心的問題。 也許,鴻蒙內核確實對
    發表于 08-20 08:00

    【HarmonyOS】鴻蒙內核源碼分析(調度機制篇)

    看task從哪些渠道產生:渠道很多,可能是shell 的一個命令,也可能由內核創建,更多的是大家編寫應用程序new出來的一個線程。調度的內容已經有了,那他們如何有序的被調度?答案:是32個進程和線程就緒
    發表于 10-14 14:00

    鴻蒙源碼分析系列(總目錄) | 給HarmonyOS源碼逐行加上中文注釋

    |-鴻蒙內核源碼分析(調度機制篇) | 任務是如何被調度執行的|-鴻蒙內核源碼分析(
    發表于 11-20 11:24

    鴻蒙內核源碼分析(調度機制篇):Task是如何被調度執行的

    本文分析任務調度機制源碼 詳見:代碼庫建議先閱讀閱讀之前建議先讀本系列其他文章,進入鴻蒙系統源碼分析(總目錄),以便對本文任務調度機制的理解。為什么學一個東西要學那么多的概念?
    發表于 11-23 10:53

    鴻蒙內核源碼分析(調度隊列篇):進程和Task的就緒隊列調度的作用

    為何單獨講調度隊列鴻蒙內核代碼中有兩個源文件是關于隊列的,一個是用于
    發表于 11-23 11:09

    鴻蒙內核源碼分析(Task管理篇):task是內核調度的單元

    獨立運行、獨立調度,當前進程內線程的調度不受其它進程內線程的影響。鴻蒙內核中的線程采用搶占式調度機制,同時支持時間片輪轉
    發表于 11-23 14:01

    鴻蒙內核源碼分析(Task管理篇):task是內核調度的單元

    )代碼 ,這是怎么回事?其實在鴻蒙內核中, task就是線程, 初學者完全可以這么理解,但二者還是有區別,否則干嘛要分兩個詞描述。到底有什么區別?是管理上的區別,task是調度層面的概
    發表于 11-24 10:24

    VxWorks實時內核調度的研究分析

    VxWorks實時內核調度的研究分析論述了0S中調度的概念、類型、調度隊列模型,并著重對VxWorks實時
    發表于 12-16 14:07 ?13次下載

    Vx Works實時內核調度的研究分析

    論述了OS 中調度的概念、類型、調度隊列模型,并著重對VxWorks 實時內核進行了分析。關鍵詞:嵌入式實時操作系統(RTOS) ;VxWorks ;
    發表于 03-25 10:36 ?33次下載

    VxWorks實時內核調度的研究分析

    論述了0S中調度的概念、類型、調度隊列模型,并著重對VxWorks實時內核進行了分析。
    發表于 11-27 16:22 ?16次下載

    淺談鴻蒙內核源碼的棧

    上面的代碼鴻蒙內核用棧方式一樣,都采用了遞減滿棧的方式, 什么是遞減滿棧?
    的頭像 發表于 04-24 11:21 ?1442次閱讀
    <b class='flag-5'>淺談</b><b class='flag-5'>鴻蒙</b><b class='flag-5'>內核</b>源碼的棧

    鴻蒙內核源碼:誰來觸發調度工作?

    鴻蒙內核中 Task 和 線程 在廣義上可以理解為是一個東西,但狹義上肯定會有區別,區別在于管理體系的不同,Task是調度層面的概念,線程是進程層面概念。
    的頭像 發表于 04-24 10:50 ?1503次閱讀
    <b class='flag-5'>鴻蒙</b><b class='flag-5'>內核</b>源碼:誰來觸發<b class='flag-5'>調度</b>工作?

    鴻蒙內核源碼分析:task是內核調度的單元

    從系統的角度看,線程是競爭系統資源的最小運行單元。線程可以使用或等待CPU、使用內存空間等系統資源,并獨立于其它線程運行。 鴻蒙內核每個進程內的線程獨立運行、獨立調度,當前進程內線程的調度
    發表于 11-23 15:51 ?22次下載
    <b class='flag-5'>鴻蒙</b><b class='flag-5'>內核</b>源碼分析:task是<b class='flag-5'>內核</b><b class='flag-5'>調度</b>的單元

    鴻蒙內核源碼分析:進程和Task的就緒隊列調度的作用

    鴻蒙內核代碼中有兩個源文件是關于隊列的,一個是用于調度隊列,另一個是用于線程間通訊的IPC
    發表于 11-23 15:48 ?31次下載
    <b class='flag-5'>鴻蒙</b><b class='flag-5'>內核</b>源碼分析:進程和Task的就緒<b class='flag-5'>隊列</b>對<b class='flag-5'>調度</b>的作用
    主站蜘蛛池模板: 黄色网在线| 九月婷婷综合| 欧美人与物另类| 亚洲一区二区三区在线网站| 欧美aaaaa| 婷婷激情狠狠综合五月| 国产高清在线精品一区| 五月婷婷视频在线| 女人张开腿让男人桶免费最新| 寄宿日记免费看| 一本到午夜92版免费福利| 失禁h啪肉尿出来高h健身房| 午夜视频在线免费看| 交专区videossex另类| 又色又爽的视频| 性欧美xxxxhd| 久久激情综合网| 天天弄天天操| 国产h视频在线观看高清| 一级毛片视屏| 免费人成a大片在线观看动漫| 超级碰碰青草久热国产| 老湿成人影院| 欧美人成a视频www| 美女国产在线观看免费观看 | 午夜精品福利在线观看| 国产成人一区二区三中文| 欧美456| 亚洲精品aaa揭晓| 四虎最新紧急入口| 俺也去第四色| 一级aaaaa毛片免费视频| 免费黄色在线观看| 成人午夜网址| 一级黄色毛片播放| 777色狠狠一区二区三区香蕉| 成人精品福利| 日本xxxxx黄区免费看动漫| 最近免费| 久久国产精品无码网站| 国产gaysexchina男同men1068|