从Linux BITMAP到索引池的实现

Linux代码中很多地方为了对某个资源进行标记使用了BITMAP方式,每个标记位只占用一个bit,如cpumask,它的定义在:

include\linux\bitops.h

#define BITS_PER_TYPE(type) (sizeof(type) * BITS_PER_BYTE)
#define BITS_TO_LONGS(nr)	DIV_ROUND_UP(nr, BITS_PER_TYPE(long))  //(nr, 4 * 8 = 32)

#include

#define DECLARE_BITMAP(name,bits) unsigned long name[BITS_TO_LONGS(bits)] // bits个位需要多少个long
typedef struct cpumask { DECLARE_BITMAP(bits, NR_CPUS); } cpumask_t; 
//定义一个结构体,成员位一个long数组,其包含的bit数大于NR_CPUS
static inline void cpumask_clear_cpu(int cpu, struct cpumask *dstp)

通过这些宏可以看出,所有的bit都是保存在一个数组中,通过BITS_TO_LONGS计算出数组的大小,随后通过一系列的宏和函数提供bit位的设置,检查和清除操作;如:

  • set_bit
  • clear_bit
  • test_bit

在实际的工作中,可能需要一个数据结构,它可以分配在一个连续范围内不重复的索引,要可以方便的进行分配、释放,快速检查某个索引是否被占用,这里我们可以借用Linux上面这段代码中的方法,定义如下数据结构:

索引池的管理结构:

typedef struct tagINDEX_POOL_S
{
    unsigned int   count;
    unsigned int   nr;
    unsigned long *bits;
}INDEX_POOL_T;

索引池初始化:

int index_pool_init(int count, INDEX_POOL_T* pstIndexPoolCtr)
{
    ... ...
    pstIndexPoolCtr->nr = (count + sizeof(unsigned long) * 8 - 1) / (sizeof(unsigned long) * 8);
    pstIndexPoolCtr->bits = (unsigned long *)malloc(nr * sizeof(unsigned long));
    pstIndexPoolCtr->count = count;
    return 0;
}

通常索引池需要提供以下操作,分配、释放和检查操作:

// 索引的分配
int indx_pool_alloc_index(unsigned int *index, INDEX_POOL_T* pstIndexPoolCtr)
{
	int i = 0;
	//有效性检查
	......
	for ( i = 0; i < pstIndexPoolCtr->nr; ++i)
	{
	    if (pstIndexPoolCtr->bits[i] != 0xffffffff)
		{
		    break;
		}
	}
	if (i ==  < pstIndexPoolCtr->nr)
	{
	    return -1;
	}
	for (int b = 0; b < sizeof(unsigned long) * 8; ++b)
	{
	     if ( (1 << b) & pstIndexPoolCtr->bits[i] == 0)
		 {
		     *index = i * sizeof(unsigned long) * 8 + b;
			 return 0;
		 }
	}
	return -1;
}

// 索引的释放
int indx_pool_free_index(unsigned int index, INDEX_POOL_T* pstIndexPoolCtr)
{
    ......
	loc = index / (sizeof(unsigned long) * 8);
	bits = index % (sizeof(unsigned long) * 8);
	......
	pstIndexPoolCtr->bits[loc] &= ~(1 << bits);
	......
}

// 索引的检查
int indx_pool_test_index(unsigned int index, INDEX_POOL_T* pstIndexPoolCtr)
{
    ......
	loc = index / (sizeof(unsigned long) * 8);
	bits = index % (sizeof(unsigned long) * 8);
	......
	return (pstIndexPoolCtr->bits[loc] & (1 << bits))
}

还可以这个索引池做进一步优化,如为了提高分配速度,可以加一个标记,标记最近释放的或者未分配的bit索引,这样就不需从pstIndexPoolCtr->bits的第一个开始遍历;还有如:要求索引池分配的索引不是从0开始,可以在控制字段中加一个字段表示偏移,如:

typedef struct tagINDEX_POOL_S
{
    unsigned int   count;
    unsigned int   nr;
    unsigned int   offset;  // 索引分配便宜,如从100开始分配,这个值在初始化是设置位100
    unsigned int   last_index; //初始值位0,如果bits的前n个ulong都为全f,则该值设置位n+1,下一次遍历从n+1开始
    unsigned long *bits;
}INDEX_POOL_T;

上面分配操作函数页需要做相应的修改,对入参index做相应的修正。

你可能感兴趣的:(C语言,Linux,学习小结)