Linux中的IDR机制

版权声明:转载请声明 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)
  1. 参数idp是之前通过idr init初始化的idr指针,或者DEFINE IDR宏定义的IDR的指针。
  2. 参数ptr是和ID号相关联的指针。
  3. 参数id由内核自动分配的ID号。
  4. 参数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;
	}

猜你喜欢

转载自blog.csdn.net/qq_40732350/article/details/83342688
今日推荐