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");

posted on 2025-11-14 16:22  slgkaifa  阅读(0)  评论(0)    收藏  举报

导航