Linux内核xarray数据结构采用
Linux 内核的 xarray(eXtensible Array)是一种灵活、高性能的键值存储数据结构,核心功能是将 64 位整数 ID 与指针(内核对象)关联,支持 ID 分配、查找、插入、删除等操作,适用于需要管理大范围 ID 映射的场景(如进程 ID、文件句柄、设备资源等)。以下是其详细用法:
一、核心特性
- ID 范围:支持全 64 位整数 ID(
-2^63 ~ 2^63-1),包括负数、零和大整数。 - 功能丰富:支持自动分配 ID、手动指定 ID 插入、范围操作(批量删除)、原子操作等。
- 并发安全:通过 RCU 机制支持无锁读操作,写操作需配合锁(如自旋锁)保证线程安全。
- 高效性能:基于优化的树结构实现,查找、插入、删除的时间复杂度接近 O (log n),适合大规模数据。
二、使用步骤
1. 头文件与数据结构
需包含头文件 <linux/xarray.h>,核心结构体为 struct xarray:
#include
struct xarray my_xa; // 定义一个 xarray 实例
2. 初始化与销毁
- 初始化:使用
xa_init()初始化xarray(必须在使用前调用)。 - 销毁:使用
xa_destroy()释放资源(需确保所有元素已删除)。
// 初始化
xa_init(&my_xa);
// 销毁(模块退出或不再使用时)
xa_destroy(&my_xa);
3. 核心操作 API
(1)ID 分配(自动分配唯一ID 并关联指针)
自动分配一个未使用的 ID,并将其与指针关联:
// 从 [min_id, max_id) 范围内分配最小可用 ID,关联到 ptr
int xa_alloc(struct xarray *xa, void *ptr, unsigned long min_id, unsigned long max_id, gfp_t gfp);
// 循环分配(超过 max_id 后从 min_id 重新开始)
int xa_alloc_cyclic(struct xarray *xa, void *ptr, unsigned long min_id, unsigned long max_id, gfp_t gfp);
- 参数:
min_id/max_id:ID 范围(max_id=0表示无上限)。ptr:要关联的内核对象指针(如struct my_obj *)。gfp:内存分配标志(GFP_KERNEL允许睡眠,GFP_ATOMIC用于中断上下文)。
- 返回值:成功返回分配的 ID(非负整数),失败返回负数(
-ENOMEM内存不足,-ENOSPC无可用 ID)。
(2)手动插入(指定 ID 关联指针)
直接指定 ID 并关联指针(若 ID 已被使用,返回错误):
int xa_store(struct xarray *xa, unsigned long id, void *ptr, gfp_t gfp);
- 返回值:成功返回 0,失败返回
-ENOMEM(内存不足)或其他错误。 - 示例:将 ID=100 与
obj关联:
struct my_obj *obj = kzalloc(sizeof(*obj), GFP_KERNEL);
int ret = xa_store(&my_xa, 100, obj, GFP_KERNEL);
(3)查找(通过 ID 获取关联的指针)
根据 ID 查找对应的指针,支持无锁并发读(依赖 RCU):
void *xa_load(const struct xarray *xa, unsigned long id);
- 返回值:成功返回关联的指针,失败返回
NULL(ID 未分配或已删除)。 - 示例(RCU 保护的无锁读):
struct my_obj *obj;
rcu_read_lock(); // 进入 RCU 读临界区
obj = xa_load(&my_xa, 100); // 查找 ID=100 关联的对象
rcu_read_unlock(); // 退出读临界区
(4)删除(释放 ID 并解除关联)
删除指定 ID 及其关联的指针(指针指向的对象需手动释放):
void *xa_erase(struct xarray *xa, unsigned long id);
- 返回值:返回被删除的指针(可用于手动释放对象),若 ID 不存在返回
NULL。 - 示例:
struct my_obj *obj = xa_erase(&my_xa, 100); // 删除 ID=100
kfree(obj); // 释放对象内存
(5)范围操作(批量删除一个区间的 ID)
删除 [start, end) 范围内的所有 ID:
void xa_erase_range(struct xarray *xa, unsigned long start, unsigned long end);
示例:删除 ID=100 到 ID=200(不含 200)的所有条目:
xa_erase_range(&my_xa, 100, 200);
(6)遍历所有 ID 与指针
使用 xa_for_each() 遍历所有已分配的 ID 及其关联的指针:
// 回调函数:每个 ID 触发一次调用(id 为当前 ID,p 为关联的指针,data 为用户数据)
typedef int (*xa_visitor_t)(unsigned long id, void *p, void *data);
// 遍历所有 ID,调用 visitor 回调
int xa_for_each(struct xarray *xa, xa_visitor_t visitor, void *data);
示例:遍历并打印所有 ID 和对象值:
static int print_xa_entry(unsigned long id, void *p, void *data) {
struct my_obj *obj = p;
printk("ID: %lu, value: %d\n", id, obj->value);
return 0; // 返回 0 继续遍历,非 0 终止
}
xa_for_each(&my_xa, print_xa_entry, NULL); // 启动遍历
三、并发安全处理
xarray 依赖 RCU 和锁机制保证并发安全:
- 读操作(
xa_load):需在rcu_read_lock()/rcu_read_unlock()保护下执行(无锁,高效)。 - 写操作(分配 / 插入 / 删除):需通过自旋锁保护(避免并发修改导致数据不一致)。
示例:
#include
static DEFINE_SPINLOCK(xa_lock); // 保护写操作的自旋锁
// 安全分配 ID
int safe_alloc_xa(struct my_obj *obj) {
int id;
unsigned long flags;
spin_lock_irqsave(&xa_lock, flags); // 加锁(禁止中断)
id = xa_alloc(&my_xa, obj, 1, 0, GFP_ATOMIC); // 从 ID=1 开始分配
spin_unlock_irqrestore(&xa_lock, flags); // 解锁
return id;
}
// 安全删除 ID
void safe_erase_xa(unsigned long id) {
struct my_obj *obj;
unsigned long flags;
spin_lock_irqsave(&xa_lock, flags);
obj = xa_erase(&my_xa, id); // 删除 ID
spin_unlock_irqrestore(&xa_lock, flags);
kfree(obj); // 释放对象(在锁外执行,避免长时间持有锁)
}
四、完整示例(模块中使用)
#include
#include
#include
#include
// 定义要关联的对象
struct my_obj {
int value;
};
static struct xarray my_xa;
static DEFINE_SPINLOCK(xa_lock);
static int __init xa_demo_init(void) {
int id1, id2;
struct my_obj *obj1, *obj2;
struct my_obj *found_obj;
// 初始化 xarray
xa_init(&my_xa);
// 创建对象
obj1 = kzalloc(sizeof(*obj1), GFP_KERNEL);
obj2 = kzalloc(sizeof(*obj2), GFP_KERNEL);
if (!obj1 || !obj2)
return -ENOMEM;
obj1->value = 100;
obj2->value = 200;
// 自动分配 ID 并关联对象
spin_lock_irq(&xa_lock);
id1 = xa_alloc(&my_xa, obj1, 10, 0, GFP_ATOMIC); // 从 ID=10 开始分配
id2 = xa_alloc(&my_xa, obj2, 10, 0, GFP_ATOMIC);
spin_unlock_irq(&xa_lock);
printk("Allocated IDs: %d, %d\n", id1, id2);
// 手动插入 ID=100
spin_lock_irq(&xa_lock);
xa_store(&my_xa, 100, obj1, GFP_ATOMIC); // ID=100 关联 obj1
spin_unlock_irq(&xa_lock);
// 查找 ID=100 关联的对象
rcu_read_lock();
found_obj = xa_load(&my_xa, 100);
if (found_obj)
printk("Found ID=100, value: %d\n", found_obj->value);
rcu_read_unlock();
// 遍历所有条目
xa_for_each(&my_xa, print_xa_entry, NULL);
return 0;
}
static void __exit xa_demo_exit(void) {
// 释放所有 ID 和对象(简化示例,实际需遍历释放)
spin_lock_irq(&xa_lock);
xa_erase(&my_xa, id1);
xa_erase(&my_xa, id2);
xa_erase(&my_xa, 100);
spin_unlock_irq(&xa_lock);
// 销毁 xarray
xa_destroy(&my_xa);
printk("XArray demo exited\n");
}
module_init(xa_demo_init);
module_exit(xa_demo_exit);
MODULE_LICENSE("GPL");
浙公网安备 33010602011771号