kref是乙個引用計數器,它被巢狀進其它的結構中,記錄所巢狀結構的引用計數,並在計數清零時呼叫相應的清理函式。kref的原理和實現都非常簡單,但要想用好卻不容易,或者說kref被建立就是為了跟蹤複雜情況下地結構引用銷毀情況。所以這裡先介紹kref的實現,再介紹其使用規則。
kref的標頭檔案在include/linux/kref.h,實現在lib/kref.c。閒話少說,上**。
[cpp]view plain
copy
print?
struct
kref ;
可以看到,kref的結構中就包含乙個atomic_t型別的計數值。atomic_t是原子型別,對其操作都要求是原子執行的,有專門的原子操作api執行,即使在多處理器間也保持原子性。使用atomic_t型別充當計數值,就省去了加鎖去鎖的過程。
[cpp]view plain
copy
print?
void
kref_set(
struct
kref *kref,
intnum)
kref_set 設定kref的初始計數值。具體計數值設定由原子操作atomic_set完成。之後還有乙個smp_mb()是為了增加記憶體屏障,保證這一寫操作會在之後的讀寫操作完成之前完成。
[cpp]view plain
copy
print?
void
kref_init(
struct
kref *kref)
kref_init 初始化kref的計數值為1。
[cpp]view plain
copy
print?
void
kref_get(
struct
kref *kref)
kref_get遞增kref的計數值。
[cpp]view plain
copy
print?
intkref_put(
struct
kref *kref,
void
(*release)(
struct
kref *kref))
return
0;
}
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到你的資料結構中:
[cpp]view plain
copy
print?
struct
my_data ;
kref可以出現在你結構中的任意位置。
在分配kref後你必須初始化它,可以呼叫kref_init,把kref計數值初始為1。
[cpp]view plain
copy
print?
struct
my_data *data;
data = kmalloc(sizeof
(*data), gfp_kernel);
if(!data)
return
-enomem;
kref_init(&data->refcount);
初始化之後,kref的使用應該遵循以下三條規則:
1) 如果你製造了乙個結構指標的非暫時性副本,特別是當這個副本指標會被傳遞到其它執行執行緒時,你必須在傳遞副本指標之前執行kref_get:
[cpp]view plain
copy
print?
kref_put(&data->refcount);
2)當你使用完,不再需要結構的指標,必須執行kref_put。如果這是結構指標的最後乙個引用,release函式會被呼叫。如果**絕不會在沒有擁有引用計數的請求下去呼叫kref_get,在kref_put時就不需要加鎖。
[cpp]view plain
copy
print?
kref_put(&data->refcount, data_release);
3)如果**試圖在還沒擁有引用計數的情況下就呼叫kref_get,就必須序列化kref_put和kref_get的執行。因為很可能在kref_get執行之前或者執行中,kref_put就被呼叫並把整個結構釋放掉了。
例如,你分配了一些資料並把它傳遞到其它執行緒去處理:
[cpp]view plain
copy
print?
void
data_release(
struct
kref *kref)
void
more_data_handling(
void
*cb_data)
intmy_data_handler(
void
)
. . do
stuff with data here
. out:
kref_put(&data->refcount, data_release);
return
rv;
}
這樣做,無論兩個執行緒的執行順序是怎樣的都無所謂,kref_put知道何時資料不再有引用計數,可以被銷毀。kref_get()呼叫不需要加鎖,因為在my_data_handler中呼叫kref_get時已經擁有乙個引用。同樣地原因,kref_put也不需要加鎖。
要注意規則一中的要求,必須在傳遞指標之前呼叫kref_get。決不能寫下面的**:
[cpp]view plain
copy
print?
task = kthread_run(more_data_handling, data,
"more_data_handling"
);
if(task == err_ptr(-enomem))
else
; static
struct
my_data *get_entry()
mutex_unlock(&mutex);
return
entry;
} static
void
release_entry(
struct
kref *ref)
static
void
put_entry(
struct
my_data *entry)
如果你不想在整個釋放過程中都加鎖,kref_put的返回值就有用了。例如你不想在加鎖情況下呼叫kfree,你可以如下使用kref_put。
[cpp]view plain
copy
print?
static
void
release_entry(
struct
kref *ref)
static
void
put_entry(
struct
my_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...