天天看點

v19.04 鴻蒙核心源碼分析(位圖管理篇) | 誰能一分錢分兩半用 | 百篇部落格分析OpenHarmony源碼

子曰:“飯疏食,飲水,曲肱而枕之,樂亦在其中矣。不義而富且貴,于我如浮雲。” 《論語》:述而篇

v19.04 鴻蒙核心源碼分析(位圖管理篇) | 誰能一分錢分兩半用 | 百篇部落格分析OpenHarmony源碼

百篇部落格系列篇.本篇為:

v19.xx 鴻蒙核心源碼分析(位圖管理篇) | 誰能一分錢分兩半用

基礎工具相關篇為:

  • v01.12 鴻蒙核心源碼分析(雙向連結清單) | 誰是核心最重要結構體
  • v19.04 鴻蒙核心源碼分析(位圖管理) | 誰能一分錢分兩半花
  • v20.03 鴻蒙核心源碼分析(用棧方式) | 程式運作場地由誰提供
  • v31.02 鴻蒙核心源碼分析(定時器) | 哪個任務的優先級最高
  • v34.04 鴻蒙核心源碼分析(原子操作) | 誰在為原子操作保駕護航
  • v35.03 鴻蒙核心源碼分析(時間管理) | 誰是核心基本時間機關

先看四個宏定義,程序和線程(線程就是任務)最高和最低優先級定義,[0,31]區間,即32級,優先級用于排程,CPU根據這個來決定先運作哪個程序和任務。

#define OS_PROCESS_PRIORITY_HIGHEST      0 //程序最高優先級
#define OS_PROCESS_PRIORITY_LOWEST       31 //程序最低優先級
#define OS_TASK_PRIORITY_HIGHEST    0 //任務最高優先級,軟時鐘任務就是最進階任務,見于 OsSwtmrTaskCreate
#define OS_TASK_PRIORITY_LOWEST     31 //任務最低優先級
           

為何程序和線程都是32個優先級?

回答這個問題之前,先回答另一個問題,為什麼人類幾乎所有的文明都是用十進制的計數方式。答案掰手指就知道了,因為人有十根手指頭。瑪雅人的二十進制那是把腳指頭算上了,但其實也算是十進制的表示。

這是否說明一個問題,認知受環境的影響,方向是怎麼簡單/友善怎麼來。這也可以解釋為什麼人類語言發音包括各種方言對媽媽這個詞都很類似,因為嬰兒說mama是最容易的。 注意認識這點很重要!

而計算機的世界是二進制的,是是非非,清清楚楚,特别的簡單,二進制已經最簡單了,到底啦,不可能有更簡單的了。還記得雙向連結清單篇中說過的嗎,因為簡單是以才不簡單啊,大道若簡,計算機就靠着這01碼,表述萬千世界。

但人類的大腦不擅長存儲,二進制太長了數到100就撐爆了大腦,記不住,為了記憶和運算友善,程式設計常用靠近10進制的 16進制來表示 ,0x9527ABCD 看着比 0011000111100101010100111舒服多了。

應用開發和核心開發有哪些差別?

差別還是很大的,這裡隻說一點,就是對位的控制能力,核心會出現大量的按位運算(&,|,~,^) , 一個變量的不同位表達不同的含義,但這在應用程式員那是很少看到的,他們用的更多的是邏輯運算(&&,||,!)

#define OS_TASK_STATUS_INIT         0x0001U //初始化狀态
#define OS_TASK_STATUS_READY        0x0002U //就緒狀态的任務都将插入就緒隊列
#define OS_TASK_STATUS_RUNNING      0x0004U //運作狀态
#define OS_TASK_STATUS_SUSPEND      0x0008U //挂起狀态
#define OS_TASK_STATUS_PEND         0x0010U //阻塞狀态
           

這是任務各種狀态(注者後續将比如成貼标簽)表述,将它們還原成二進制就是:

0000000000000001 = 0x0001U

0000000000000010 = 0x0002U

0000000000000100 = 0x0004U

0000000000001000 = 0x0008U

0000000000010000 = 0x0010U

發現二進制這邊的差別沒有,用每一位來表示一種不同的狀态,1表示是,0表示不是。

這樣的好處有兩點:

1.可以多種标簽同時存在 比如 0x07 = 0b00000111,對應以上就是任務有三個标簽(初始,就緒,和運作),程序和線程在運作期間是允許多種标簽同時存在的。

2.節省了空間,一個變量就搞定了,如果是應用程式員要實作這三個标簽同時存在,習慣上要定義三個變量的,因為你的排他性顆粒度是一個變量而不是一個位。

而對位的管理/運算就需要有個專門的管理器:位圖管理器 (見源碼 los_bitmap.c )

什麼是位圖管理器?

直接上部分代碼,代碼關鍵地方都加了中文注釋,簡單說就是對位的各種操作,比如如何在某個位上設1?如何找到最高位為1的是哪個位置?這些函數都是有大用途的。

//對狀态字的某一标志位進行置1操作
VOID LOS_BitmapSet(UINT32 *bitmap, UINT16 pos)
{
    if (bitmap == NULL) {
        return;
    }

    *bitmap |= 1U << (pos & OS_BITMAP_MASK);//在對應位上置1
}
//對狀态字的某一标志位進行清0操作
VOID LOS_BitmapClr(UINT32 *bitmap, UINT16 pos)
{
    if (bitmap == NULL) {
        return;
    }

    *bitmap &= ~(1U << (pos & OS_BITMAP_MASK));//在對應位上置0
}
/********************************************************
雜項算術指令
CLZ 用于計算操作數最高端0的個數,這條指令主要用于一下兩個場合
  計算操作數規範化(使其最高位為1)時需要左移的位數
  确定一個優先級掩碼中最高優先級
********************************************************/
//擷取狀态字中為1的最高位 例如: 00110110 傳回 5
UINT16 LOS_HighBitGet(UINT32 bitmap)
{
    if (bitmap == 0) {
        return LOS_INVALID_BIT_INDEX;
    }

    return (OS_BITMAP_MASK - CLZ(bitmap));
}
//擷取狀态字中為1的最低位, 例如: 00110110 傳回 2
UINT16 LOS_LowBitGet(UINT32 bitmap)
{
    if (bitmap == 0) {
        return LOS_INVALID_BIT_INDEX;
    }

    return CTZ(bitmap);//
}


           

位圖在哪些地方應用?

核心很多子產品在使用位圖,這裡隻說程序和線程子產品,還記得開始的問題嗎,為何程序和線程都是32個優先級?因為他們的優先級是由位圖管理的,管理一個UINT32的變量,是以是32級,一個位一個級别,最高位優先級最低。

UINT32          priBitMap;          /**< BitMap for recording the change of task priority, //任務在執行過程中優先級會經常變化,這個變量用來記錄所有曾經變化
                                             the priority can not be greater than 31 */   //過的優先級,例如 ..01001011 曾經有過 0,1,3,6 優先級


           

這是任務控制塊中對排程優先級位圖的定義,注意一個任務的優先級在運作過程中可不是一成不變的,核心會根據運作情況而改變它的,這個變量是用來儲存這個任務曾經有過的所有優先級曆史記錄。

比如 任務A的優先級位圖是 00000001001011 ,可以看出它曾經有過四個排程等級記錄,那如果想知道優先級最低的記錄是多少時怎麼辦呢?

诶,上面的位圖管理器函數 UINT16 LOS_HighBitGet(UINT32 bitmap) 就很有用啦 ,它傳回的是1在高位出現的位置,可以數一下是 6

因為任務的優先級0最大,是以最終的意思就是A任務曾經有過的最低優先級是6

一定要了解位圖的操作,核心中大量存在這類代碼,尤其到了彙編層,對寄存器的操作大量的出現。

比如以下這段彙編代碼。

MSR     CPSR_c, #(CPSR_INT_DISABLE | CPSR_SVC_MODE)  @禁止中斷并切到管理模式
    LDRH    R1, [R0, #4]  @将存儲器位址為R0+4 的低16位資料讀入寄存器R1,并将R1的高16 位清零
    ORR     R1, #OS_TASK_STATUS_RUNNING @或指令 R1=R1|OS_TASK_STATUS_RUNNING
    STRH    R1, [R0, #4]  @将寄存器R1中的低16位寫入以R0+4為位址的存儲器中


           

程式設計執行個體

對資料實作位操作,本執行個體實作如下功能:

  • 某一标志位置1。
  • 擷取标志位為1的最高bit位。
  • 某一标志位清0。
  • 擷取标志位為1的最低bit位。
#include "los_bitmap.h"
#include "los_printf.h"

static UINT32 Bit_Sample(VOID)
{
  UINT32 flag = 0x10101010;
  UINT16 pos;

  dprintf("\nBitmap Sample!\n");
  dprintf("The flag is 0x%8x\n", flag);

  pos = 8;
  LOS_BitmapSet(&flag, pos);
  dprintf("LOS_BitmapSet:\t pos : %d, the flag is 0x%0+8x\n", pos, flag);

  pos = LOS_HighBitGet(flag);
  dprintf("LOS_HighBitGet:\t The highest one bit is %d, the flag is 0x%0+8x\n", pos, flag);

  LOS_BitmapClr(&flag, pos);
  dprintf("LOS_BitmapClr:\t pos : %d, the flag is 0x%0+8x\n", pos, flag);

  pos = LOS_LowBitGet(flag);
  dprintf("LOS_LowBitGet:\t The lowest one bit is %d, the flag is 0x%0+8x\n\n", pos, flag);

  return LOS_OK;
}
           

結果驗證

Bitmap Sample!
The flag is 0x10101010
LOS_BitmapSet: pos : 8,  the flag is 0x10101110
LOS_HighBitGet:The highest one bit is 28, the flag is 0x10101110
LOS_BitmapClr: pos : 28, the flag is 0x00101110
LOS_LowBitGet: The lowest one bit is 4, the flag is 0x00101110
           

百篇部落格分析.深挖核心地基

  • 給鴻蒙核心源碼加注釋過程中,整理出以下文章。内容立足源碼,常以生活場景打比方盡可能多的将核心知識點置入某種場景,具有畫面感,容易了解記憶。說别人能聽得懂的話很重要! 百篇部落格絕不是百度教條式的在說一堆诘屈聱牙的概念,那沒什麼意思。更希望讓核心變得栩栩如生,倍感親切.确實有難度,自不量力,但已經出發,回頭已是不可能的了。 😛
  • 與代碼有bug需不斷debug一樣,文章和注解内容會存在不少錯漏之處,請多包涵,但會反複修正,持續更新,v**.xx 代表文章序号和修改的次數,精雕細琢,言簡意赅,力求打造精品内容。

按功能子產品:

基礎工具 加載運作 程序管理 編譯建構

雙向連結清單

位圖管理

用棧方式

定時器

原子操作

時間管理

ELF格式

ELF解析

靜态連結

重定位

程序映像

程序概念

Fork

特殊程序

程序回收

信号生産

信号消費

Shell編輯

Shell解析

編譯環境

編譯過程

環境腳本

建構工具

gn應用

忍者ninja

程序通訊 記憶體管理 前因後果 任務管理

自旋鎖

互斥鎖

信号量

事件控制

消息隊列

記憶體配置設定

記憶體彙編

記憶體映射

記憶體規則

實體記憶體

總目錄

排程故事

記憶體主奴

源碼注釋

源碼結構

靜态站點

時鐘任務

任務排程

排程隊列

排程機制

線程概念

并發并行

CPU

系統調用

任務切換

檔案系統 硬體架構

檔案概念

索引節點

挂載目錄

根檔案系統

字元裝置

VFS

檔案句柄

管道檔案

彙編基礎

彙編傳參

工作模式

寄存器

異常接管

彙編彙總

中斷切換

中斷概念

中斷管理

百萬漢字注解.精讀核心源碼

四大碼倉中文注解 . 定期同步官方代碼

v19.04 鴻蒙核心源碼分析(位圖管理篇) | 誰能一分錢分兩半用 | 百篇部落格分析OpenHarmony源碼

鴻蒙研究站( weharmonyos ) | 每天死磕一點點,原創不易,歡迎轉載,請注明出處。若能支援點贊更好,感謝每一份支援。

繼續閱讀