Linux内核bitmap组件详解
1. 引言
在Linux内核开发中,bitmap(位图)是一种极为常用的数据结构。它以紧凑的方式用一组二进制位(bit)来表示大量的布尔状态,广泛应用于资源分配、状态管理、权限控制等场景。Linux内核为bitmap的操作提供了高效、丰富的API,主要实现位于bitmap.c和bitmap.h。本文将系统性地介绍内核bitmap组件的功能、设计思想、核心实现、典型应用及其优化策略,帮助读者深入理解其原理与工程价值。记得刚参加工作那会,傻傻的在那里自己造轮子,果然开源linux才是最大的宝藏来源。
2. bitmap的基本概念与应用场景
2.1 基本概念
bitmap是一种用一组连续的bit位来表示集合元素状态的数据结构。每一位(bit)可以表示一个对象的“有/无”、“占用/空闲”、“允许/禁止”等二值状态。与数组、链表等结构相比,bitmap在存储空间和操作效率上具有显著优势,尤其适合大规模、稀疏、二值状态的场景。
2.2 典型应用场景
-
物理/虚拟内存页管理(页帧分配器)
-
进程PID分配
-
文件系统inode、block分配
-
设备资源分配(如PCI中断号、DMA通道)
-
权限掩码、状态标记
-
任务调度、CPU亲和性掩码
3. bitmap的内核实现结构
在内核中,bitmap通常用unsigned long
数组实现,每个unsigned long
包含若干bit(32位或64位,取决于平台)。头文件include/linux/bitmap.h
定义了相关类型和宏:
typedef unsigned long *bitmap_t;
#define BITS_PER_LONG (sizeof(unsigned long) * 8)
#define BITMAP_LAST_WORD_MASK(nbits) ...
-
BITS_TO_LONGS(bits) 计算需要多少个
unsigned long
才能容纳bits
个 bit。例如,100 位 bitmap 在 64 位系统上需要 2 个unsigned long
。
bitmap的分配有两种方式:
-
静态分配:如
DECLARE_BITMAP(name, nbits)
,适用于编译期已知大小的场景。 -
动态分配:如
bitmap_zalloc(nbits, gfp_flags)
,适用于运行时动态需求。
释放时使用bitmap_free()
。
4. bitmap 的核心 API 及实现
4.1 基本操作
4.1.1 bitmap_zero
将 bitmap 的所有位清零。
static inline void bitmap_zero(unsigned long *dst, unsigned int nbits)
{if (small_const_nbits(nbits))*dst = 0UL;else {memset(dst, 0, bitmap_size(nbits));}
}
- 对小型 bitmap 直接赋值,大型 bitmap 用
memset
。
4.1.2 bitmap_fill
将 bitmap 的所有位置为 1。
static inline void bitmap_fill(unsigned long *dst, unsigned int nbits)
{unsigned int nlongs = BITS_TO_LONGS(nbits);if (!small_const_nbits(nbits)) {unsigned int len = (nlongs - 1) * sizeof(unsigned long);memset(dst, 0xff, len);}dst[nlongs - 1] = BITMAP_LAST_WORD_MASK(nbits);
}
- 先将所有
unsigned long
填满,再处理最后一个 long 的多余位。
4.1.3 bitmap_set / bitmap_clear
批量置位或清零指定区间的 bit。
static inline void bitmap_set(unsigned long *map, unsigned int start, unsigned int nbits)
{if (__builtin_constant_p(nbits) && nbits == 1)__set_bit(start, map);else if (small_const_nbits(start + nbits))*map |= GENMASK(start + nbits - 1, start);else__bitmap_set(map, start, nbits);
}
- 对单个位或小范围优化,批量操作调用底层实现。
4.1.4 bitmap_and / bitmap_or / bitmap_equal
支持集合的与、或、等于等操作。
static inline bool bitmap_and(unsigned long *dst, const unsigned long *src1,const unsigned long *src2, unsigned int nbits)
{if (small_const_nbits(nbits))return (*dst = *src1 & *src2 & BITMAP_LAST_WORD_MASK(nbits)) != 0;return __bitmap_and(dst, src1, src2, nbits);
}
- 对小型 bitmap 直接操作,大型 bitmap 调用循环实现。
4.1.5 bitmap_weight
统计 bitmap 中 1 的个数(即集合大小)。
static inline unsigned int bitmap_weight(const unsigned long *src, unsigned int nbits)
{if (small_const_nbits(nbits))return hweight_long(*src & BITMAP_LAST_WORD_MASK(nbits));return __bitmap_weight(src, nbits);
}
4.1.6 bitmap_empty / bitmap_full
判断 bitmap 是否全为 0 或全为 1。
static inline bool bitmap_empty(const unsigned long *src, unsigned int nbits)
{if (small_const_nbits(nbits))return ! (*src & BITMAP_LAST_WORD_MASK(nbits));return find_first_bit(src, nbits) == nbits;
}
4.1.7 bitmap_alloc / bitmap_zalloc / bitmap_free
动态分配、清零分配和释放 bitmap。
static inline unsigned long *bitmap_alloc(unsigned int nbits, gfp_t flags)
{return malloc(bitmap_size(nbits));
}
static inline unsigned long *bitmap_zalloc(int nbits)
{return calloc(1, bitmap_size(nbits));
}
static inline void bitmap_free(unsigned long *bitmap)
{free(bitmap);
}
4.2 进阶操作
4.2.1 bitmap_scnprintf
将 bitmap 以字符串形式输出,便于调试和日志。
size_t bitmap_scnprintf(unsigned long *bitmap, unsigned int nbits,char *buf, size_t size);
- 输出如
"0-3,5,7-9"
,表示哪些 bit 被置位。
4.2.3 bitmap_intersects
判断两个 bitmap 是否有交集。
static inline bool bitmap_intersects(const unsigned long *src1,const unsigned long *src2,unsigned int nbits)
{if (small_const_nbits(nbits))return ((*src1 & *src2) & BITMAP_LAST_WORD_MASK(nbits)) != 0;elsereturn __bitmap_intersects(src1, src2, nbits);
}
5. bitmap 的性能优化
5.1 小型 bitmap 优化
-
对于 bit 数较少的 bitmap,直接用单个
unsigned long
操作,避免循环和函数调用。 -
利用编译器内建函数(如
__builtin_constant_p
)优化常量场景。
5.2 批量操作优化
-
对齐到字节/字长边界时,直接用
memset
、memcpy
批量处理。 -
对于大 bitmap,可用 SIMD 指令(如 AVX2/NEON)进一步加速。
5.3 查找优化
-
利用硬件指令(如 x86 的 BSF/POPCNT)加速查找第一个 1/0、统计 1 的个数。
-
对于稀疏 bitmap,可用跳表、哈希等高级结构进一步优化。
6. 总结与最佳实践
-
bitmap 是 Linux 内核和驱动开发中不可或缺的高效集合管理工具。
-
通过位操作、批量处理、查找优化等手段,bitmap 能高效支持大规模对象的分配、回收、集合运算等需求。
-
合理选择 bitmap 大小、对齐方式、操作 API,可进一步提升性能和可维护性。
-
在多核/并发场景下,注意使用原子位操作,避免竞态条件。
-
善用 bitmap_scnprintf、for_each_set_bit 等辅助工具,提升调试和开发效率。