kref是乙個引用計數器,它被巢狀進其它的結構中,記錄所巢狀結構的引用計數,並在計數清零時呼叫相應的清理函式。kref的原理和實現都非常簡單,但要想用好卻不容易,或者說kref被建立就是為了跟蹤複雜情況下地結構引用銷毀情況。所以這裡先介紹kref的實現,再介紹其使用規則。
kref的標頭檔案在include/linux/kref.h,實現在lib/kref.c。閒話少說,上**。
structkref ;
可以看到,kref的結構中就包含乙個atomic_t型別的計數值。atomic_t是原子型別,對其操作都要求是原子執行的,有專門的原子操作api執行,即使在多處理器間也保持原子性。使用atomic_t型別充當計數值,就省去了加鎖去鎖的過程。
voidkref_set(structkref *kref,intnum)
kref_set 設定kref的初始計數值。具體計數值設定由原子操作atomic_set完成。之後還有乙個smp_mb()是為了增加記憶體屏障,保證這一寫操作會在之後的讀寫操作完成之前完成。
voidkref_init(structkref *kref)
kref_init 初始化kref的計數值為1。
voidkref_get(structkref *kref)
kref_get遞增kref的計數值。
intkref_put(structkref *kref,void(*release)(structkref *kref))
return0;
}
kref_put遞減kref的計數值,如果計數值減為0,說明kref所指向的結構生命週期結束,會執行release釋放函式。
所以說kref的api很簡單,kref_init和kref_set基本都是初始時才會用到,平時常用的就是kref_get和kref_put。一旦在kref_put時計數值清零,立即呼叫結束函式。
kref設計得如此簡單,是為了能靈活地用在各種結構的生命週期管理中。要用好它可不簡單,好在documentation/kref.txt中為我們總結了一些使用規則,下面簡單翻譯一下。
對於那些用在多種場合,被到處傳遞的結構,如果沒有引用計數,bug幾乎總是肯定的事。所以我們需要kref。kref允許我們在已有的結構中方便地新增引用計數。
你可以以如下方式新增kref到你的資料結構中:
structmy_data ;
kref可以出現在你結構中的任意位置。
在分配kref後你必須初始化它,可以呼叫kref_init,把kref計數值初始為1。
structmy_data *data;
data = kmalloc(sizeof(*data), gfp_kernel);
if(!data)
return-enomem;
kref_init(&data->refcount);
初始化之後,kref的使用應該遵循以下三條規則:
1) 如果你製造了乙個結構指標的非暫時性副本,特別是當這個副本指標會被傳遞到其它執行執行緒時,你必須在傳遞副本指標之前執行kref_get:
kref_put(&data->refcount);
2)當你使用完,不再需要結構的指標,必須執行kref_put。如果這是結構指標的最後乙個引用,release函式會被呼叫。如果**絕不會在沒有擁有引用計數的請求下去呼叫kref_get,在kref_put時就不需要加鎖。
kref_put(&data->refcount, data_release);
3)如果**試圖在還沒擁有引用計數的情況下就呼叫kref_get,就必須序列化kref_put和kref_get的執行。因為很可能在kref_get執行之前或者執行中,kref_put就被呼叫並把整個結構釋放掉了。
例如,你分配了一些資料並把它傳遞到其它執行緒去處理:
voiddata_release(structkref *kref)
voidmore_data_handling(void*cb_data)
intmy_data_handler(void)
. .dostuff with data here
. out:
kref_put(&data->refcount, data_release);
returnrv;
}
這樣做,無論兩個執行緒的執行順序是怎樣的都無所謂,kref_put知道何時資料不再有引用計數,可以被銷毀。kref_get()呼叫不需要加鎖,因為在my_data_handler中呼叫kref_get時已經擁有乙個引用。同樣地原因,kref_put也不需要加鎖。
要注意規則一中的要求,必須在傳遞指標之前呼叫kref_get。決不能寫下面的**:
task = kthread_run(more_data_handling, data, "more_data_handling"
);
if(task == err_ptr(-enomem))
else;
static
structmy_data *get_entry()
mutex_unlock(&mutex);
returnentry;
}static
voidrelease_entry(structkref *ref)
static
voidput_entry(structmy_data *entry)
如果你不想在整個釋放過程中都加鎖,kref_put的返回值就有用了。例如你不想在加鎖情況下呼叫kfree,你可以如下使用kref_put。
static
voidrelease_entry(structkref *ref)
static
voidput_entry(structmy_data *entry)
else
mutex_unlock(&mutex);
}
如果你在撤銷結構的過程中需要呼叫其它的需要較長時間的函式,或者函式也可能要獲取同樣地互斥鎖,這樣做就很有用了。但要注意在release函式中做完撤銷工作會使**看起來更整潔
記錄生命週期的kref
kref是乙個引用計數器,它被巢狀進其它的結構中,記錄所巢狀結構的引用計數,並在計數清零時呼叫相應的清理函式。kref的原理和實現都非常簡單,但要想用好卻不容易,或者說kref被建立就是為了跟蹤複雜情況下地結構引用銷毀情況。所以這裡先介紹kref的實現,再介紹其使用規則。kref的標頭檔案在incl...
Fragment生命週期記錄
這是fragment的生命週期,我的arrayadapter一直無法顯示文字,後來才發現,原來是我的生命週期弄錯了。在我的imagefragment中,我本來應該是將onviewcreate中建立arrayadapter,但是很遺憾,這樣做沒有成功。因為我是在onactivitycreated的時候...
Android View生命週期(筆記記錄)
view 就是螢幕上的一塊矩形區域,我們可以在這塊區域繪製我們想讓使用者看到的圖形 負責繪製這個區域和事件的處理 view 預設是可見的。1.建立 creation 1 constructors 建構函式 有一種形式的建構函式是view在 中被建立時呼叫 第一種構造方法 另一種形式的建構函式會在vi...