Linux内核里的“智能指针” (续)
在上一篇文章《Linux內(nèi)核里的智能指針》里介紹了Linux內(nèi)核如何使用引用計數(shù)來更加安全的管理內(nèi)存,本文承接前篇,主要介紹幾點使用kref時的注意事項。
Linux內(nèi)核文檔kref.txt羅列了三條規(guī)則,我們在使用kref時必須遵守。
規(guī)則一:
If you make a non-temporary copy of a pointer, especially if ?it can be passed to another thread of execution, you must ?increment the refcount with kref_get() before passing it off;
規(guī)則二:
When you are done with a pointer, you must call kref_put();
規(guī)則三:
If the code attempts to gain a reference to a kref-ed structure without already holding a valid pointer, it must serialize access where a kref_put() cannot occur during the kref_get(), and the?? structure must remain valid during the kref_get().
?
對于規(guī)則一,其實主要是針對多條執(zhí)行路徑(比如另起一個線程)的情況。如果是在單一的執(zhí)行路徑里,比如把指針傳遞給一個函數(shù),是不需要使用kref_get的。看下面這個例子:
kref_init(&obj->ref);// do something here // ...kref_get(&obj->ref); call_something(obj); kref_put(&obj->ref);// do something here // ...kref_put(&obj->ref);您是不是覺得call_something前后的一對kref_get和kref_put很多余呢?obj并沒有逃出我們的掌控,所以它們確實是沒有必要的。
但是當遇到多條執(zhí)行路徑的情況就完全不一樣了,我們必須遵守規(guī)則一。下面是摘自內(nèi)核文檔里的一個例子:
struct my_data {..struct kref refcount;.. };void data_release(struct kref *ref) {struct my_data *data = container_of(ref, struct my_data, refcount);kfree(data); }void more_data_handling(void *cb_data) {struct my_data *data = cb_data;.. do stuff with data here.kref_put(&data->refcount, data_release); }int my_data_handler(void) {int rv = 0;struct my_data *data;struct task_struct *task;data = kmalloc(sizeof(*data), GFP_KERNEL);if (!data)return -ENOMEM;kref_init(&data->refcount);kref_get(&data->refcount);task = kthread_run(more_data_handling, data, "more_data_handling");if (task == ERR_PTR(-ENOMEM)) {rv = -ENOMEM;goto out;}.. do stuff with data here.out:kref_put(&data->refcount, data_release);return rv; }因為我們并不知道線程more_data_handling何時結(jié)束,所以要用kref_get來保護我們的數(shù)據(jù)。
注意規(guī)則一里的那個單詞“before",kref_get必須是在傳遞指針之前進行,在本例里就是在調(diào)用kthread_run之前就要執(zhí)行kref_get,否則,何談保護呢?
?
對于規(guī)則二我們就不必多說了,前面調(diào)用了kref_get,自然要配對使用kref_put。
?
規(guī)則三主要是處理遇到鏈表的情況。我們假設一個情景,如果有一個鏈表擺在你的面前,鏈表里的節(jié)點是用引用計數(shù)保護的,那你如何操作呢?首先我們需要獲得節(jié)點的指針,然后才可能調(diào)用kref_get來增加該節(jié)點的引用計數(shù)。根據(jù)規(guī)則三,這種情況下我們要對上述的兩個動作串行化處理,一般我們可以用mutex來實現(xiàn)。請看下面這個例子:
static DEFINE_MUTEX(mutex); static LIST_HEAD(q); struct my_data {struct kref refcount;struct list_head link; };static struct my_data *get_entry() {struct my_data *entry = NULL;mutex_lock(&mutex);if (!list_empty(&q)) {entry = container_of(q.next, struct my_q_entry, link);kref_get(&entry->refcount);}mutex_unlock(&mutex);return entry; }static void release_entry(struct kref *ref) {struct my_data *entry = container_of(ref, struct my_data, refcount);list_del(&entry->link);kfree(entry); }static void put_entry(struct my_data *entry) {mutex_lock(&mutex);kref_put(&entry->refcount, release_entry);mutex_unlock(&mutex); }這個例子里已經(jīng)用mutex來進行保護了,假如我們把mutex拿掉,會出現(xiàn)什么情況?記住,我們遇到的很可能是多線程操作。如果線程A在用container_of取得entry指針之后、調(diào)用kref_get之前,被線程B搶先執(zhí)行,而線程B碰巧又做的是kref_put的操作,當線程A恢復執(zhí)行時一定會出現(xiàn)內(nèi)存訪問的錯誤,所以,遇到這種情況一定要串行化處理。
?
我們在使用kref的時候要嚴格遵循這三條規(guī)則,才能安全有效的管理數(shù)據(jù)。
轉(zhuǎn)載于:https://www.cnblogs.com/wwang/archive/2010/12/03/1895852.html
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎勵來咯,堅持創(chuàng)作打卡瓜分現(xiàn)金大獎總結(jié)
以上是生活随笔為你收集整理的Linux内核里的“智能指针” (续)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C# 生成私钥和公钥
- 下一篇: 开机的时候重新设置密linux管理员的密