idr在linux内核中指的就是整数ID管理机制,从本质上来说,这就是一种将整数ID号和特定指针关联在一起的机制。本文将从如下几个方面进行内容的介绍:
- idr的使用场景
- idr的使用方法
- idr的使用示例
本文内容转载自网络,很不幸找不到原文了,就当作是自己的笔记吧。
1. idr的使用场景
idr机制适用在那些需要把某个整数和特定指针关联在一起的地方。举个例子,在I2C总线中,每个设备都有自己的地址,要想在总线上找到特定的设备,就必须要先发送该设备的地址。如果我们的PC是一个I2C总线上的主节点,那么要访问总线上的其他设备,首先要知道他们的ID号,同时要在pc的驱动程序中建立一个用于描述该设备的结构体。
此时,问题来了,我们怎么才能将这个设备的ID号和他的设备结构体联系起来呢?最简单的方法当然是通过数组进行索引,但如果ID号的范围很大(比如32位的ID号),则用数组索引显然不可能;第二种方法是用链表,但如果网络中实际存在的设备较多,则链表的查询效率会很低。遇到这种清况,我们就可以采用idr机制,该机制内部采用radix树实现,可以很方便地将整数和指针关联起来,并且具有很高的搜索效率。
2. idr的使用方法
2.1 获得idr
要在代码中使用idr,首先要包括<linux/idr.h>
。接下来,我们要在代码中分配idr
结构体,并初始化:
void idr_init(struct idr *idp)
;
其中idr定义如下:
1 2 3 4 5 6 7
| struct idr { struct idr_layer *top; struct idr_layer *id_free; int layers; int id_free_cnt; spinlock_t lock; };
|
idr
是idr机制的核心结构体。
2.2 为idr分配内存
int idr_pre_get(struct idr *idp, unsigned int gfp_mask);
每次通过idr获得ID号之前,需要先分配内存。
返回0表示错误,非零值代表正常
2.3 分配ID号并将ID号和指针关联
1 2
| int idr_get_new(struct idr *idp, void *ptr, int *id); int idr_get_new_above(struct idr *idp, void *ptr, int start_id, int *id);
|
idp: 之前通过idr_init初始化的idr指针
id: 由内核自动分配的ID号
ptr: 和ID号相关联的指针
start_id: 起始ID号。内核在分配ID号时,会从start_id开始。如果为I2C节点分配ID号,可以将设备地址作为start_id。
函数调用正常返回0,如果没有ID可以分配,则返回-ENOSPC
在实际中,上述函数常常采用如下方式使用:
1 2 3 4 5 6 7 8 9 10 11
| again: if (idr_pre_get(&my_idr, GFP_KERNEL) == 0) { } spin_lock(&my_lock); result = idr_get_new(&my_idr, &target, &id); if (result == -EAGAIN) { sigh(); spin_unlock(&my_lock); goto again; }
|
2.4 通过ID号搜索对应的指针
void *idr_find(struct idr *idp, int id);
返回值是和给定id相关联的指针,如果没有,则返回NULL
2.5 删除ID
要删除一个ID,使用:
void idr_remove(struct idr *idp, int id);
通过上面这些方法,内核代码可以为子设备,inode生成对应的ID号。
3. idr的使用示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
| <linux/idr.h> ... static DEFINE_IDR(i2c_adapter_idr); ...
int i2c_add_adapter(struct i2c_adapter *adapter) { int id, res = 0; retry: if (idr_pre_get(&i2c_adapter_idr, GFP_KERNEL) == 0) return -ENOMEM; mutex_lock(&core_lists); res = idr_get_new_above(&i2c_adapter_idr, adapter, __i2c_first_dynamic_bus_num, &id); mutex_unlock(&core_lists); if (res < 0) { if (res == -EAGAIN) goto retry; return res; } adapter->nr = id; return i2c_register_adapter(adapter); } EXPORT_SYMBOL(i2c_add_adapter);
int i2c_add_numbered_adapter(struct i2c_adapter *adap) { int id; int status; if (adap->nr & ~MAX_ID_MASK) return -EINVAL; retry: if (idr_pre_get(&i2c_adapter_idr, GFP_KERNEL) == 0) return -ENOMEM; mutex_lock(&core_lists);
status = idr_get_new_above(&i2c_adapter_idr, adap, adap->nr, &id); if (status == 0 && id != adap->nr) { status = -EBUSY; idr_remove(&i2c_adapter_idr, id); } mutex_unlock(&core_lists); if (status == -EAGAIN) goto retry; if (status == 0) status = i2c_register_adapter(adap); return status; } EXPORT_SYMBOL_GPL(i2c_add_numbered_adapter);
int i2c_del_adapter(struct i2c_adapter *adap) { ... idr_remove(&i2c_adapter_idr, adap->nr); ... return res; } EXPORT_SYMBOL(i2c_del_adapter);
struct i2c_adapter* i2c_get_adapter(int id) { struct i2c_adapter *adapter; mutex_lock(&core_lists); adapter = (struct i2c_adapter *)idr_find(&i2c_adapter_idr, id); if (adapter && !try_module_get(adapter->owner)) adapter = NULL; mutex_unlock(&core_lists); return adapter; } EXPORT_SYMBOL(i2c_get_adapter);
|
参考资料:
- ID Allocation