鸿蒙轻内核A核源码分析系列二:数据结构-位图操作

开发 前端
文章由鸿蒙社区产出,想要了解更多内容请前往:51CTO和华为官方战略合作共建的鸿蒙技术社区https://harmonyos.51cto.com

[[405939]]

想了解更多内容,请访问:

51CTO和华为官方合作共建的鸿蒙技术社区

https://harmonyos.51cto.com

在进一步分析之前,本文我们先来熟悉下OpenHarmony鸿蒙轻内核提供的位操作模块,在互斥锁等模块对位操作有使用。位操作是指对二进制数的bit位进行操作。程序可以设置某一变量为状态字,状态字中的每一bit位(标志位)可以具有自定义的含义。

1 位操作的宏定义

位操作模块提供对32位无符号整数数值的bit位进行操作,bit位取值为0-31,以0开始计算,从左向右,第0位,第1位。。。第31位等。⑴处定义的宏OS_BITMAP_MASK如下,也就是十进制31。如果传入的比特位pos大于31,会通过逻辑与运算截断(pos & OS_BITMAP_MASK),只取低5位,确保不会大于31,避免溢出。⑵处定义的位图掩码全是1。

  1. ⑴  #define OS_BITMAP_MASK 0x1FU 
  2. ⑵  #define OS_BITMAP_WORD_MASK ~0UL 

 在文件kernel\include\los_bitmap.h中定义了常用的位操作相关的宏。宏BITMAP_WORD根据参数x计算出需要操作第几个状态字,由于计算状态字的使用的是UINTPTR,状态字可以是32位、也可以是64位。后文,我们默认以32位进行讲解。宏BITMAP_FIRST_WORD_MASK传入的参数是位操作的开始bit位数,用于计算需要进行位操作的掩码,从开始位全部是1,宏BITMAP_LAST_WORD_MASK传入的参数是位操作的结束bit位数,用于计算需要进行位操作的掩码,结束位之前全部是1。宏BITMAP_NUM_WORDS传入位数,计算状态字的数量。

  1. #define _ONE(x) (1 + ((x) - (x))) 
  2.  #define BIT(n)  (1U << (n)) 
  3.  #define BIT_GET(x, bit) ((x) & (_ONE(x) << (bit))) 
  4.  #define BIT_SHIFT(x, bit) (((x) >> (bit)) & 1) 
  5.  #define BITS_GET(x, high, low) ((x) & (((_ONE(x) << ((high) + 1)) - 1) & ~((_ONE(x) << (low)) - 1))) 
  6.  #define BITS_SHIFT(x, high, low) (((x) >> (low)) & ((_ONE(x) << ((high) - (low) + 1)) - 1)) 
  7.  #define BIT_SET(x, bit) (((x) & (_ONE(x) << (bit))) ? 1 : 0) 
  8.  #define BITMAP_BITS_PER_WORD (sizeof(UINTPTR) * 8) 
  9.  #define BITMAP_NUM_WORDS(x) (((x) + BITMAP_BITS_PER_WORD - 1) / BITMAP_BITS_PER_WORD) 
  10.  #define BITMAP_WORD(x) ((x) / BITMAP_BITS_PER_WORD) 
  11.  #define BITMAP_BIT_IN_WORD(x) ((x) & (BITMAP_BITS_PER_WORD - 1)) 
  12.  #define BITMAP_FIRST_WORD_MASK(start) (~0UL << ((start) % BITMAP_BITS_PER_WORD)) 
  13.  #define BITMAP_LAST_WORD_MASK(nbits) \ 
  14.      (((nbits) % BITMAP_BITS_PER_WORD) ? (1UL << ((nbits) % BITMAP_BITS_PER_WORD)) - 1 : ~0UL) 
  15.  #define BITMAP_BITS_PER_INT (sizeof(INTPTR) * 8) 
  16.  #define BITMAP_BIT_IN_INT(x) ((x) & (BITMAP_BITS_PER_INT - 1)) 
  17.  #define BITMAP_INT(x) ((x) / BITMAP_BITS_PER_INT) 
  18.  #define BIT_MASK(x) (((x) >= sizeof(UINTPTR) * 8) ? (0UL - 1) : ((1UL << (x)) - 1)) 

2 位操作常用功能

OpenHarmony鸿蒙轻内核的位操作模块提供标志位的置1和清0操作,可以改变标志位的内容,同时还提供获取状态字中标志位为1的最高位和最低位的功能。用户也可以对系统的寄存器进行位操作。位操作提供了7个API,进行置1、清0、获取为1的最高、最低位等操作,如下:

下面,我们剖析下位操作的源代码。

2.1 LOS_BitmapSet()对状态字的某一标志位进行置1操作

对状态字的某一标志位进行置1操作。我们先看看传入的参数,需要的2个参数分别是:需要改变bit位内容的状态字UINT32 *bitmap,需要改变的bit位位数UINT16 pos。

代码很简单,首先进行基础的校验,如果状态字为空,则返回。然后计算pos & OS_BITMAP_MASK,只取二进制的低5位,最大位值为31,避免左移的时候发生溢出。1U << (pos & OS_BITMAP_MASK)就是需要改变内容的状态字的bit位,通过按位或运算设置状态字UINT32 *bitmap的指定bit位的内容为1。

  1. VOID LOS_BitmapSet(UINT32 *bitmap, UINT16 pos) 
  2.     if (bitmap == NULL) { 
  3.         return
  4.     } 
  5.  
  6.     *bitmap |= 1U << (pos & OS_BITMAP_MASK); 

2.2 LOS_BitmapClr()对状态字的某一标志位进行清0操作

对状态字的某一标志位进行清0操作,代码和置1操作对应,比较简单,~(1U << (pos & OS_BITMAP_MASK))表示需要改变内容的状态字的bit位为0,其余位为1,然后通过按位与运算设置状态字UINT32 *bitmap的指定bit位的内容为0。

  1. VOID LOS_BitmapClr(UINT32 *bitmap, UINT16 pos) 
  2.     if (bitmap == NULL) { 
  3.         return
  4.     } 
  5.  
  6.     *bitmap &= ~(1U << (pos & OS_BITMAP_MASK)); 

2.3 LOS_HighBitGet()获取状态字中为1的最高位

代码中CLZ(bitmap)是宏,展开为(__builtin_clz(bitmap)),这是编译器内置的高效位运算的库函数,clz是count leading zeros的缩写,就是统计二进制数值中高位区开头的全是0的数目。使用OS_BITMAP_MASK减去该值,结果就是状态字中的1的最高位。

  1. UINT16 LOS_HighBitGet(UINT32 bitmap) 
  2.     if (bitmap == 0) { 
  3.         return LOS_INVALID_BIT_INDEX; 
  4.     } 
  5.  
  6.     return (OS_BITMAP_MASK - CLZ(bitmap)); 

2.4 LOS_LowBitGet()获取状态字中为1的最低位

代码其中CTZ(bitmap)是宏,展开为(__builtin_ctz(value)),这是编译器内置的高效位运算的库函数,ctz是count trailing zeros的缩写,就是统计二进制数值中低位区结尾的全是0的数目,该结果就是状态字中的1的最低位。

  1. UINT16 LOS_LowBitGet(UINT32 bitmap) 
  2.     if (bitmap == 0) { 
  3.         return LOS_INVALID_BIT_INDEX; 
  4.     } 
  5.  
  6.     return CTZ(bitmap); 

2.5 LOS_BitmapSetNBits()对状态字的连续标志位进行置1操作

可以使用LOS_BitmapSetNBits()函数对状态字的连续比特位进行置1操作,第一个参数是需要改变bit位内容的状态字UINT32 *bitmap,第二个参数是需要置1的bit位开始数start,第三个参数是需要置1的数量numsSet。由于bit位开始数start并没有限制在[0,31],所以实际上设置的可能是UINT32 *bitmap状态字后面的状态字,需要根据业务实际情况进行设置,避免覆写其他内存。同样,需要置1的数量numsSet也可能跨多个状态字。如图所示:

我们看下代码,

⑴处计算出需要操作的状态字,其中BITMAP_WORD(start)计算相对状态字bitmap需要偏移的数量,如果start处于区间[0,31],BITMAP_WORD(start)等于0,操作的就是状态字bitmap。如果start处于区间[32,63],BITMAP_WORD(start)等于1,操作的就是状态字bitmap后面的第一个状态字,以此类推。

⑵处size可以和bit位开始数start结合来理解,size就是需要置1的bit位结束位数。

⑶处需要置1操作的bit位的位数。

⑷是对应需要置1操作的bit位的掩码。

⑸处如果条件成立,说明需要置1操作需要跨多个状态字进行操作,代码会一个状态字处理完毕,再去处理下一个状态字。

⑹处把当前状态字的相应的bit位进行置1操作,然后执行⑺把剩余需要置1的位数减去已经置1的位数。

⑻处更新bitsToSet和maskToSet,然后指针p指向下一个状态字。

⑼处如果需要置1的位数大于0,并且此时已经可以在一个状态字内完成操作,执行⑽处计算需要置1操作的掩码,从bit开始位到结束位需要进行置1。

⑾处代码执行置1操作,完成对状态字的连续标志位进行置1操作。

  1. VOID LOS_BitmapSetNBits(UINTPTR *bitmap, UINT32 start, UINT32 numsSet) 
  2. ⑴  UINTPTR *p = bitmap + BITMAP_WORD(start); 
  3. ⑵  const UINT32 size = start + numsSet; 
  4. ⑶  UINT16 bitsToSet = BITMAP_BITS_PER_WORD - (start % BITMAP_BITS_PER_WORD); 
  5. ⑷  UINTPTR maskToSet = BITMAP_FIRST_WORD_MASK(start); 
  6.  
  7. ⑸  while (numsSet > bitsToSet) { 
  8. ⑹      *p |= maskToSet; 
  9. ⑺      numsSet -= bitsToSet; 
  10. ⑻      bitsToSet = BITMAP_BITS_PER_WORD; 
  11.         maskToSet = OS_BITMAP_WORD_MASK; 
  12.         p++; 
  13.     } 
  14. ⑼  if (numsSet) { 
  15. ⑽      maskToSet &= BITMAP_LAST_WORD_MASK(size); 
  16.         *p |= maskToSet; 
  17.     } 

2.6 LOS_BitmapClrNBits()对状态字的连续标志位进行清0操作

可以使用LOS_BitmapClrNBits()函数对状态字的连续比特位进行清0操作,第一个参数是需要改变bit位内容的状态字UINT32 *bitmap,第二个参数是需要清0的bit位开始数start,第三个参数是需要清0的数量numsClear。该函数是函数LOS_BitmapSetNBits()的反向操作,代码解释可以参考函数LOS_BitmapSetNBits()。

  1. VOID LOS_BitmapClrNBits(UINTPTR *bitmap, UINT32 start, UINT32 numsClear) 
  2.     UINTPTR *p = bitmap + BITMAP_WORD(start); 
  3.     const UINT32 size = start + numsClear; 
  4.     UINT16 bitsToClear = BITMAP_BITS_PER_WORD - (start % BITMAP_BITS_PER_WORD); 
  5.     UINTPTR maskToClear = BITMAP_FIRST_WORD_MASK(start); 
  6.  
  7.     while (numsClear >= bitsToClear) { 
  8.         *p &= ~maskToClear; 
  9.         numsClear -= bitsToClear; 
  10.         bitsToClear = BITMAP_BITS_PER_WORD; 
  11.         maskToClear = OS_BITMAP_WORD_MASK; 
  12.         p++; 
  13.     } 
  14.     if (numsClear) { 
  15.         maskToClear &= BITMAP_LAST_WORD_MASK(size); 
  16.         *p &= ~maskToClear; 
  17.     } 

2.7 LOS_BitmapFfz()获取从最低有效位开始的第一个0的bit位

可以使用LOS_BitmapFfz()函数获取从最低有效位开始的第一个0的bit位位数,第一个参数是需要改变bit位内容的状态字UINT32 *bitmap,第二个参数numBits表示最大的位数,对返回值进行限制,需要在指定的位数内找到符合条件的位数,否则返回-1。

在看函数代码之前,先了解下Ffz()函数,如下:调用内嵌函数__builtin_ffsl()可以获取一个unsigned long类型数字的二进制形式的从左开始的第一个1的位数,这个位数从1开始计数。比如对于二进制数字0110,该函数会返回2。在下面的函数中,给函数__builtin_ffsl()传入的参数进行了取反,并减去了1,所以Ffz()函数返回一个数字从左开始的第一个0的位数,这个位数从0开始计数。

  1. /* find first zero bit starting from LSB */ 
  2. STATIC INLINE UINT16 Ffz(UINTPTR x) 
  3.     return __builtin_ffsl(~x) - 1; 

我们接着看下函数LOS_BitmapFfz()的代码。⑴处根据位数numBits计算出对应的状态字的数量,然后依次循环每一个状态字,⑵处如果状态字全为1,则继续循环,否则执行⑶。执行到⑶说明,,前面有i个状态字的各个位全为1。i * BITMAP_BITS_PER_WORD + Ffz(bitmap[i])就表示各个状态字的二进制位中,从左到右第一个0的位置。⑷处如果获取的位数小于第二个参数,则返回获取的位数,否则返回-1。如下图所示:

源代码如下:

  1. INT32 LOS_BitmapFfz(UINTPTR *bitmap, UINT32 numBits) 
  2.     INT32 bit, i; 
  3.  
  4. ⑴  for (i = 0; i < BITMAP_NUM_WORDS(numBits); i++) { 
  5. ⑵      if (bitmap[i] == OS_BITMAP_WORD_MASK) { 
  6.             continue
  7.         } 
  8. ⑶      bit = i * BITMAP_BITS_PER_WORD + Ffz(bitmap[i]); 
  9. ⑷      if (bit < numBits) { 
  10.             return bit
  11.         } 
  12.         return -1; 
  13.     } 
  14.     return -1; 

小结

本文带领大家一起剖析了鸿蒙轻内核的位操作模块的源代码。

想了解更多内容,请访问:

51CTO和华为官方合作共建的鸿蒙技术社区

https://harmonyos.51cto.com

 

责任编辑:jianghua 来源: 鸿蒙社区
相关推荐

2021-05-12 09:45:20

鸿蒙HarmonyOS应用

2021-04-30 15:06:34

鸿蒙HarmonyOS应用

2021-05-10 15:05:56

鸿蒙HarmonyOS应用

2021-11-08 15:06:15

鸿蒙HarmonyOS应用

2022-03-11 20:23:14

鸿蒙源码分析进程管理

2022-04-13 11:02:12

鸿蒙事件模块事件Event

2022-03-03 18:28:28

Harmony进程任务管理模块

2022-01-12 10:50:23

鸿蒙HarmonyOS应用

2022-01-10 15:31:44

鸿蒙HarmonyOS应用

2021-06-04 09:57:49

鸿蒙HarmonyOS应用

2021-06-04 14:15:10

鸿蒙HarmonyOS应用

2021-05-17 09:28:59

鸿蒙HarmonyOS应用

2021-11-05 15:00:33

鸿蒙HarmonyOS应用

2021-05-08 15:14:50

鸿蒙HarmonyOS应用

2022-04-13 11:12:43

鸿蒙轻内核信号量模块操作系统

2021-05-25 09:28:34

鸿蒙HarmonyOS应用

2021-10-20 16:08:57

鸿蒙HarmonyOS应用

2021-05-31 20:30:55

鸿蒙HarmonyOS应用

2021-12-01 15:59:22

鸿蒙HarmonyOS应用

2022-03-31 16:26:49

鸿蒙源码分析进程管理
点赞
收藏

51CTO技术栈公众号