更多内容请访问:https://harmonyos.51cto.com,与华为联合打造的鸿蒙技术社区正在进一步解析之前文章,我们来熟悉一下OpenHarmony鸿蒙轻内核提供的位操作模块,用于互斥锁等模块中的位操作。位操作是指对二进制数的位进行操作。程序可以将某个变量设置为状态字,状态字中的每一位(标志位)都可以有自定义的含义。1位运算的宏定义位运算模块提供了对32位无符号整数值的位的运算。位的值是0-31,从0开始,从左到右,第0位,第1位。..No.31等⑴中定义的宏OS_BITMAP_MASK如下,十进制为31。如果传入的位pos大于31,则通过逻辑与运算(pos&OS_BITMAP_MASK)截断,只取低5位,保证不大于31,避免溢出。(2)地方定义的位图掩码全为1。⑴#defineOS_BITMAP_MASK0x1FU⑵#defineOS_BITMAP_WORD_MASK~0UL在文件kernel\include\los_bitmap.h中定义了与位操作相关的常用宏。宏BITMAP_WORD根据参数x计算需要操作哪个状态字。由于UINTPTR是用来计算状态字的,所以状态字可以是32位也可以是64位。下面我们默认以32位来说明。宏BITMAP_FIRST_WORD_MASK传入的参数是位操作开始的位数,用于计算需要操作的掩码。计算需要位操作的掩码,在结束位之前全为1。宏BITMAP_NUM_WORDS传入位数来计算状态字的个数。#define_ONE(x)(1+((x)-(x)))#defineBIT(n)(1U<<(n))#defineBIT_GET(x,bit)((x)&(_ONE(x)<<(位)))#defineBIT_SHIFT(x,位)(((x)>>(位))&1)#defineBITS_GET(x,高,低)((x)&(((_ONE(x)<<((高)+1))-1)&~((_ONE(x)<<(低))-1)))#defineBITS_SHIFT(x,高,低)(((x)>>(低))&((_ONE(x)<<((high)-(low)+1))-1))#defineBIT_SET(x,bit)(((x)&(_ONE(x)<<(bit)))?1:0)#defineBITMAP_BITS_PER_WORD(sizeof(UINTPTR)*8)#defineBITMAP_NUM_WORDS(x)(((x)+BITMAP_BITS_PER_WORD-1)/BITMAP_BITS_PER_WORD)#defineBITMAP_WORD(x)((x)/BITMAP_BITS_PER_WORD)#defineBITMAP_BIT(x)(WORDx)&(BITMAP_BITS_PER_WORD-1))#defineBITMAP_FIRST_WORD_MASK(开始)(~0UL<<(开始)%BITMAP_BITS_PER_WORD))#defineBITMAP_LAST_WORD_MASK(nbits)\(((nbits)%BITMAP_BITS_PER_WORD)?(1UL<<((nbits)%BITMAP_BITS_PER_WORD))-1:~0UL)#defineBITMAP_BITS_PER_INT(sizeof(INTPTR)*8)#defineBITMAP_BIT_IN_INT(x)((x)&(BITMAP_BITS_PER_INT-1))#defineBITMAP_INT(x)((x)/BITMAP_BITS_PER_INT)#defineBIT_MASK(x)(((x)>=sizeof(UINTPTR)*8)?(0UL-1):((1UL<<(x))-1))2位操作常用函数OpenHarmony鸿蒙轻核的位操作模块提供了设置和清除标志位的操作,可以改变标志位的内容,也提供了获取最高位和最低位的功能标志位为1的状态字中的位用户还可以对系统寄存器进行位操作。位操作提供了7个API来进行置1、清0、获取1的最高位和最低位等操作,如下:接下来我们分析下位操作的源码。2.1LOS_BitmapSet()Performsa1operationonacertainflagbitofthestatusword对状态字的某个标志位执行1操作。我们先来看一下传入的参数。需要的两个参数是:需要改变位内容的状态字UINT32*bitmap,以及需要改变的位数UINT16pos。代码很简单,先进行基本的验证,如果状态字为空则返回。然后计算pos&OS_BITMAP_MASK,只取二进制的低5位,最大位值为31,避免左移时溢出。1U<<(pos&OS_BITMAP_MASK)是状态字中需要改变内容的位,将状态字UINT32*bitmap指定位的内容按位或运算置1。VOIDLOS_BitmapSet(UINT32*bitmap,UINT16pos){if(bitmap==NULL){return;}*bitmap|=1U<<(pos&OS_BITMAP_MASK);}2.2LOS_BitmapClr()清除状态字的某个标志位为0某个标志位字的清0,代码对应set操作,比较简单。~(1U<<(pos&OS_BITMAP_MASK))表示状态字中需要改变内容的位为0,其余位为1。然后设置状态字UINT32指定位的内容*bitmap通过按位与运算为0。VOIDLOS_BitmapClr(UINT32*bitmap,UINT16pos){if(bitmap==NULL){return;}*bitmap&=~(1U<<(pos&OS_BITMAP_MASK));}2.3LOS_HighBitGet()获取状态中最高位码为1的CLZword(bitmap)是一个宏,它扩展为(__builtin_clz(bitmap)),是编译器中用于高效位运算的内置库函数。clz是countleadingzeros的缩写,是统计二进制值中countleadingzeros的个数。.使用OS_BITMAP_MASK减去该值,结果是状态字中最高有效位1。UINT16LOS_HighBitGet(UINT32bitmap){if(bitmap==0){returnLOS_INVALID_BIT_INDEX;}return(OS_BITMAP_MASK-CLZ(bitmap));}2.4LOS_LowBitGet()获取状态字中1的最低位码其中,CTZ(bitmap)为一个宏,展开为(__builtin_ctz(value)),是编译器内置的高效位运算库函数。Ctz是counttrailingzeros的缩写,就是统计二进制值中低位区末尾的零个数。结果为状态字的最低位1。UINT16LOS_LowBitGet(UINT32bitmap){if(bitmap==0){returnLOS_INVALID_BIT_INDEX;}returnCTZ(bitmap);}2.5LOS_BitmapSetNBits()将状态字的连续标志位设置为1可以使用LOS_BitmapSetNBits()函数来设置状态字的连续位对于设置1操作,第一个参数为状态字UINT32*bitmap,其位内容需要改变,第二个参数为起始编号start需要设置为1的bit,第三个参数为需要设置为1的numsSet的个数。由于bit起始编号start不限于[0,31],所以UINT32*bitmapstatus后面的状态字word实际可以设置,需要根据实际业务情况设置,避免覆盖其他内存。同样,需要设置为1的数量numsSet也可能跨越多个状态字。如图:看代码,在(1)处计算需要操作的状态字,其中BITMAP_WORD(start)计算相对于状态字位图需要偏移的量,如果start是在[0,31]区间,BITMAP_WORD(start)等于0,操作的是状态字位图。如果start在区间[32,63],BITMAP_WORD(start)等于1,则操作是状态字位图后的第一个状态字,以此类推。(2)size可以结合bit的起始数来理解,size就是需要置1的结束bit的个数。⑶需要置1的bit的个数。(4)为需要置1的位对应的掩码。(5)如果满足条件,则表示置1的操作需要跨多个状态字进行操作,代码会处理一个状态处理下一个状态字之前的字。在⑩处,将当前状态字对应的位设置为1,然后执行⑺将已经设置为1的位减去剩余需要设置为1的位。⑻更新bitsToSet和maskToSet,然后指针p指向下一个状态字。如果⑼处需要置1的位数大于0,此时可以在一个状态字内完成运算,执行⑽计算需要置1的掩码,置1从起始位到结束位。⑾处的代码执行置1的操作,完成设置状态字连续标志位的操作。VOIDLOS_BitmapSetNBits(UINTPTR*bitmap,UINT32start,UINT32numsSet){⑴UINTPTR*p=bitmap+BITMAP_WORD(start);⑵constUINT32size=start+numsSet;⑶UINT16bitsToSet=BITMAP_BITS_PER_WORD-(start%BITMAP_BITS_PER_WORD);⑷UINTPTRmaskToSet=BITMAP_FIRST_WORD_MASK(start);⑸while(numsSet>bitsToSet){⑩*p|=maskToSet;⑺numsSet-=bitsToSet;⑻bitsToSet=BITMAP_BITS_PER_WORD;maskToSet=OS_BITMAP_WORD_MASK;p++;}⑼if(numsSet){⑽maskToSet&=BITMAP_LAST_WORD_MASK(size);*p|=maskToSet;}}2.6ClrNBits(连续ThecontinuousTheClrNBits(可以使用LOS_BitmapClrNBits()函数将状态字的连续位清0,将状态字的标志位清0,第一个参数为需要改变位内容的状态字UINT32*bitmap,第二个参数是start开始的需要清除的位数,第三个参数是需要清除的numsClear个数,这个函数是函数LOS_BitmapSetNBits()的逆操作,代码解释可以参考functi在LOS_BitmapSetNBits()上。VOIDLOS_BitmapClrNBits(UINTPTR*bitmap,UINT32start,UINT32numsClear){UINTPTR*p=位图+BITMAP_WORD(开始);constUINT32size=start+numsClear;UINT16bitsToClear=BITMAP_BITS_PER_WORD-(start%BITMAP_BITS_PER_WORD);UINTPTRnumskToClear=STBITMAP_ClearDIR(SK_STATOR_FIR);bitsToClear){*p&=~maskToClear;numsClear-=bitsToClear;bitsToClear=BITMAP_BITS_PER_WORD;maskToClear=OS_BITMAP_WORD_MASK;p++;}if(numsClear){maskToClear&=BITMAP_LAST_WORD_MASK(size);*p&=~maskToClear;}}2.7LOS_BitmapF的位从最低位开始的第一个0可以使用LOS_BitmapFfz()函数获取从最低位开始的第一个0的位数。第一个参数是需要改变位*bitmap内容的状态字UINT32,第二个参数numBits表示最大位数,限制返回值。必须在指定位数内找到符合条件的位数,否则返回-1。在看函数代码之前,先了解一下Ffz()函数,如下:调用内置函数__builtin_ffsl()获取一个unsignedlong类型数的二进制形式从左起第1位,该位为Counting从1开始,比如对于二进制数0110,函数返回2。在下面的函数中,传递给函数__builtin_ffsl()的参数取反,减1,所以Ffz()函数返回第一个0位左边一个数,这个数从0开始计数。/*findfirstzerobitstartingfromLSB*/STATICINLINEUINT16Ffz(UINTPTRx){return__builtin_ffsl(~x)-1;}让我们看一下函数LOS_BitmapFfz()的代码。⑴处,根据位数numBits计算对应状态字的个数,然后依次循环每个状态字。如果⑵处的状态字全为1,继续循环,否则执行⑶。执行到⑶可知,前面i个状态字的每一位全为1。i*BITMAP_BITS_PER_WORD+Ffz(bitmap[i])表示每个状态字的二进制位中从左到右第一个0的位置。(4)如果获取的位数小于第二个参数,则返回获取的位数,否则返回-1。如下图:源码如下:INT32LOS_BitmapFfz(UINTPTR*bitmap,UINT32numBits){INT32bit,i;⑴for(i=0;i
