Linux内核里的“智能指针” (续)
在上一篇文章《Linux內(nèi)核里的智能指針》里介紹了Linux內(nèi)核如何使用引用計(jì)數(shù)來(lái)更加安全的管理內(nèi)存,本文承接前篇,主要介紹幾點(diǎn)使用kref時(shí)的注意事項(xiàng)。
Linux內(nèi)核文檔kref.txt羅列了三條規(guī)則,我們?cè)谑褂胟ref時(shí)必須遵守。
規(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().
?
對(duì)于規(guī)則一,其實(shí)主要是針對(duì)多條執(zhí)行路徑(比如另起一個(gè)線程)的情況。如果是在單一的執(zhí)行路徑里,比如把指針傳遞給一個(gè)函數(shù),是不需要使用kref_get的。看下面這個(gè)例子:
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);您是不是覺(jué)得call_something前后的一對(duì)kref_get和kref_put很多余呢?obj并沒(méi)有逃出我們的掌控,所以它們確實(shí)是沒(méi)有必要的。
但是當(dāng)遇到多條執(zhí)行路徑的情況就完全不一樣了,我們必須遵守規(guī)則一。下面是摘自內(nèi)核文檔里的一個(gè)例子:
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; }因?yàn)槲覀儾⒉恢谰€程more_data_handling何時(shí)結(jié)束,所以要用kref_get來(lái)保護(hù)我們的數(shù)據(jù)。
注意規(guī)則一里的那個(gè)單詞“before",kref_get必須是在傳遞指針之前進(jìn)行,在本例里就是在調(diào)用kthread_run之前就要執(zhí)行kref_get,否則,何談保護(hù)呢?
?
對(duì)于規(guī)則二我們就不必多說(shuō)了,前面調(diào)用了kref_get,自然要配對(duì)使用kref_put。
?
規(guī)則三主要是處理遇到鏈表的情況。我們假設(shè)一個(gè)情景,如果有一個(gè)鏈表擺在你的面前,鏈表里的節(jié)點(diǎn)是用引用計(jì)數(shù)保護(hù)的,那你如何操作呢?首先我們需要獲得節(jié)點(diǎn)的指針,然后才可能調(diào)用kref_get來(lái)增加該節(jié)點(diǎn)的引用計(jì)數(shù)。根據(jù)規(guī)則三,這種情況下我們要對(duì)上述的兩個(gè)動(dòng)作串行化處理,一般我們可以用mutex來(lái)實(shí)現(xiàn)。請(qǐng)看下面這個(gè)例子:
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); }這個(gè)例子里已經(jīng)用mutex來(lái)進(jìn)行保護(hù)了,假如我們把mutex拿掉,會(huì)出現(xiàn)什么情況?記住,我們遇到的很可能是多線程操作。如果線程A在用container_of取得entry指針之后、調(diào)用kref_get之前,被線程B搶先執(zhí)行,而線程B碰巧又做的是kref_put的操作,當(dāng)線程A恢復(fù)執(zhí)行時(shí)一定會(huì)出現(xiàn)內(nèi)存訪問(wèn)的錯(cuò)誤,所以,遇到這種情況一定要串行化處理。
?
我們?cè)谑褂胟ref的時(shí)候要嚴(yán)格遵循這三條規(guī)則,才能安全有效的管理數(shù)據(jù)。
轉(zhuǎn)載于:https://www.cnblogs.com/wwang/archive/2010/12/03/1895852.html
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來(lái)咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)總結(jié)
以上是生活随笔為你收集整理的Linux内核里的“智能指针” (续)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: C# 生成私钥和公钥
- 下一篇: 开机的时候重新设置密linux管理员的密