ID 分配¶
- 作者:
Matthew Wilcox
概述¶
一個常見的待解決問題是分配識別符號 (ID);通常是用於標識某個事物的較小數字。例如檔案描述符、程序 ID、網路協議中的資料包識別符號、SCSI 標籤和裝置例項號。IDR 和 IDA 為此問題提供了合理的解決方案,避免了每個人都發明自己的方案。IDR 提供了將 ID 對映到指標的能力,而 IDA 僅提供 ID 分配,因此記憶體效率更高。
IDR 介面已棄用;請改用 XArray。
IDR 用法¶
首先初始化一個 IDR,可以透過 DEFINE_IDR() 用於靜態分配的 IDR,或者透過 idr_init() 用於動態分配的 IDR。
您可以呼叫 idr_alloc() 來分配一個未使用的 ID。透過呼叫 idr_find() 查詢與該 ID 關聯的指標,並透過呼叫 idr_remove() 釋放該 ID。
如果您需要更改與某個 ID 關聯的指標,可以呼叫 idr_replace()。這樣做的常見原因之一是透過向分配函式傳遞 NULL 指標來保留一個 ID;然後使用保留的 ID 初始化物件,最後將初始化後的物件插入到 IDR 中。
一些使用者需要分配大於 INT_MAX 的 ID。到目前為止,所有這些使用者都滿足於 UINT_MAX 的限制,他們使用 idr_alloc_u32()。如果您需要無法容納在 u32 中的 ID,我們將與您合作解決您的需求。
如果您需要按順序分配 ID,可以使用 idr_alloc_cyclic()。IDR 在處理較大 ID 時效率會降低,因此使用此函式會帶來輕微的開銷。
要對 IDR 使用的所有指標執行操作,可以使用基於回撥的 idr_for_each() 或迭代器風格的 idr_for_each_entry()。您可能需要使用 idr_for_each_entry_continue() 來繼續迭代。如果迭代器不符合您的需求,您也可以使用 idr_get_next()。
當您使用完一個 IDR 後,可以呼叫 idr_destroy() 來釋放 IDR 使用的記憶體。這不會釋放 IDR 指向的物件;如果您想這樣做,請使用其中一個迭代器來完成。
您可以使用 idr_is_empty() 來查明當前是否分配了任何 ID。
如果在從 IDR 分配新 ID 時需要加鎖,您可能需要傳遞一組限制性的 GFP 標誌,這可能導致 IDR 無法分配記憶體。為了解決這個問題,您可以在加鎖前呼叫 idr_preload(),然後在分配後呼叫 idr_preload_end()。
idr 同步(摘自 radix-tree.h)
idr_find() 能夠使用 RCU 無鎖呼叫。呼叫者必須確保在此函式呼叫是在 rcu_read_lock() 區域內進行的。其他讀者(無鎖或其他)和修改可能併發執行。
仍然要求呼叫者管理專案的同步和生命週期。因此,如果使用 RCU 無鎖查詢,通常這意味著專案有自己的鎖,或者適合無鎖訪問;並且專案由 RCU 釋放(或者僅在從 idr 樹中刪除後以及經過 synchronize_rcu() 寬限期後才釋放)。
IDA 用法¶
IDA 是一種 ID 分配器,它不提供將 ID 與指標關聯的能力。因此,它只需為每個 ID 儲存一位,從而比 IDR 更節省空間。要使用 IDA,請使用 DEFINE_IDA() 定義它(或將 struct ida 嵌入到資料結構中,然後使用 ida_init() 初始化)。要分配新 ID,請呼叫 ida_alloc()、ida_alloc_min()、ida_alloc_max() 或 ida_alloc_range()。要釋放 ID,請呼叫 ida_free()。
ida_destroy() 可用於處理 IDA,而無需釋放其中的單個 ID。您可以使用 ida_is_empty() 來查明 IDA 當前是否分配了任何 ID。
IDA 處理其自身的鎖。在您的程式碼中,安全地呼叫任何 IDA 函式都無需同步。
ID 目前限制在 [0-INT_MAX] 範圍內。如果這是一個不便的限制,那麼提高最大值應該相當簡單。
函式和結構¶
-
IDR_INIT¶
IDR_INIT (name)
初始化一個 IDR。
引數
名稱IDR 的名稱。
描述
一個新初始化的 IDR 不包含任何 ID。
-
DEFINE_IDR¶
DEFINE_IDR (name)
定義一個靜態分配的 IDR。
引數
名稱IDR 的名稱。
描述
使用此宏定義的 IDR 可直接使用,無需額外初始化。它不包含任何 ID。
引數
struct idr *idridr 控制代碼
unsigned int val新位置
描述
下次呼叫 idr_alloc_cyclic() 將返回 val,如果它空閒(否則搜尋將從該位置開始)。
引數
const struct idr *idrIDR 控制代碼。
返回
如果已從該 IDR 分配了任何 ID,則為 true。
-
void idr_preload_end(void)¶
結束由 idr_preload() 開始的預載入段
引數
void無引數
描述
每個 idr_preload() 都應該與此函式的呼叫相匹配。有關詳細資訊,請參閱 idr_preload()。
-
idr_for_each_entry¶
idr_for_each_entry (idr, entry, id)
迭代 IDR 中給定型別的元素。
引數
idrIDR 控制代碼。
entry用作遊標的型別 *
id條目 ID。
描述
entry 和 id 在迴圈之前無需初始化,正常終止後 entry 將保留 NULL 值。這對於表示“未找到”的值很方便。
-
idr_for_each_entry_ul¶
idr_for_each_entry_ul (idr, entry, tmp, id)
迭代 IDR 中給定型別的元素。
引數
idrIDR 控制代碼。
entry用作遊標的型別 *。
tmpID 的臨時佔位符。
id條目 ID。
描述
entry 和 id 在迴圈之前無需初始化,正常終止後 entry 將保留 NULL 值。這對於表示“未找到”的值很方便。
-
idr_for_each_entry_continue¶
idr_for_each_entry_continue (idr, entry, id)
繼續迭代 IDR 中給定型別的元素
引數
idrIDR 控制代碼。
entry用作遊標的型別 *。
id條目 ID。
描述
繼續迭代條目,從當前位置之後繼續。
-
idr_for_each_entry_continue_ul¶
idr_for_each_entry_continue_ul (idr, entry, tmp, id)
繼續迭代 IDR 中給定型別的元素
引數
idrIDR 控制代碼。
entry用作遊標的型別 *。
tmpID 的臨時佔位符。
id條目 ID。
描述
繼續迭代條目,從當前位置之後繼續。正常終止後,entry 將保留 NULL 值。這對於表示“未找到”的值很方便。
引數
struct ida *idaIDA 控制代碼。
gfp_t gfp記憶體分配標誌。
描述
分配一個介於 0 和 INT_MAX(含)之間的 ID。
上下文
任何上下文。在您的程式碼中,安全地呼叫此函式無需加鎖。
返回
已分配的 ID,如果無法分配記憶體則為 -ENOMEM,如果沒有空閒 ID 則為 -ENOSPC。
引數
struct ida *idaIDA 控制代碼。
unsigned int min要分配的最低 ID。
gfp_t gfp記憶體分配標誌。
描述
分配一個介於 min 和 INT_MAX(含)之間的 ID。
上下文
任何上下文。在您的程式碼中,安全地呼叫此函式無需加鎖。
返回
已分配的 ID,如果無法分配記憶體則為 -ENOMEM,如果沒有空閒 ID 則為 -ENOSPC。
引數
struct ida *idaIDA 控制代碼。
unsigned int max要分配的最高 ID。
gfp_t gfp記憶體分配標誌。
描述
分配一個介於 0 和 max(含)之間的 ID。
上下文
任何上下文。在您的程式碼中,安全地呼叫此函式無需加鎖。
返回
已分配的 ID,如果無法分配記憶體則為 -ENOMEM,如果沒有空閒 ID 則為 -ENOSPC。
引數
struct idr *idrIDR 控制代碼。
void *ptr與新 ID 關聯的指標。
u32 *nextid指向一個 ID 的指標。
unsigned long max要分配的最大 ID(含)。
gfp_t gfp記憶體分配標誌。
描述
在由 nextid 和 max 指定的範圍內分配一個未使用的 ID。請注意,max 是包含的,而 idr_alloc() 的 end 引數是排他的。新 ID 在指標插入 IDR 之前被分配給 nextid,因此如果 nextid 指向 ptr 所指向的物件,併發查詢將不會找到未初始化的 ID。
呼叫者應提供自己的鎖,以確保不可能對 IDR 進行兩次併發修改。對 IDR 的只讀訪問可以在 RCU 讀鎖下進行,或者可以排除同時寫入。
返回
如果分配了 ID 則為 0,如果記憶體分配失敗則為 -ENOMEM,如果找不到空閒 ID 則為 -ENOSPC。如果發生錯誤,nextid 不變。
引數
struct idr *idrIDR 控制代碼。
void *ptr與新 ID 關聯的指標。
int start最小 ID(含)。
int end最大 ID(不含)。
gfp_t gfp記憶體分配標誌。
描述
在由 start 和 end 指定的範圍內分配一個未使用的 ID。如果 end <= 0,則將其視為比 INT_MAX 大一。這允許呼叫者使用 start + N 作為 end,只要 N 在整數範圍內即可。
呼叫者應提供自己的鎖,以確保不可能對 IDR 進行兩次併發修改。對 IDR 的只讀訪問可以在 RCU 讀鎖下進行,或者可以排除同時寫入。
返回
新分配的 ID;如果記憶體分配失敗則為 -ENOMEM;如果找不到空閒 ID 則為 -ENOSPC。
引數
struct idr *idrIDR 控制代碼。
void *ptr與新 ID 關聯的指標。
int start最小 ID(含)。
int end最大 ID(不含)。
gfp_t gfp記憶體分配標誌。
描述
在由 start 和 end 指定的範圍內分配一個未使用的 ID。如果 end <= 0,則將其視為比 INT_MAX 大一。這允許呼叫者使用 start + N 作為 end,只要 N 在整數範圍內即可。對未使用 ID 的搜尋將從上次分配的 ID 開始,如果未在到達 end 之前找到空閒 ID,則將環繞到 start。
呼叫者應提供自己的鎖,以確保不可能對 IDR 進行兩次併發修改。對 IDR 的只讀訪問可以在 RCU 讀鎖下進行,或者可以排除同時寫入。
返回
新分配的 ID;如果記憶體分配失敗則為 -ENOMEM;如果找不到空閒 ID 則為 -ENOSPC。
引數
struct idr *idrIDR 控制代碼。
unsigned long id指標 ID。
描述
從 IDR 中移除此 ID。如果此 ID 之前不在 IDR 中,此函式返回 NULL。
由於此函式修改 IDR,呼叫者應提供自己的鎖以確保不可能對同一 IDR 進行併發修改。
返回
以前與此 ID 關聯的指標。
引數
const struct idr *idrIDR 控制代碼。
unsigned long id指標 ID。
描述
查詢與此 ID 關聯的指標。NULL 指標可能表示 id 未分配,或者 NULL 指標與此 ID 關聯。
在葉指標生命週期正確管理的情況下,此函式可以在 rcu_read_lock() 下呼叫。
返回
與此 ID 關聯的指標。
-
int idr_for_each(const struct idr *idr, int (*fn)(int id, void *p, void *data), void *data)¶
迭代所有儲存的指標。
引數
const struct idr *idrIDR 控制代碼。
int (*fn)(int id, void *p, void *data)為每個指標呼叫的函式。
void *data傳遞給回撥函式的資料。
描述
將為 idr 中的每個條目呼叫回撥函式,並傳遞 ID、條目和 data。
如果 fn 返回非 0 的值,則迭代停止,並從此函式返回該值。
如果受 RCU 保護,idr_for_each() 可以與 idr_alloc() 和 idr_remove() 併發呼叫。新新增的條目可能不會被看到,已刪除的條目可能會被看到,但新增和刪除條目不會導致其他條目被跳過,也不會導致虛假條目被看到。
引數
struct idr *idrIDR 控制代碼。
unsigned long *nextid指向一個 ID 的指標。
描述
返回樹中 ID 大於或等於 nextid 所指向的值的下一個已填充條目。退出時,nextid 將更新為找到的值的 ID。要在迴圈中使用,使用者必須遞增 nextid 所指向的值。
引數
struct idr *idrIDR 控制代碼。
int *nextid指向一個 ID 的指標。
描述
返回樹中 ID 大於或等於 nextid 所指向的值的下一個已填充條目。退出時,nextid 將更新為找到的值的 ID。要在迴圈中使用,使用者必須遞增 nextid 所指向的值。
引數
struct idr *idrIDR 控制代碼。
void *ptr與 ID 關聯的新指標。
unsigned long id要更改的 ID。
描述
替換已註冊 ID 的指標並返回舊值。此函式可以在 RCU 讀鎖下與 idr_alloc() 和 idr_remove() 併發呼叫(只要被移除的 ID 不是被替換的 ID!)。
返回
成功時的舊值。-ENOENT 表示未找到 id。-EINVAL 表示 ptr 無效。
引數
struct ida *idaIDA 控制代碼。
unsigned int min要分配的最低 ID。
unsigned int max要分配的最高 ID。
gfp_t gfp記憶體分配標誌。
描述
分配一個介於 min 和 max(含)之間的 ID。即使 max 更大,分配的 ID 也不會超過 INT_MAX。
上下文
任何上下文。在您的程式碼中,安全地呼叫此函式無需加鎖。
返回
已分配的 ID,如果無法分配記憶體則為 -ENOMEM,如果沒有空閒 ID 則為 -ENOSPC。
引數
struct ida *idaIDA 控制代碼。
unsigned int min要獲取的最低 ID。
unsigned int max要獲取的最高 ID。
描述
獲取介於 min 和 max(含)之間的最低已使用 ID。即使 max 更大,返回的 ID 也不會超過 INT_MAX。
上下文
任何上下文。獲取並釋放 xa_lock。
返回
最低使用的 ID,如果未找到使用的 ID 則返回 errno。
引數
struct ida *idaIDA 控制代碼。
unsigned int id先前分配的 ID。
上下文
任何上下文。在您的程式碼中,安全地呼叫此函式無需加鎖。
引數
struct ida *idaIDA 控制代碼。
描述
呼叫此函式將釋放所有 ID 並釋放 IDA 使用的所有資源。此呼叫返回後,IDA 將為空,可以重複使用或釋放。如果 IDA 已為空,則無需呼叫此函式。
上下文
任何上下文。在您的程式碼中,安全地呼叫此函式無需加鎖。