記錄生命週期的kref

2021-07-30 01:16:22 字數 4209 閱讀 1941

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...