Linux内核数据结构-BITMAP

Linux内核数据结构-BITMAP

1.概述

在驱动中,有时候我们会使用位图的方式去管理资源,这种方式比较简单易懂。在对效率没有特别要求的情况下,采用位图不失为一种好的选择。

其实在内核源码中也是有很多地方使用位图的,比如cpumask就是其中之一:

typedef struct cpumask { DECLARE_BITMAP(bits, NR_CPUS); } cpumask_t;

简单点理解位图管理“资源”就是0表示“资源”可用,1表示"资源"已经被占用,浅显易懂。当然内核中已经为我们提供了很多位图操作的API,我们只要充分发扬“拿来主义”精神即可。

2.位图定义

在驱动中使用内核提供的位图很简单,定义的接口如下

#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))

实际上就是利用DECLARE_BITMAP(name,bits)宏定义了一个unsigned long的数组,bits代表所需位图的bit个数,name是数组名,也代表bit空间的起始地址

3.位图操作

在讲位图操作之前,我们把内核中几个常用的bit操作函数说明下:

3.1 bit位操作
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;
}

static inline void __clear_bit(int nr, volatile unsigned long *addr)
{
	unsigned long mask = BIT_MASK(nr);
	unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr);

	*p &= ~mask;
}
static inline void __change_bit(int nr, volatile unsigned long *addr)
{
	unsigned long mask = BIT_MASK(nr);
	unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr);

	*p ^= mask;
}
  • set_bit用于在bitmap中将相应的bit位置1,其中nr,是bit index 属于[0,bits-1]
  • clear_bit 与set_bit相反,它将相应位置的bit清除位0,参数定义与set_bit一致
  • change_bit将相应位置的值取反,0->1,1->0

另有三个带test的对应的函数:test_and_set_bit;test_and_clear_bit;test_and_change_bit,多了返回old值的功能

另外还有 test_bit函数用于测试对应位置是否置位

static inline int test_bit(int nr, const volatile unsigned long *addr)
{
	return 1UL & (addr[BIT_WORD(nr)] >> (nr & (BITS_PER_LONG-1)));
}
3.2 bit位遍历

关于bit位的遍历有以下的接口

find_first_zero_bit(p,sz)		寻找bitmap中第一个为0的位序,并返回
find_next_zero_bit(p,sz,off)	从offset开始,寻找bitmap中第一个为0的位序,并返回
find_first_bit(p,sz)		  	寻找bitmap中第一个为1的位序,并返回
find_next_bit(p,sz,off)			从offset开始,寻找bitmap中第一个为0的位序,并返回

#define for_each_set_bit(bit, addr, size) \
	for ((bit) = find_first_bit((addr), (size));		\
	     (bit) < (size);					\
	     (bit) = find_next_bit((addr), (size), (bit) + 1))

/* same as for_each_set_bit() but use bit as value to start with */
#define for_each_set_bit_from(bit, addr, size) \
	for ((bit) = find_next_bit((addr), (size), (bit));	\
	     (bit) < (size);					\
	     (bit) = find_next_bit((addr), (size), (bit) + 1))

#define for_each_clear_bit(bit, addr, size) \
	for ((bit) = find_first_zero_bit((addr), (size));	\
	     (bit) < (size);					\
	     (bit) = find_next_zero_bit((addr), (size), (bit) + 1))

/* same as for_each_clear_bit() but use bit as value to start with */
#define for_each_clear_bit_from(bit, addr, size) \
	for ((bit) = find_next_zero_bit((addr), (size), (bit));	\
	     (bit) < (size);					\
	     (bit) = find_next_zero_bit((addr), (size), (bit) + 1))
  • for_each_set_bit:从0位序开始遍历到size-1,返回每一个置位bit的位序
  • for_each_set_bit_from:同上面函数基本相同,只是增了一个起始位,从特定位置开始查找返回
  • for_each_clear_bit:从0位序开始遍历,返回每一个为0bit的位序
  • for_each_clear_bit_from:同上一致,也是从特定位置开始查找返回为 0 bit 的位序

4.位图的应用

在开篇的时候说到,位图可以作为管理资源的一种手段,下面举个例子:

#include 
#include 

typedef   unsigned short uint16;
typedef   unsigned int   uint32;

#define BITMAP_LEN 128

DECLARE_BITMAP(Test, BITMAP_LEN);

static ssize_t bitmap_debug_get_usable_index(struct device *dev,
					 struct device_attribute *attr,
					 char *buf)
{
	uint16 index =0;
			
	index = find_first_zero_bit(Test,128);	

	printk("\n   index:%d\r\n",index);
	
	return 0;
					 
}

static ssize_t bitmap_debug_set_used_index(struct device *dev,
					  struct device_attribute *attr,
					  const char *buf, size_t count)
{
	uint32 start = 0;
	uint32 end   = 0;
	uint32 index = 0;
	uint32 old   = 0;
	
	sscanf(buf, "%u%u", &start, &end);
	
	if( start > end) printk("index start input error\n\r");
	
	for(index = start; index <= end; index++) old = test_and_set_bit(index, Test);
			
	
	return count;
}

static	DEVICE_ATTR(get_index, S_IRUSR, bitmap_debug_get_usable_index,NULL);
static  DEVICE_ATTR(set_index, S_IWUSR, NULL,bitmap_debug_set_used_index);

struct attribute *bitmap_attrs[] = {
	
	&dev_attr_get_index.attr,
	&dev_attr_set_index.attr,
	
	NULL,
};

struct attribute_group bitmap_group = {
	.name  	= "bitmap",
	.attrs  = bitmap_attrs,
};

上面的代码中定义了一个长度为128 bitmap,代表128份的资源,1表示不可用,0表示可用,利用sysfs添加了bitmap的调试接口,结果如下:

# cd /sys/devices/platform/debug/bitmap/
# cat get_index 

   index:0


# echo 0 100 > set_index 
# cat get_index 

   index:101

你可能感兴趣的:(Linux,Driver,and,Kernel)