最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

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

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


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

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

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


  • 這個例子里已經(jīng)用mutex來進(jìn)行保護(hù)了,假如我們把mutex拿掉,會出現(xiàn)什么情況?記住,我們遇到的很可能是多線程操作。如果線程A在用container_of取得entry指針之后、調(diào)用kref_get之前,被線程B搶先執(zhí)行,而線程B碰巧又做的是kref_put的操作,當(dāng)線程A恢復(fù)執(zhí)行時一定會出現(xiàn)內(nèi)存訪問的錯誤,所以,遇到這種情況一定要串行化處理。


  • 我們在使用kref的時候要嚴(yán)格遵循這三條規(guī)則,才能安全有效的管理數(shù)據(jù)。

【文章福利】小編推薦自己的Linux內(nèi)核技術(shù)交流群:【891587639】整理了一些個人覺得比較好的學(xué)習(xí)書籍、視頻資料共享在群文件里面,有需要的可以自行添加哦?。。∏?00名進(jìn)群領(lǐng)取,額外贈送一份價值699的內(nèi)核資料包(含視頻教程、電子書、實戰(zhàn)項目及代碼)?


?


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

  • 我們先通過一段偽代碼來了解一下如何使用kref。

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


  • 看完偽代碼之后,我們再來實戰(zhàn)一下。為了節(jié)省篇幅,這個實作并沒有建立一個字符設(shè)備,只是通過模塊的加載和卸載過程來對感受一下kref。

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

  • sudo insmod ./kref_test.ko

  • sudo rmmod kref_test

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

  • kreftest_init

  • kreftest_exit

  • obj_release

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


  • 有了kref引用計數(shù),即使內(nèi)核驅(qū)動寫的再復(fù)雜,我們對內(nèi)存管理也應(yīng)該有信心了吧。

  • 上面介紹了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的。看下面這個例子:

  • 您是不是覺得call_something前后的一對kref_get和kref_put很多余呢?obj并沒有逃出我們的掌控,所以它們確實是沒有必要的。


  • 但是當(dāng)遇到多條執(zhí)行路徑的情況就完全不一樣了,我們必須遵守規(guī)則一。下面是摘自內(nèi)核文檔里的一個例子:

  • 因為我們并不知道線程more_data_handling何時結(jié)束,所以要用kref_get來保護(hù)我們的數(shù)據(jù)。


  • 注意規(guī)則一里的那個單詞“before",kref_get必須是在傳遞指針之前進(jìn)行,在本例里就是在調(diào)用kthread_run之前就要執(zhí)行kref_get,否則,何談保護(hù)呢?


  • 對于規(guī)則二我們就不必多說了,前面調(diào)用了kref_get,自然要配對使用kref_put。

  • 規(guī)則三主要是處理遇到鏈表的情況。我們假設(shè)一個情景,如果有一個鏈表擺在你的面前,鏈表里的節(jié)點是用引用計數(shù)保護(hù)的,那你如何操作呢?首先我們需要獲得節(jié)點的指針,然后才可能調(diào)用kref_get來增加該節(jié)點的引用計數(shù)。根據(jù)規(guī)則三,這種情況下我們要對上述的兩個動作串行化處理,一般我們可以用mutex來實現(xiàn)。請看下面這個例子:

  • 這個例子里已經(jīng)用mutex來進(jìn)行保護(hù)了,假如我們把mutex拿掉,會出現(xiàn)什么情況?記住,我們遇到的很可能是多線程操作。如果線程A在用container_of取得entry指針之后、調(diào)用kref_get之前,被線程B搶先執(zhí)行,而線程B碰巧又做的是kref_put的操作,當(dāng)線程A恢復(fù)執(zhí)行時一定會出現(xiàn)內(nèi)存訪問的錯誤,所以,遇到這種情況一定要串行化處理。


  • 我們在使用kref的時候要嚴(yán)格遵循這三條規(guī)則,才能安全有效的管理數(shù)據(jù)。


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

分享到微博請遵守國家法律
盐城市| 丹凤县| 涞源县| 安平县| 泊头市| 公安县| 苗栗县| 吴旗县| 开封市| 沁水县| 兴义市| 洞口县| 长乐市| 沂水县| 定西市| 福海县| 个旧市| 海安县| 榆社县| 靖安县| 吴堡县| 长阳| 嘉黎县| 三原县| 凉山| 桃园县| 凤庆县| 剑阁县| 东明县| 甘谷县| 育儿| 多伦县| 巩留县| 华安县| 明光市| 溧阳市| 大荔县| 郓城县| 敦煌市| 贡山| 舟山市|