Linux内核idr数据结构使用
Linux 内核的 idr
(ID 映射)是一种用于将整数 ID 与指针(内核对象)关联的数据结构,核心功能是高效分配、查找、释放 ID,并通过 ID 快速定位对应的内核对象(如设备、会话、缓冲区等)。以下是其详细用法:
一、核心概念
idr
解决的问题:当需要用唯一整数 ID 标识动态创建的内核对象(如多个网卡实例、进程私有资源)时,idr
可自动分配不重复的 ID,并建立 ID 到对象指针的映射,方便通过 ID 快速访问对象。
二、使用步骤
1. 头文件与数据结构
需包含头文件 <linux/idr.h>
,核心结构体为 struct idr
:
#include <linux/idr.h>struct idr my_idr; // 定义一个 idr 实例
2. 初始化与销毁
- 初始化:使用
idr_init()
初始化idr
结构(必须在使用前调用)。 - 销毁:使用
idr_destroy()
释放资源(需确保所有 ID 已释放)。
// 初始化
idr_init(&my_idr);// 销毁(通常在模块退出时)
idr_destroy(&my_idr);
3. ID 分配(关联对象)
idr
提供多种分配方式,分配时需指定要关联的对象指针:
函数原型 | 功能描述 |
---|---|
int idr_alloc(struct idr *idr, void *ptr, int start, int end, gfp_t gfp) | 从 [start, end) 范围内分配最小可用 ID,关联到 ptr (start=0 表示从 0 开始,end=0 表示无上限)。 |
int idr_alloc_cyclic(struct idr *idr, void *ptr, int start, int end, gfp_t gfp) | 循环分配 ID(超过 end 后从 start 重新开始),适合需要循环利用 ID 的场景。 |
- 参数说明:
ptr
:要关联的内核对象指针(如struct my_device *
)。gfp
:内存分配标志(GFP_KERNEL
允许睡眠,GFP_ATOMIC
用于中断上下文)。- 返回值:成功返回分配的 ID(非负整数),失败返回负数(
-ENOMEM
内存不足,-ENOSPC
无可用 ID)。
4. 通过 ID 查找对象
使用 idr_find()
根据 ID 查找关联的对象指针,支持无锁并发读(依赖 RCU 机制):
void *idr_find(const struct idr *idr, unsigned int id);
- 返回值:成功返回关联的指针,失败返回
NULL
(ID 未分配或已释放)。
5. 释放 ID 与解除关联
使用 idr_remove()
释放指定 ID,并解除与对象的关联(对象需手动释放):
void idr_remove(struct idr *idr, unsigned int id);
6. 迭代所有 ID 与对象
使用 idr_for_each()
遍历所有已分配的 ID 及其关联对象:
int idr_for_each(struct idr *idr, int (*fn)(int id, void *p, void *data), void *data);
fn
:回调函数,每个 ID 会触发一次调用(id
为当前 ID,p
为关联的指针,data
为用户传入的参数)。- 返回值:回调函数返回非 0 时终止遍历,否则返回 0。
三、并发安全处理
idr
内部通过 RCU 和自旋锁保证并发安全:
- 读操作(
idr_find
):需在rcu_read_lock()
/rcu_read_unlock()
保护下执行(无锁,高效)。 - 写操作(分配 / 释放):需通过自旋锁保护(避免并发修改)。
#include <linux/spinlock.h>
#include <linux/rculist.h>static DEFINE_SPINLOCK(idr_lock); // 保护写操作的自旋锁// 分配 ID 并关联对象(带锁)
int alloc_id_and_obj(struct my_obj *obj) {int id;unsigned long flags;spin_lock_irqsave(&idr_lock, flags); // 加锁(禁止中断)id = idr_alloc(&my_idr, obj, 1, 0, GFP_ATOMIC); // 从 ID=1 开始分配spin_unlock_irqrestore(&idr_lock, flags); // 解锁return id;
}// 通过 ID 查找对象(无锁读)
struct my_obj *find_obj_by_id(int id) {struct my_obj *obj;rcu_read_lock(); // 进入 RCU 读临界区obj = idr_find(&my_idr, id);rcu_read_unlock(); // 退出读临界区return obj;
}// 释放 ID 与对象(带锁)
void free_id_and_obj(int id) {struct my_obj *obj;unsigned long flags;spin_lock_irqsave(&idr_lock, flags);obj = idr_find(&my_idr, id); // 先查找对象if (obj) {idr_remove(&my_idr, id); // 释放 IDkfree(obj); // 手动释放对象(idr 不管理对象内存)}spin_unlock_irqrestore(&idr_lock, flags);
}
四、完整示例(模块中使用)
#include <linux/module.h>
#include <linux/idr.h>
#include <linux/spinlock.h>
#include <linux/slab.h>// 定义需要用 ID 标识的对象
struct my_obj {int value;
};static struct idr my_idr;
static DEFINE_SPINLOCK(idr_lock);// 遍历回调函数:打印 ID 和对象值
static int print_id_obj(int id, void *p, void *data) {struct my_obj *obj = p;printk("ID: %d, value: %d\n", id, obj->value);return 0; // 继续遍历
}static int __init idr_demo_init(void) {int id1, id2;struct my_obj *obj1, *obj2;// 初始化 IDRidr_init(&my_idr);// 创建对象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(&idr_lock);id1 = idr_alloc(&my_idr, obj1, 1, 0, GFP_ATOMIC);id2 = idr_alloc(&my_idr, obj2, 1, 0, GFP_ATOMIC);spin_unlock_irq(&idr_lock);printk("Allocated IDs: %d, %d\n", id1, id2);// 遍历所有 ID 和对象idr_for_each(&my_idr, print_id_obj, NULL);return 0;
}static void __exit idr_demo_exit(void) {// 释放 ID 和对象(简化示例,实际需保存 id1、id2)spin_lock_irq(&idr_lock);idr_remove(&my_idr, id1);idr_remove(&my_idr, id2);kfree(idr_find(&my_idr, id1)); // 假设退出前仍能找到对象kfree(idr_find(&my_idr, id2));spin_unlock_irq(&idr_lock);// 销毁 IDRidr_destroy(&my_idr);printk("IDR demo exited\n");
}module_init(idr_demo_init);
module_exit(idr_demo_exit);
MODULE_LICENSE("GPL");