Linux 內核之bitmap

1.Bit 簡介

  bit 中文翻譯 ‘位’,即代表計算機裏最小的計數單位。bit 在計算機裏可用於表示 ‘0’ 和 ‘1’ 兩個值,由於表徵數字信號高低電平,爲計算機提供了最基礎的數據基礎。 計算機中,多個 bit 的集合構成了固定長度不同的數據類型,比如字節,字,雙字等 數據類型;多個 bit 也可以構成長度不同的位圖 (bitmap), 因此位圖就是包含了多個 bit 的數組。

  由於 bit 的值只包含了 ‘0’ 和 ‘1’ 兩種結果,因此在計算機中,bit 經常用於標識 數據的狀態。例如使用 bit 標識外圍硬件開關的狀態等。bit 從低位 (LSB) 開始編號 到高位 (MSB) 將 bit 定義爲有序的 bit 序列,程序可以通過特定的算法,有序的管理 每個 bit 的使用,從而構造複雜的數據結構。

2.Bit 框架

  Linux 內核廣泛使用 bit 作爲其基礎數據結構,包括 bitmap (位圖), bitops, bitmask, bit find 等,基於這些基礎數據結構,將 bit 運用到複雜的數據結構 中,例如 Bit,虛擬文件系統,進程管理等。因此 bit 是內核運行必不可少的部分。

2.1.bitmap 位圖

  Linux 定義了位圖 bitmap 爲 bit 的數組,數組的成員就是獨立的 bit。內核提供 了 bitmap 的通用框架和接口函數,以此便捷使用 bitmap,其定義如下:

  • lib/bitmap.c :文件包含了 bitmap 的核心實現以及通用接口。
  • include/linux/bitmap.h :bitmap 頭文件,包含了 bitmap 相關的宏以及內嵌函數接口
  • Documentation/core-api/kernel-api.rst bitmap :內核文檔

2.2.bitops

  Linux 提供了一套用於操作與機器位寬相同的位操作函數,這套函數用於操作 bitmap 長度在 BITS_PER_LONG 以內的 bit 集合,包括了 bit 的置位,清零,以及 翻轉操作。bitops 的函數實現與體系架構有關,因此不同的體系結構有不同的 實現方式,但實現的效果都是一致,因此 bitops 又分爲通用部分和與體系相關 部分。Linux 提供了一套通用框架接口函數,其定義基本如下:

  • include/asm-generic/bitops/non-atomic.h: bitops 函數最通用實現
  • arch/arm/include/asm/bitops.h: 與 ARM 相關的 bitops 函數實現

2.2.1.位操作__set_bit函數爲例來分析其原理:

include/asm-generic/bitops/non-atomic.h
 * __set_bit - Set a bit in memory
 * @nr: the bit to set
 * @addr: the address to start counting from
 *
 * Unlike set_bit(), this function is non-atomic and may be reordered.
 * If it's called on the same region of memory simultaneously, the effect
 * may be that only one operation succeeds.
 */
static inline void __set_bit(int nr, volatile unsigned long *addr)
{
    unsigned long mask = BIT_MASK(nr);
    unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr);

    *p |= mask;
}

宏定義如下:

include/linux/bitops.h:
#define BIT(nr)            (1UL << (nr))
#define BIT_MASK(nr)        (1UL << ((nr) % BITS_PER_LONG))
#define BIT_WORD(nr)        ((nr) / BITS_PER_LONG)

arch/arm/include/asm/types.h:
#define BITS_PER_LONG 32

__set_bit函數內容等價表示如下:

static inline void __set_bit(int nr, volatile unsigned long *addr)
{
    addr[nr/32] |= (1UL << (nr % 32)); 
    //或者addr[nr >> 5] |= (1UL << (nr & 31));
}

  addr是一個類型爲unsigned long(32 bits)的數組,通過nr/32得到要設置的比特位nr位於該數組中的哪一個unsigned long,然後通過( nr%32 )得到該unsigned long整數中是哪一位(第0位、第1位、…還是第31位?)需要設置;最後,通過 addr[nr/32] |= (1UL << (nr % 32)) 設置該unsigned long整數中相應的比特位。

  理解了上述代碼,則其它比特位操作API就容易懂了。

__clear_bit: 將addr所指的地址處的值第nr位清0,方法 addr[nr/32] & 11111011111- __change_bit: 將addr所指的地址處的值第nr位取反,方法addr[nr/32] ^ 00000100000
__test_and_set_bit:將addr所指的地址處的值第nr位置1,返回該bit原始值(0或1);
__test_and_clear_bit:將addr所指的地址處的值第nr位清0,返回該bit原始值(0或1);
__test_and_change_bit:將addr所指的地址處的值第nr位取反,返回該bit原始值(0或1);
test_bit:即測試nr位是否被置位,置位返回1;

2.3.bitmask

  Linux 提供了一套用於掩碼操作的函數。由於 bit 是內核的基礎組成部分,多種 複雜的數據結構都也基於 bit 實現,爲了實現多個 bit 的掩碼操作,內核也提供 多個函數以及宏用於便捷實現其功能。bitmask 的實現與機器的位寬實現密切相關, 其實現與 BITS_PER_LONG 的實現有關。Linux 提供了一套通過的框架接口,其 定義如下:

  • linux/include/linux/bitmap.h 包含了 bitmap 掩碼相關的宏以及內嵌函數接口
  • linux/include/bits.h 包含了 bit 基礎的掩碼宏。

2.4.bit find

  由於 bitmap 包含了多個 bit,需要在 bit 集合中找到符合要求的 bit, 由於 bit 序與大小端有關係,並與體系結構也有關係,因此內核提供了一套 用於查找 bit 的函數接口,其定義如下:

  • linux/lib/find_bit.c: bit 查找的通用接口函數
  • linux/arch/arm/include/asm/bitops.h: ARM 相關的 bit find 接口。
  • linux/lib/find_bit_benchmark.c: bit 查找測試接口函數

3.Bitmap 在內核中的應用

3.1.Bitmap 與、或、非、異或、與非操作

  提供了一套基礎的算術邏輯,包括與運算、或運算、非運算、異或運算、與非 運算等,這將大大增加 bitmap 的可用性,其函數接口及實踐接口如下:

  • bitmap_and: bitmap 與運算
  • bitmap_or: bitmap 或運算
  • bitmap_xor: bitmap 異或運算
  • bitmap_complement: bitmap 非運算
  • bitmap_andnot: bitmap 與非運算

3.1.1.bitmap_and

static inline int bitmap_and(unsigned long *dst, const unsigned long *src1,
                        const unsigned long *src2, unsigned int nbits)
{
        if (small_const_nbits(nbits))
                return (*dst = *src1 & *src2 & BITMAP_LAST_WORD_MASK(nbits)) != 0;
        return __bitmap_and(dst, src1, src2, nbits);
}

  函數用於 bitmap 的 AND 操作。參數 dst 存儲 AND 結果;參數 src1 指向第一個 bitmap;參數 src2 指向第二個 bitmap;參數 nbits 代表相與的位數。 函數首先調用 small_const_nbits() 函數判斷 nbits 參數是不是一個常數,並且 nbits 大於 0 小於 BITS_PER_LONG。如果滿足上面這些條件,那麼函數就將兩個 bitmap 相與, 結果與 BITMAP_LAST_WORD_MASK(nbits) 掩碼相與,生成的結果存儲在 dst 裏; 如果不符合,要麼 nbits 的位數超過 BITS_PER_LONG 或者 nbits 不是一個常量, 那麼函數就調用 __bitmap_and() 函數指向兩個 bitmap 的相與操作。

3.2.Bitmap 置位、清零、填充操作

  Bitmap 提供了一套用於置位、清零、填充的函數接口,使用這些接口可以便捷的對特定 位進行操作,其函數接口及實踐入口如下:

  • bitmap_set: bitmap 置位操作
  • bitmap_clear: bitmap 清零操作
  • bitmap_fill: bitmap 填充操作

3.3.Bitmap 轉換操作

  在 Linux 中,由於不同的位寬導致 bitmap 所包含的 bit 數目不一樣,其與 BITS_PER_LONG 有直接的關係,32-bit 系統中,BITS_PER_LONG 代表一個 long 變量中包含 32 個 bit;在 64-bit 系統中,BIST_PER_LONG 代表一個 long 變量中包含 64 個 bit。由於體系位寬的 差異,內核也爲 bitmap 提供了一套用於轉換的函數,函數接口及實踐入口如下:

  • bitmap_from_arr32: 將 32bit 的數組轉換成一個 bitmap
  • bitmap_to_arr32: 將 bitmap 的數組轉換成一個 32 位數組
  • bitmap_from_u64: 將 64 位變量轉換成 bitmap
  • bitmap_parse: 將字符串轉換成 bitmap

3.4.Bitmap 位移操作

  Bitmap 在特定的需求中需要將其左移或右移多個 bit,因此內核提供了專門用於 bitmap 位移的函數,函數接口及實踐入口如下:

  • bitmap_shift_left: bitmap 左移操作
  • bitmap_shift_right: bitmap 右移操作

3.5.Bitmap 查找操作

  Bitmap 同樣提供了一套用於查找特定 bit 的函數接口,以便在 bitmap 中找到指定 的 bit,函數接口及實踐入口如下:

  • bitmap_find_next_zero_area_off: bitmap 中找到整塊零區域
  • find_first_bit: bitmap 中找到第一個置位的位置
  • find_first_zero_bit: bitmap 中找到第一個清零的位置
  • find_last_bit: bitmap 中找到最後一個置位的位置

3.6.Bitmap 掩碼操作

  Bitmap 也提供了一套用於掩碼的操作,和 bitops 的掩碼相比,bitmap 提供的掩碼 涉及的 bit 範圍更廣闊,可以超出 BITS_PER_LONG 的限制,函數接口及實踐入口如下:

  • BITMAP_FIRST_WORD_MASK: bitmap 低位指定個 bit 爲零,其餘全 1 的掩碼
  • BITMAP_LAST_WORD_MASK: bitmap 低位指定個 bit 爲 1,其餘全 0 的掩碼

3.7.Bitmap 遍歷操作

  Bitmap 提供了一套函數用於遍歷 bitmap 中置位或者清零的位置,這些函數中可以從 頭還是遍歷,也可以從指定位置遍歷,函數接口及實踐入口如下:

  • for_each_clear_bit_from: 從指定位置遍歷 bitmap 清零 bit
  • for_each_clear_bit: 從頭開始遍歷 bitmap 清零 bit
  • for_each_set_bit: 從頭開始遍歷 bitmap 置位 bit
  • for_each_set_bit_from: 從指定位置開始遍歷 bitmap 置位 bit

3.8.Bitmap 其餘操作

  Bitmap 還提供了其餘特定需求的函數,函數接口及實踐入口如下:

  • bitmap_copy: 複製 bitmap
  • bitmap_copy_clear_tail: 複製 bitmap 低位
  • bitmap_empty: 判斷 bitmap 是否爲空
  • bitmap_equal: 判斷 bitmap 是否相等
  • bitmap_weight: 獲得 bitmap 中 1 的個數

refer to

  • https://biscuitos.github.io/blog/BITMAP/
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章