Linux設備驅動程序學習----5.模塊的初始化和關閉

模塊的初始化和關閉

更多內容請參考Linux設備驅動程序學習----目錄

1. 初始化函數

??模塊的初始化函數負責注冊模塊所提供的任何設施,即可以被應用程序訪問的新功能,可能是一個完整的驅動程序或者僅僅是一個新的軟件抽象。初始化函數的定義通常如下所示:

static int __init initialization_function(void)
{
    // 初始化代碼
    
    return 0;
}
module_init(initialization_function);

??初始化函數被聲明為static,因為初始化函數在特定文件之外沒有其他意義。__init標記表明該函數僅在初始化期間使用。在模塊被裝載之后,模塊裝載器就會將初始化函數扔掉,可將該函數占用的內存釋放出來。

??注意:不要在結束初始化之后仍要使用的函數或數據結構上使用__init和__initdata標記。對于__devinit和__devinitdata,只有在內核未被配置為支持熱插拔設備的情況下,才會被翻譯為__init和__initdata。

??module_init()宏的使用是強制性的,會在模塊的目標代碼中增加一個特殊的段,用于說明內核初始化函數所在的位置。如果沒有這個定義,初始化函數永遠不會被調用。

2. 清除函數

??每個模塊都需要一個清除函數,在模塊被移除前注銷接口并向系統中返回所有資源。該函數定義如下:

static void __exit cleanup_function(void)
{
    // 清除代碼
}
module_exit(cleanup_function);

??清除函數沒有返回值,__exit修飾詞標記該代碼僅用于模塊卸載,編譯器會把該函數放在特殊的ELF段中。如果模塊被直接編譯到內核中,或者內核配置不允許卸載模塊,則被標記為__exit的函數將被直接丟棄。所以被標記為__exit的函數只能在模塊被卸載或者系統關閉時被調用,其他任何用法都是錯的。module_exit()聲明對于內核找到模塊的清除函數是必需的。如果一個模塊未定義清除函數,則內核不允許卸載該模塊。

3. 初始化過程中的錯誤處理

??在內核中注冊設施時,注冊可能會失敗。即使最簡單的動作,都需要內存分配,而所需的內存可能無法獲得。因此模塊代碼必須始終檢查返回值,并確保所請求的操作已真正執行成功。如果在注冊設施時遇到錯誤,首先要判斷模塊是否可以繼續初始化,只要可能,模塊應該繼續向前并盡可能提供其功能。

??如果在發生了某個特定類型的錯誤之后無法繼續裝載模塊,則要將出錯之前的所有注冊工作都撤銷掉。即當模塊的初始化出現錯誤之后,模塊必須自行撤銷已注冊的設施。如果未能撤銷已注冊的設施,則內核會處于一種不穩定狀態,這時,唯一有效的解決辦法就是重新引導系統。所以必須在初始化過程出現錯誤時認真完成正確的工作。

??錯誤恢復的處理有時使用goto語句非常有效。正常情況下,很少使用goto,但是唯一在錯誤處理時卻非常有效。內核經常使用goto來處理錯誤。如下例子所示:

int __init my_init_function(void)
{
    int err;
    
    // 使用指針和名稱注冊
    err = register_this(ptr1, "skull");
    if (err)
        goto fail_this;
    
    err = register_that(ptr2, "skull");
    if (err)
        goto fail_that;
        
    err = register_those(ptr3, "skull");
    if (err)
        goto fail_those;
    
    return 0;   // 成功
    
fail_those:
    unregister_that(ptr2, "skull");
fail_that:
    unregister_that(ptr1, "skull");
fail_this:
    return err; // 返回錯誤
}

在出錯的時候使用goto語句,將只撤銷出錯時刻以前所成功注冊的那些設施。

??另一種方法是,記錄任何成功注冊的設施,在出錯的時候調用模塊的清除函數。清除函數將僅僅回滾已成功完成的步驟。這種方法需要更多的代碼和CPU時間,因此在追求效率的代碼中使用goto語句是最好的錯誤恢復機制。

??在Linux內核中錯誤編碼是定義在<linux/errno.h>頭文件中的負整數,如果不想使用其他函數返回的錯誤碼,應該包含<linux/errno.h>頭文件,以使用如:-ENODEV、-ENOMEM之類的符號值。每次返回核時的錯誤編碼是個好習慣,因為用戶程序可以通過perror()函數或類似途徑將錯誤符號轉換為有意義的字符串。

??模塊的清除函數需要撤銷初始化函數所注冊的所有設施,并且習慣上以相反于注冊的順序撤銷設施,如下所示:

void __exit my_cleanup_function(void)
{
    unregister_those(ptr3, "skull");
    unregister_that(ptr2, "skull");
    unregister_this(ptr1, "skull");
    
    return;
}

??如果初始化和清除工作涉及很多設施,則goto方法可能難以管理,因為所有用于清除設施的代碼在初始化函數中給重復,同時一些標號交織在一起。

??每次發生錯誤時從初始化函數中調用清除函數,將減少代碼的重復并且時代碼更清晰、更有條理。清除函數必須在撤銷每項設施的注冊之前檢查它的狀態。如下示例:

struct something *item1;
struct somethingelse *item2;

void my_cleanup(void)
{
    if (item1)
        release_thing(item1);
    if (item2)
        release_thing2(item2);
    if (stuff_ok)
        unregister_stuff();
        
    return;
}

int __init my_init(void)
{
    int err = -ENOMEM;
    
    item1 = allocate_thing(arguments);
    item2 = allocate_thing2(arguments2);
    if (!item1 || !item2)
        goto fail;
    err = register_stuff(item1, item2);
    if (!err)
        stuff_ok = 1;
    else
        goto fail;
    
    return 0;   // 返回成功

fail:
    my_cleanup();
    return err; // 返回錯誤
}

??如上代碼所示,根據調用的注冊/分配函數的語義,可以使用或不使用外部標記來標記每個初始化步驟的成功。這種方式的初始化能很好地擴展到對大量設施的支持。注意:因為清除函數被非退出代碼調用,因此不能將清除函數標記為__exit;

4. 模塊裝載競爭

??模塊裝載中也存在競態。在模塊注冊完成之前,內核的某些部分可能會立即使用我們剛剛注冊的任何設施,即在初始化函數還在運行的時候,內核就完全可能會調用我們的模塊。因此,在首次注冊完成之后,代碼就應該準備好被內核的其他部分調用;在支持某個設施的所有內部初始化完成之前,不要注冊任何設施。

??當模塊初始化失敗而內核的某些部分已經使用了模塊所注冊的某個設施時,此時根本不應該出現模塊初始化失敗,因為模塊已經成功導出了可用的功能及符號。如果初始化一定要失敗,則應該仔細處理內核其他部分正在進行的操作,并且要等待這些操作的完成。

更多內容請參考Linux設備驅動程序學習----目錄

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,488評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,034評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,327評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,554評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,337評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,883評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,975評論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,114評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,625評論 1 332
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,555評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,737評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,244評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,973評論 3 345
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,362評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,615評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,343評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,699評論 2 370