bitmap用于实现bool的数组,标识一个事件发生没发生。可以理解为bitmap处理的是有没有的问题。bitmap将一片连续的空间作为一个数据类型,其中的成员都是1位,长度是bitmap的容量。
声明一个 bitmap
DECLARE_BITMAP 宏
#define DECLARE_BITMAP(name,bits) \
unsigned long name[BITS_TO_LONGS(bits)]
#define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long))
#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))
#define BITS_PER_BYTE 8
根据上边的定义,可以解析成
- 在32位系统下:DECLARE_BITMAP(name, bits) == unsigned long name[(bits+31)/32]
- 在64位系统下:DECLARE_BITMAP(name, bits) == unsigned long name[(bits+63)/64]
声明一个保存数据类型是 unsigned long 类型的结构体,整个的数组成员,我们看做是保存了 bits 个位的容器。
参数:
- name,保存位的 unsigned long 类型的数据保存在内存中的首地址,或者是bitmap的首地址
- bits,我们要用到多少位,或者是bitmap中元素个数
假设我们要一个能存储32个bit的bitmap,我们只需要定义一个含一个 unsigned long 类型的数组就可以,即unsigned long name[1],我们声明的时候,只是简单的 DECLARE_BITMAP(xxx, 32);
bitmap_zero函数
用于初始化位图,将dst地址的n 位置为0(实际是按照unsigned long长度对齐的)
源码如下:
/*
*@dst: 位图的起始地址
*@nbits: 位图的个数
*/
static inline void bitmap_zero(unsigned long *dst, int nbits)
{
int len = BITS_TO_LONGS(nbits) * sizeof(unsigned long);
memset(dst, 0, len);
}
其中BITS_TO_LONGS(nbits)是计算nbits需要多少个long来表示,比如long为8字节,那么63bits也需要8个long来表示
bitmap_set函数
用于设置特定的位
1 /*
2 * @map: 位图的内存空间的首地址
3 * @start:需要设置的起始位
4 * @nr : 需要设置的位的数目
5 */
6 void bitmap_set(unsigned long *map, int start, int nr)
7 {
8 unsigned long *p = map + BIT_WORD(start); //以unsigned long 为单位的情况,设置起始位置
9 const int size = start + nr; //需要设置的末位,不包括此位
10 /*
11 * @bits_to_set 在一个需要设置的unsigned long中,可能需要设置的位
12 * [0_____start_________BITS_PER_LONG)
13 * [0____________________bits_to_set)
14 */
15 int bits_to_set = BITS_PER_LONG - (start % BITS_PER_LONG);
16 unsigned long mask_to_set = BITMAP_FIRST_WORD_MASK(start);
17
18 while (nr - bits_to_set >= 0) {
19 *p |= mask_to_set;
20 nr -= bits_to_set;
21 bits_to_set = BITS_PER_LONG; //经过上一步的操作后,需要设置的位都是BITS_PER_LONG的倍数
22 mask_to_set = ~0UL;
23 p++;
24 }
25 if (nr) { //nr 必须大于1,此函数才有效果
26 mask_to_set &= BITMAP_LAST_WORD_MASK(size);
27 *p |= mask_to_set; //产生最终的效果
28 }
29 }
与bit_set函数相对应的是bit_clear函数用于清除位,其实现原理和bit_set函数类似。
find_first_zero_bit函数
用于找到第一个清空位(也就是0位)
/*
* Find the first cleared bit in a memory region.
* @addr: 位图的起始位置
* @size: 位图的大小
*/
unsigned long find_first_zero_bit(const unsigned long *addr, unsigned long size)
{
const unsigned long *p = addr;
unsigned long result = 0;
unsigned long tmp;
while (size & ~(BITS_PER_LONG-1)) { //位图的大小超过一个 BIT_PER_LONG的情况下
if (~(tmp = *(p++)))
goto found;
result += BITS_PER_LONG;
size -= BITS_PER_LONG;
}
if (!size) //此情况成立的时候,也是没有找到。且size应该是 BIT_PER_LONG的整数倍。
return result;
tmp = (*p) | (~0UL << size);
if (tmp == ~0UL) /* Are any bits zero? */
return result + size; /* Nope. */ //如果没有找到就会返回 size
found:
return result + ffz(tmp);
}
与此函数对应的是函数find_first_bit寻找第一个设置位。
find_next_zero_bit 和 find_next_bit 函数
可以在第一次找到后,再次寻找清空位或设置位。