轉帖|其它|編輯:郝浩|2010-12-03 14:49:08.000|閱讀 493 次
概述:眾所周知,C/C++語言本身并不支持垃圾回收機制,雖然語言本身具有極高的靈活性,但是當遇到大型的項目時,繁瑣的內存管理往往讓人痛苦異常。現代的C/C++類庫一般會提供智能指針來作為內存管理的折中方案,比如STL的auto_ptr,Boost的Smart_ptr庫,QT的QPointer家族,甚至是基于C語言構建的GTK+也通過引用計數來實現類似的功能。Linux內核是如何解決這個問題呢?同樣作為C語言的解決方案,Linux內核采用的也是引用計數的方式。如果您更熟悉C++,可以把它類比為Boost的shared_ptr,或者是QT的QSharedPointer。
# 界面/圖表報表/文檔/IDE等千款熱門軟控件火熱銷售中 >>
眾所周知,C/C++語言本身并不支持垃圾回收機制,雖然語言本身具有極高的靈活性,但是當遇到大型的項目時,繁瑣的內存管理往往讓人痛苦異常。現代的C/C++類庫一般會提供智能指針來作為內存管理的折中方案,比如STL的auto_ptr,Boost的Smart_ptr庫,QT的QPointer家族,甚至是基于C語言構建的GTK+也通過引用計數來實現類似的功能。Linux內核是如何解決這個問題呢?同樣作為C語言的解決方案,Linux內核采用的也是引用計數的方式。如果您更熟悉C++,可以把它類比為Boost的shared_ptr,或者是QT的QSharedPointer。
在Linux內核里,引用計數是通過struct kref結構來實現的。在介紹如何使用kref之前,我們先來假設一個情景。假如您開發的是一個字符設備驅動,當設備插上時,系統自動建立一個設備節點,用戶通過文件操作來訪問設備節點。

如上圖所示,最左邊的綠色框圖表示實際設備的插拔動作,中間黃色的框圖表示內核中設備對象的生存周期,右邊藍色的框圖表示用戶程序系統調用的順序。如果用戶程序正在訪問的時候設備突然被拔掉,驅動程序里的設備對象是否立刻釋放呢?如果立刻釋放,用戶程序執行的系統調用一定會發生內存非法訪問;如果要等到用戶程序close之后再釋放設備對象,我們應該怎么來實現?kref就是為了解決類似的問題而生的。
kref的定義非常簡單,其結構體里只有一個原子變量。
struct kref {
atomic_t refcount;
};
Linux內核定義了下面三個函數接口來使用kref:
void kref_init(struct kref *kref);
void kref_get(struct kref *kref);
int kref_put(struct kref *kref, void (*release) (struct kref *kref));
我們先通過一段偽代碼來了解一下如何使用kref。
struct my_obj
{
int val;
struct kref refcnt;
};
struct my_obj *obj;
void obj_release(struct kref *ref)
{
struct my_obj *obj = container_of(ref, struct my_obj, refcnt);
kfree(obj);
}
device_probe()
{
obj = kmalloc(sizeof(*obj), GFP_KERNEL);
kref_init(&obj->refcnt);
}
device_disconnect()
{
kref_put(&obj->refcnt, obj_release);
}
.open()
{
kref_get(&obj->refcnt);
}
.close()
{
kref_put(&obj->refcnt, obj_release);
}
在這段代碼里,我們定義了obj_release來作為釋放設備對象的函數,當引用計數為0時,這個函數會被立刻調用來執行真正的釋放動作。我們先在device_probe里把引用計數初始化為1,當用戶程序調用open時,引用計數又會被加1,之后如果設備被拔掉,device_disconnect會減掉一個計數,但此時refcnt還不是0,設備對象obj并不會被釋放,只有當close被調用之后,obj_release才會執行。
看完偽代碼之后,我們再來實戰一下。為了節省篇幅,這個實作并沒有建立一個字符設備,只是通過模塊的加載和卸載過程來對感受一下kref。
#include <linux/kernel.h>
#include <linux/module.h>
struct my_obj {
int val;
struct kref refcnt;
};
struct my_obj *obj;
void obj_release(struct kref *ref)
{
struct my_obj *obj = container_of(ref, struct my_obj, refcnt);
printk(KERN_INFO "obj_release\n");
kfree(obj);
}
static int __init kreftest_init(void)
{
printk(KERN_INFO "kreftest_init\n");
obj = kmalloc(sizeof(*obj), GFP_KERNEL);
kref_init(&obj->refcnt);
return 0;
}
static void __exit kreftest_exit(void)
{
printk(KERN_INFO "kreftest_exit\n");
kref_put(&obj->refcnt, obj_release);
return;
}
module_init(kreftest_init);
module_exit(kreftest_exit);
MODULE_LICENSE("GPL");
通過kbuild編譯之后我們得到kref_test.ko,然后我們順序執行以下命令來掛載和卸載模塊。
sudo insmod ./kref_test.ko
sudo rmmod kref_test
此時,系統日志會打印出如下消息:
kreftest_init
kreftest_exit
obj_release
本站文章除注明轉載外,均為本站原創或翻譯。歡迎任何形式的轉載,但請務必注明出處、不得修改原文相關鏈接,如果存在內容上的異議請郵件反饋至chenjj@ke049m.cn
文章轉載自:網絡轉載