成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Linux內(nèi)核里的“智能指針”

系統(tǒng) Linux 開(kāi)源
C/C++語(yǔ)言本身并不支持垃圾回收機(jī)制,雖然語(yǔ)言本身具有極高的靈活性,但是當(dāng)遇到大型的項(xiàng)目時(shí),繁瑣的內(nèi)存管理往往讓人痛苦異常。Linux內(nèi)核是如何解決這個(gè)問(wèn)題呢?同樣作為C語(yǔ)言的解決方案,Linux內(nèi)核采用的也是引用計(jì)數(shù)的方式。

眾所周知,C/C++語(yǔ)言本身并不支持垃圾回收機(jī)制,雖然語(yǔ)言本身具有極高的靈活性,但是當(dāng)遇到大型的項(xiàng)目時(shí),繁瑣的內(nèi)存管理往往讓人痛苦異?!,F(xiàn)代的C/C++類庫(kù)一般會(huì)提供智能指針來(lái)作為內(nèi)存管理的折衷方案,比如STL的auto_ptr,Boost的Smart_ptr庫(kù),QT的QPointer家族,甚至是基于C語(yǔ)言構(gòu)建的GTK+也通過(guò)引用計(jì)數(shù)來(lái)實(shí)現(xiàn)類似的功能。Linux內(nèi)核是如何解決這個(gè)問(wèn)題呢?同樣作為C語(yǔ)言的解決方案,Linux內(nèi)核采用的也是引用計(jì)數(shù)的方式。如果您更熟悉C++,可以把它類比為Boost的shared_ptr,或者是QT的QSharedPointer。

在Linux內(nèi)核里,引用計(jì)數(shù)是通過(guò) struct kref 結(jié)構(gòu)來(lái)實(shí)現(xiàn)的。在介紹如何使用 kref 之前,我們先來(lái)假設(shè)一個(gè)情景。假如您開(kāi)發(fā)的是一個(gè)字符設(shè)備驅(qū)動(dòng),當(dāng)設(shè)備插上時(shí),系統(tǒng)自動(dòng)建立一個(gè)設(shè)備節(jié)點(diǎn),用戶通過(guò)文件操作來(lái)訪問(wèn)設(shè)備節(jié)點(diǎn)。

如上圖所示,最左邊的綠色框圖表示實(shí)際設(shè)備的插拔動(dòng)作,中間黃色的框圖表示內(nèi)核中設(shè)備對(duì)象的生存周期,右邊藍(lán)色的框圖表示用戶程序系統(tǒng)調(diào)用的順序。如果用戶程序正在訪問(wèn)的時(shí)候設(shè)備突然被拔掉,驅(qū)動(dòng)程序里的設(shè)備對(duì)象是否立刻釋放呢?如果立刻釋放,用戶程序執(zhí)行的系統(tǒng)調(diào)用一定會(huì)發(fā)生內(nèi)存非法訪問(wèn);如果要等到用戶 程序close之后再釋放設(shè)備對(duì)象,我們應(yīng)該怎么來(lái)實(shí)現(xiàn)?kref就是為了解決類似的問(wèn)題而生的。

kref的定義非常簡(jiǎn)單,其結(jié)構(gòu)體里只有一個(gè)原子變量。

  1. struct kref { 
  2.  atomic_t refcount; 
  3. }; 

Linux內(nèi)核定義了下面三個(gè)函數(shù)接口來(lái)使用kref:

  1. void kref_init(struct kref *kref); 
  2. void kref_get(struct kref *kref); 
  3. int kref_put(struct kref *kref, void (*release) (struct kref *kref)); 

我們先通過(guò)一段偽代碼來(lái)了解一下如何使用kref。

  1. struct my_obj 
  2.  int val; 
  3.  struct kref refcnt; 
  4. }; 
  5.   
  6. struct my_obj *obj; 
  7.   
  8. void obj_release(struct kref *ref)  
  9.  struct my_obj *obj = container_of(ref, struct my_obj, refcnt); 
  10.  kfree(obj); 
  11.   
  12. device_probe()  
  13.  obj = kmalloc(sizeof(*obj), GFP_KERNEL); 
  14.  kref_init(&obj->refcnt); 
  15.   
  16. device_disconnect()  
  17.  kref_put(&obj->refcnt, obj_release); 
  18.   
  19. .open()  
  20.  kref_get(&obj->refcnt); 
  21.   
  22. .close()  
  23.  kref_put(&obj->refcnt, obj_release); 

在這段代碼里,我們定義了obj_release來(lái)作為釋放設(shè)備對(duì)象的函數(shù),當(dāng)引用計(jì)數(shù)為0時(shí),這個(gè)函數(shù)會(huì)被立刻調(diào)用來(lái)執(zhí)行真正的釋放動(dòng)作。我們先在 device_probe里把引用計(jì)數(shù)初始化為1,當(dāng)用戶程序調(diào)用open時(shí),引用計(jì)數(shù)又會(huì)被加1,之后如果設(shè)備被拔 掉,device_disconnect會(huì)減掉一個(gè)計(jì)數(shù),但此時(shí)refcnt還不是0,設(shè)備對(duì)象obj并不會(huì)被釋放,只有當(dāng)close被調(diào)用之 后,obj_release才會(huì)執(zhí)行。

看完偽代碼之后,我們?cè)賮?lái)實(shí)戰(zhàn)一下。為了節(jié)省篇幅,這個(gè)實(shí)作并沒(méi)有建立一個(gè)字符設(shè)備,只是通過(guò)模塊的加載和卸載過(guò)程來(lái)對(duì)感受一下kref。

  1. #include <linux/kernel.h> 
  2. #include <linux/module.h> 
  3.   
  4. struct my_obj { 
  5.  int val; 
  6.  struct kref refcnt; 
  7. }; 
  8.   
  9. struct my_obj *obj; 
  10.   
  11. void obj_release(struct kref *ref) 
  12.  struct my_obj *obj = container_of(ref, struct my_obj, refcnt); 
  13.  printk(KERN_INFO "obj_release\n"); 
  14.  kfree(obj); 
  15.   
  16. static int __init kreftest_init(void) 
  17.  printk(KERN_INFO "kreftest_init\n"); 
  18.  obj = kmalloc(sizeof(*obj), GFP_KERNEL); 
  19.  kref_init(&obj->refcnt); 
  20.  return 0; 
  21.   
  22. static void __exit kreftest_exit(void) 
  23.  printk(KERN_INFO "kreftest_exit\n"); 
  24.  kref_put(&obj->refcnt, obj_release); 
  25.  return
  26.   
  27. module_init(kreftest_init); 
  28. module_exit(kreftest_exit); 
  29.   
  30. MODULE_LICENSE("GPL"); 

#p#

通過(guò)kbuild編譯之后我們得到kref_test.ko,然后我們順序執(zhí)行以下命令來(lái)掛載和卸載模塊。

sudo insmod ./kref_test.ko

sudo rmmod kref_test

此時(shí),系統(tǒng)日志會(huì)打印出如下消息:

kreftest_init

kreftest_exit

obj_release

這正是我們預(yù)期的結(jié)果。

有了kref引用計(jì)數(shù),即使內(nèi)核驅(qū)動(dòng)寫的再?gòu)?fù)雜,我們對(duì)內(nèi)存管理也應(yīng)該有信心了吧!

接下來(lá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è)例子:

  1. kref_init(&obj->ref); 
  2.   
  3. // do something here 
  4. // ... 
  5.   
  6. kref_get(&obj->ref); 
  7. call_something(obj); 
  8. kref_put(&obj->ref); 
  9.   
  10. // do something here 
  11. // ... 
  12.   
  13. 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è)例子:

  1. struct my_data 
  2.  . 
  3.  . 
  4.  struct kref refcount; 
  5.  . 
  6.  . 
  7. }; 
  8.   
  9. void data_release(struct kref *ref) 
  10.  struct my_data *data = container_of(ref, struct my_data, refcount); 
  11.  kfree(data); 
  12.   
  13. void more_data_handling(void *cb_data) 
  14.  struct my_data *data = cb_data; 
  15.  . 
  16.  . do stuff with data here 
  17.  . 
  18.  kref_put(&data->refcount, data_release); 
  19.   
  20. int my_data_handler(void) 
  21.  int rv = 0; 
  22.  struct my_data *data; 
  23.  struct task_struct *task; 
  24.  data = kmalloc(sizeof(*data), GFP_KERNEL); 
  25.  if (!data) 
  26.  return -ENOMEM; 
  27.  kref_init(&data->refcount); 
  28.   
  29.  kref_get(&data->refcount); 
  30.  task = kthread_run(more_data_handling, data, "more_data_handling"); 
  31.  if (task == ERR_PTR(-ENOMEM)) { 
  32.  rv = -ENOMEM; 
  33.  goto out
  34.  } 
  35.   
  36.  . 
  37.  . do stuff with data here 
  38.  . 
  39.  out
  40.  kref_put(&data->refcount, data_release); 
  41.  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è)例子:

  1. static DEFINE_MUTEX(mutex); 
  2. static LIST_HEAD(q); 
  3. struct my_data 
  4.  struct kref refcount; 
  5.  struct list_head link; 
  6. }; 
  7.   
  8. static struct my_data *get_entry() 
  9.  struct my_data *entry = NULL
  10.  mutex_lock(&mutex); 
  11.  if (!list_empty(&q)) { 
  12.  entry = container_of(q.next, struct my_q_entry, link); 
  13.  kref_get(&entry->refcount); 
  14.  } 
  15.  mutex_unlock(&mutex); 
  16.  return entry; 
  17.   
  18. static void release_entry(struct kref *ref) 
  19.  struct my_data *entry = container_of(ref, struct my_data, refcount); 
  20.   
  21.  list_del(&entry->link); 
  22.  kfree(entry); 
  23.   
  24. static void put_entry(struct my_data *entry) 
  25.  mutex_lock(&mutex); 
  26.  kref_put(&entry->refcount, release_entry); 
  27.  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ù)。

責(zé)任編輯:火鳳凰 來(lái)源: wwang博客
相關(guān)推薦

2021-08-11 09:01:48

智能指針Box

2010-12-17 10:07:59

2010-01-27 14:18:41

Android智能指針

2010-02-05 14:36:20

C++智能指針

2021-07-30 05:12:54

智能指針C++編程語(yǔ)言

2017-04-12 14:30:45

Linux內(nèi)核DebugFS

2011-07-01 14:28:47

Qt 指針

2023-11-17 11:48:08

智能指針C++

2021-09-09 17:05:36

C++智能指針語(yǔ)言

2024-12-26 10:45:08

2023-12-20 12:40:51

C++RAII編程

2024-03-01 16:43:48

C++11智能指針內(nèi)存

2021-07-29 06:09:05

萬(wàn)能指針C語(yǔ)言void

2025-06-17 08:10:00

智能指針C++代碼

2022-02-08 09:09:45

智能指針C++

2024-01-24 11:44:44

C++智能指針開(kāi)發(fā)

2025-02-26 01:23:02

C++11Raw代碼

2016-08-24 20:09:27

Linux數(shù)據(jù)結(jié)構(gòu)位數(shù)組

2020-11-11 14:48:41

Linux內(nèi)核代碼

2021-02-20 11:34:43

Linux內(nèi)核指針
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 农村真人裸体丰满少妇毛片 | 精品中文在线 | 国产精品亚洲片在线播放 | 国产一区二区三区免费 | 日韩在线h | 国产成人精品免费视频大全最热 | 国产精品视频综合 | 91视视频在线观看入口直接观看 | 欧美视频| 在线中文字幕视频 | 中文字幕免费视频 | 波多野结衣先锋影音 | 国产精品国产a | 欧美成人黄色小说 | 亚洲第一天堂 | 精品96久久久久久中文字幕无 | 成人久久久 | 成人午夜网站 | 黄色av网站在线观看 | 日日夜夜精品视频 | 久久久久久中文字幕 | 91久久久久 | 91免费看片 | 丁香一区二区 | 国内精品久久久久久 | 日韩高清国产一区在线 | 91视频久久 | 欧美黄色一区 | 日本免费一区二区三区 | 久久国产精品视频 | 久久久免费电影 | 精品久久久久久久久久久院品网 | 久久精品国产一区二区电影 | 天天爱爱网 | 国产成人精品免高潮在线观看 | 亚洲精品一区在线观看 | 免费h在线 | 久久精品网 | 亚洲视频精品在线 | 欧美v在线| 女人夜夜春 |