module_init與module_exit的分析

        在編寫驅動模塊的時候有兩個函數經常被使用也必須被使用,分別是module_init和module_exit,這兩個函數分別在加載和卸載驅動時被調用,即調用insmod和rmmod命令的時候,但是insmod和rmmod不能識別這兩個函數,它只能識別init_module和cleanup_module其實init_module和cleanup_module相當於是module_init和module_exit的別名,下面會詳細講解

我們知道驅動可以作爲一個模塊動態的加載到內核裏,也可以作爲內核的一部分靜態的編譯進內核,module_init/module_exit也就有了兩個含義:

一、編譯成模塊

/* Each module must use one module_init(). */
#define module_init(initfn) \
    static inline initcall_t __inittest(void) \
        { return initfn; } \
    int init_module(void) __attribute__((alias(#initfn)));

/* This is only required if you want to be unloadable. */
#define module_exit(exitfn) \
    static inline exitcall_t __exittest(void) \
        { return exitfn; } \
    void cleanup_module(void) __attribute__((alias(#exitfn)));

module_init有兩個含義:

  1. 驗證函數的格式

         initcall_t是一個函數指針。定義爲:

typedef int (*initcall_t)(void);

         static inline initcall_t __inittest(void) { return initfn; }

         這個函數的作用是定義一個函數__inittest返回值類型爲initcall_t如果我們傳進來的函數返回值類型不是initcall_t這個類型則會報錯,即驗證我們定義的函數是否符合規範

         所以我們寫加載函數的時候必須是返回值爲int參數爲void的函數。

2、定義別名

int init_module(void) __attribute__((alias(#initfn)));

         這段代碼的作用是給我們的加載函數定義一個別名init_module因爲insmod命令只能識別函數init_module

         module_exit的作用和module_init一樣,同樣也是驗證函數格式和定義別名。

         例:

static int __init acm_ms_init(void)
{
    return usb_composite_probe(&acm_ms_driver, acm_ms_bind);
}
module_init(acm_ms_init);
展開後爲:
static inline initcall_t __inittest(void)
{
    return acm_ms_init;//驗證acm_ms_init是否和initcall_t爲同一類型
}
int init_module(void) __attribute__((alias(#acm_ms_init)));//定義別名init_module

二、靜態編譯         

         在靜態編譯的時候module_init的定義如下:

#define __define_initcall(level,fn,id) \
static initcall_t __initcall_##fn##id __used \
__attribute__((__section__(".initcall" level ".init"))) = fn

#define device_initcall(fn) __define_initcall("6",fn,6)
#define __initcall(fn) device_initcall(fn)
#define module_init(x) __initcall(x);

         關於靜態編譯的主要函數__define_initcall的分析請參考博客:https://blog.csdn.net/qq_37600027/article/details/90739265

         module_exit在靜態編譯的時候沒有意義,因爲靜態編譯的驅動無法卸載!

         例:

static int __init acm_ms_init(void)
{
    return usb_composite_probe(&acm_ms_driver, acm_ms_bind);
}
module_init(acm_ms_init);

         展開後,其實就是如下內容

static initcall_t __initcall_acm_ms_init6 __used \
    __attribute__((__section__(".initcall6.init"))) = acm_ms_init;

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章