idr在linux內核中指的就是整數ID管理機制,從本質上來說,這就是一種將整數ID號和特定指針關聯在一起的機制。這個機制最早是在2003年2月加入內核的,當時是作為POSIX定時器的一個補丁。現在,在內核的很多地方都可以找到idr的身影。 idr機制適用在那些需要把某個整數和特定指針關聯在一起的地 ...
idr在linux內核中指的就是整數ID管理機制,從本質上來說,這就是一種將整數ID號和特定指針關聯在一起的機制。這個機制最早是在2003年2月加入內核的,當時是作為POSIX定時器的一個補丁。現在,在內核的很多地方都可以找到idr的身影。 idr機制適用在那些需要把某個整數和特定指針關聯在一起的地方。舉個例子,在I2C匯流排中,每個設備都有自己的地址,要想在匯流排上找到特定的設備,就必須要先發送該設備的地址。如果我們的PC是一個I2C匯流排上的主節點,那麼要訪問匯流排上的其他設備,首先要知道他們的ID號,同時要在pc的驅動程式中建立一個用於描述該設備的結構體。 此時,問題來了,我們怎麼才能將這個設備的ID號和他的設備結構體聯繫起來呢?最簡單的方法當然是通過數組進行索引,但如果ID號的範圍很大(比如32位的ID號),則用數組索引顯然不可能;第二種方法是用鏈表,但如果網路中實際存在的設備較多,則鏈表的查詢效率會很低。遇到這種清況,我們就可以採用idr機制,該機制內部採用radix樹實現,可以很方便地將整數和指針關聯起來,並且具有很高的搜索效率。http://hovertree.com/menu/linux/ (1)獲得idr
要在代碼中使用idr,首先要包括<linux/idr.h>。接下來,我們要在代碼中分配idr結構體,並初始化:
void idr_init(struct idr *idp);
其中idr定義如下:
struct idr { struct idr_layer *top; struct idr_layer *id_free; int layers; int id_free_cnt; spinlock_t lock; }; /* idr是idr機制的核心結構體 何問起 hovertree.com */(2)為idr分配記憶體
int idr_pre_get(struct idr *idp, unsigned int gfp_mask);
每次通過idr獲得ID號之前,需要先分配記憶體。
返回0表示錯誤,非零值代表正常 (3)分配ID號並將ID號和指針關聯
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 在實際中,上述函數常常採用如下方式使用:
again: if (idr_pre_get(&my_idr, GFP_KERNEL) == 0) { /* No memory, give up entirely */ } spin_lock(&my_lock); result = idr_get_new(&my_idr, &target, &id); if (result == -EAGAIN) { sigh(); spin_unlock(&my_lock); goto again; }/* 何問起 hovertree.com */(4)通過ID號搜索對應的指針
void *idr_find(struct idr *idp, int id);
返回值是和給定id相關聯的指針,如果沒有,則返回NULL (5)刪除ID
要刪除一個ID,使用:
void idr_remove(struct idr *idp, int id); 通過上面這些方法,內核代碼可以為子設備,inode生成對應的ID號。這些函數都定義在<linux-2.6.xx/lib/idr.c>中
下麵,我們通過分析I2C協議的核心代碼,來看一看idr機制的實際應用:
<linux-2.6.23/drivers/i2c/i2c-core.c>
...
<linux/idr.h> /* idr頭文件 */
...
static DEFINE_IDR(i2c_adapter_idr); /* 聲明idr */
... /*
採用動態匯流排號聲明並註冊一個i2c適配器(adapter),可睡眠
針對匯流排號可動態指定的設備,如基於USB的i2c設備或pci卡
*/
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);
/* __i2c_first_dynamic_bus_num是當前系統允許的動態匯流排號的最大值 */
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);
/*
採用靜態匯流排號聲明並註冊一個i2c適配器(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);
/* "above" here means "above or equal to", sigh;
* we need the "equal to" result to force the result
*/
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);
/* 註銷一個i2c適配器 */
int i2c_del_adapter(struct i2c_adapter *adap)
{
...
/* free bus id */
idr_remove(&i2c_adapter_idr, adap->nr);
...
return res;
}
EXPORT_SYMBOL(i2c_del_adapter);
/* 通過ID號獲得i2c_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); 推薦:http://www.cnblogs.com/roucheng/p/3470287.html