在驱动中,有时候我们会使用位图的方式去管理资源,这种方式比较简单易懂。在对效率没有特别要求的情况下,采用位图不失为一种好的选择。
其实在内核源码中也是有很多地方使用位图的,比如cpumask就是其中之一:
typedef struct cpumask { DECLARE_BITMAP(bits, NR_CPUS); } cpumask_t;
简单点理解位图管理“资源”就是0表示“资源”可用,1表示"资源"已经被占用,浅显易懂。当然内核中已经为我们提供了很多位图操作的API,我们只要充分发扬“拿来主义”精神即可。
在驱动中使用内核提供的位图很简单,定义的接口如下
#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空间的起始地址
在讲位图操作之前,我们把内核中几个常用的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;
}
另有三个带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)));
}
关于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))
在开篇的时候说到,位图可以作为管理资源的一种手段,下面举个例子:
#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