版权声明:转载请声明 https://blog.csdn.net/qq_40732350/article/details/83342688
IDR机制在Linux内核中指的就是整数ID管理机制。从实质上来讲,这就是一种将一个整数ID号和一个指针关联在一起的机制。
1 IDR机制原理 ------------
IDR机制适用在那些需要把某个整数和特定指针关联在一起的地方。例如,在1IC总线中,每个设备都有自己的地址,要想在总线上找到特定的设备,就必须要先发送该设备的地址。当适配器要访问总线上的IIC设备时,首先要知道它们的ID号,同时要在内核中建立一个用于描述该设备的结构体和驱动程序。
怎么才能将该设备的ID号和它的设备结构体联系起来呢?
数组:进行索引,但如果ID号的范围很大(比如32位的ID号) ,则用数组索引会占据大量的内存空间,这显然不可能
链表:但如果总线中实际存在的设备较多,则链表的查询效率会很低。
这种情况下,就可以采用IDR机制,该机制内部采用红黑树(radix,类似于二分数)实现,可以很方便地将整数和指针关联起来,并且具有很高的搜索效率。IDR机制的主要代码在/include/linux/idr.h实现,下面对其主要函数进行说明。
2 结构体
struct idr {
struct idr_layer *top;
struct idr_layer *id_free;
int layers; /* only valid without concurrent changes */
int id_free_cnt;
spinlock_t lock;
};
#define IDR_INIT(name) \
{ \
.top = NULL, \
.id_free = NULL, \
.layers = 0, \
.id_free_cnt = 0, \
.lock = __SPIN_LOCK_UNLOCKED(name.lock), \
}
#define DEFINE_IDR(name) struct idr name = IDR_INIT(name) //定义一个idr结构体
3 初始化
void idr_init(struct idr *idp)
{
memset(idp, 0, sizeof(struct idr)); //初始化为0
spin_lock_init(&idp->lock); //初始化自旋锁
}
4 分配内存存放ID号的内存
每次通过IDR获得ID号之前,需要为ID号先分配内存。分配内存的函数是, idr_preget()。
成功1 ,错误0
int idr_pre_get(struct idr *idp, gfp_t gfp_mask)
{
while (idp->id_free_cnt < IDR_FREE_MAX) {
struct idr_layer *new;
new = kmem_cache_zalloc(idr_layer_cache, gfp_mask);
if (new == NULL)
return (0);
move_to_free_list(idp, new);
}
return 1;
}
该函数的第一个参数是指向IDR结构体的指针:第二个参数是内存分配标志,与 kmalloc()函数的标志相同。
5 分配ID号并将ID号和指针关联
int idr_get_new(struct idr *idp, void *ptr, int *id)
int idr_get_new_above(struct idr *idp, void *ptr, int starting_id, int *id)
- 参数idp是之前通过idr init初始化的idr指针,或者DEFINE IDR宏定义的IDR的指针。
- 参数ptr是和ID号相关联的指针。
- 参数id由内核自动分配的ID号。
- 参数start id是起始ID号。
内核在分配ID号时,会从start id开始。函数调用成功时返回0,如果没有ID可以分配,则返回负数,
6 通过ID号查询对应的指针
如果知道了ID号,需要查询对应的指针,可以使用idr find()函数。
void *idr_find(struct idr *idp, int id)
参数idp是之前通过idr init初始化的IDR指针,或者DEFINE IDR宏定义IDR的指针。
参数id是要查询的ID号。如果成功返回,则给定ID相关联的指针,如果没有,则返回NULL.
7 删除ID
void idr_remove(struct idr *idp, int id) //删除一个
void idr_remove_all(struct idr *idp) //删除所有
8 通过ID获得适配器指针
struct i2c_adapter *i2c_get_adapter(int id)
{
struct i2c_adapter *adapter;//适配器指针
mutex_lock(&core_lock);
adapter = idr_find(&i2c_adapter_idr, id);//查询适配器
if (adapter && !try_module_get(adapter->owner))
adapter = NULL;//适配器模块引用加一
mutex_unlock(&core_lock);
return adapter;
}
9 实例代码
retry:
if (idr_pre_get(&i2c_adapter_idr, GFP_KERNEL) == 0)
return -ENOMEM;//分配内存失败
mutex_lock(&core_lock);//上锁
/* 为适配器分配ID号,__i2c_first_dynamic_bus_num是动态分配的最小值 */
res = idr_get_new_above(&i2c_adapter_idr, adapter,
__i2c_first_dynamic_bus_num, &id);//id分配
mutex_unlock(&core_lock);//解锁
if (res < 0) {
if (res == -EAGAIN)
goto retry;
return res;
}