博主按:下文原作者在linux2.4.0基礎上分析的,我現在的核心是2.6.32。在有區別的地方我會用紅色文字標出,作為對原文的一些補充吧。
3.2 linux對I/O資源的管理
linux是以一種倒置的樹形結構來管理每一類I/O資源(如:I/O連接埠、外設記憶體、DMA和IRQ)的。每一類I/O資源都對應有一顆倒置的資源樹,樹中的每一個節點都是一個resource結構,而樹的根結點root則描述了該類資源的整個資源空間。
基於上述這個思想,linux在kernel/Resource.c檔案中實現了對資源的申請、釋放及尋找等操作。
3.2.1 I/O資源的申請
假設某類資源有如下這樣一顆資源樹:
節點root、r1、r2和r3實際上都是一個resource結構類型。子資源r1、r2和r3通過sibling指標連結成一條單向非迴圈鏈表,
其表頭由root節點中的child指標定義,因此也稱為父資源的子資源鏈表。r1、r2和r3的parent指標均指向他們的父資源節點,在這裡也就是
圖中的root節點。
假設想在root節點中分配一段I/O資源(由圖中的陰影地區表示)。函數request_resource()實現這一功能。它有兩個參
數:①root指標,表示要在哪個資源根節點中進行分配;②new指標,指向描述所要分配的資源(即圖中的陰影地區)的resource結構。該函數的源
代碼如下(kernel/resource.c):
int request_resource(struct resource *root, struct resource
*new)
{
struct resource *conflict;
write_lock(&resource_lock);
conflict = __request_resource(root, new);
write_unlock(&resource_lock);
return conflict ? -EBUSY : 0;
}
對上述函數的NOTE如下:
①資源鎖resource_lock對所有資源樹進行讀防寫保護,任何程式碼片段在訪問某一顆資源樹之前都必須先持有該鎖。其定義如下(kernel/Resource.c):
static rwlock_t resource_lock = RW_LOCK_UNLOCKED;
2.6.32變成了這句static DEFINE_RWLOCK(resource_lock);
②可以看出,函數實際上是通過調用內部靜態函數__request_resource()來完成實際的資源分派工作。如果該函數返回非null 指標,則表示有資源衝突;否則,返回NULL就表示分配成功。
③最後,如果conflict指標為NULL,則request_resource()函數返回傳回值0,表示成功;否則返回-EBUSY表示想要分配的資源已被佔用。
函數__request_resource()完成實際的資源分派工作。如果參數new所描述的資源中的一部分或全部已經被其它節點所佔用,則函數返回與new相衝突的resource結構的指標。否則就返回NULL。該函數的原始碼如下
(kernel/Resource.c):
/* Return the conflict entry if you can't request it */
static struct resource * __request_resource
(struct resource *root, struct resource *new)
{
unsigned long start = new->start;
unsigned long end = new->end;
struct resource *tmp, **p;
if (end < start)
return root;
if (start < root->start)
return root;
if (end > root->end)
return root;
p = &root->child;
for (;;) {
tmp = *p;
if (!tmp || tmp->start > end) {
new->sibling = tmp;
*p = new;
new->parent = root;
return NULL;
}
p = &tmp->sibling;
if (tmp->end < start)
continue;
return tmp;
}
}
對函數的NOTE:
①前三個if語句判斷new所描述的資源範圍是否被包含在root內,以及是否是一段有效資源(因為end必須大於start)。否則就返回root指標,表示與根結點相衝突。
②接下來用一個for迴圈遍曆根節點root的child鏈表,以便檢查是否有資源衝突,並將new插入到child鏈表中的合適位置(child鏈
表是以I/O資源物理地址從低到高的順序排列的)。為此,它用tmp指標指向當前正被掃描的resource結構,用指標p指向前一個resource結
構的sibling指標成員變數,p的初始值為指向root->sibling。For迴圈體的執行步驟如下:
l 讓tmp指向當前正被掃描的resource結構(tmp=*p)。
l
判斷tmp指標是否為空白(tmp指標為空白說明已經遍曆完整個child鏈表),或者當前被掃描節點的起始位置start是否比new的結束位置end還要
大。只要這兩個條件之一成立的話,就說明沒有資源衝突,於是就可以把new鏈入child鏈表中:①設定new的sibling指標指向當前正被掃描的節
點tmp(new->sibling=tmp);②當前節點tmp的前一個兄弟節點的sibling指標被修改為指向new這個節點
(*p=new);③將new的parent指標設定為指向root。然後函數就可以返回了(傳回值NULL表示沒有資源衝突)。
l
如果上述兩個條件都不成立,這說明當前被掃描節點的資源網域有可能與new相衝突(實際上就是兩個閉區間有交集),因此需要進一步判斷。為此它首先修改指標
p,讓它指向tmp->sibling,以便於繼續掃描child鏈表。然後,判斷tmp->end是否小於new->start,如
果小於,則說明當前節點tmp和new沒有資源衝突,因此執行continue語句,繼續向下掃描child鏈表。否則,如果tmp->end大於
或等於new->start,則說明tmp->[start,end]和new->[start,end]之間有交集。所以返回當前節
點的指標tmp,表示發生資源衝突。
3.2.2 資源的釋放
函數release_resource()用於實現I/O資源的釋放。該函數只有一個參數——即指標old,它指向所要釋放的資源。起原始碼如下:
int release_resource(struct resource *old)
{
int retval;
write_lock(&resource_lock);
retval = __release_resource(old);
write_unlock(&resource_lock);
return retval;
}
可以看出,它實際上通過調用__release_resource()這個內部靜態函數來完成實際的資源釋放工作。函數__release_resource()的主要任務就是將資來源區域old(如果已經存在的話)從其父資源的child鏈表重摘除,它的原始碼如下:
static int __release_resource(struct resource *old)
{
struct resource *tmp, **p;
p = &old->parent->child;
for (;;) {
tmp = *p;
if (!tmp)
break;
if (tmp == old) {
*p = tmp->sibling;
old->parent = NULL;
return 0;
}
p = &tmp->sibling;
}
return -EINVAL;
}
對上述函數代碼的NOTE如下:
同函數__request_resource()相類似,該函數也是通過一個for迴圈來遍曆父資源的child鏈表。為此,它讓tmp指標指向當前
被掃描的資源,而指標p則指向當前節點的前一個節點的sibling成員(p的初始值為指向父資源的child指標)。迴圈體的步驟如下:
①首先,讓tmp指標指向當前被掃描的節點(tmp=*p)。
②如果tmp指標為空白,說明已經遍曆完整個child鏈表,因此執行break語句推出for迴圈。由於在遍曆過程中沒有在child鏈表中找到參數old所指定的資源節點,因此最後返回錯誤值-EINVAL,表示參數old是一個無效的值。
③接下來,判斷當前被掃描節點是否就是參數old所指定的資源節點。如果是,那就將old從child鏈表中去除,也即讓當前結點tmp的前一個兄弟
節點的sibling指標指向tmp的下一個節點,然後將old->parent指標設定為NULL。最後返回0值表示執行成功。
④如果當前被掃描節點不是資源old,那就繼續掃描child鏈表中的下一個元素。因此將指標p指向tmp->sibling成員。
3.2.3 檢查資源是否已被佔用,
2.6.32刪除了這個函數
函數check_resource()用於實現檢查某一段I/O資源是否已被佔用。其原始碼如下:
int check_resource(struct resource *root, unsigned long start,
unsigned long len)
{
struct resource *conflict, tmp;
tmp.start = start;
tmp.end = start + len - 1;
write_lock(&resource_lock);
conflict = __request_resource(root, &tmp);
if (!conflict)
__release_resource(&tmp);
write_unlock(&resource_lock);
return conflict ? -EBUSY : 0;
}
對該函數的NOTE如下:
①構造一個臨時資源tmp,表示所要檢查的資源[start,start+end-1]。
②調用__request_resource()函數在根節點root申請tmp所表示的資源。如果tmp所描述的資源還被人使用,則該函數返回
NULL,否則返回非null 指標。因此接下來在conflict為NULL的情況下,調用__release_resource()將剛剛申請的資源釋放掉。
③最後根據conflict是否為NULL,返回-EBUSY或0值。