設備驅動-模塊-module_init宏解析


module_init 

這個宏定義在 

include/linux/module.h

module 的含義,即 模塊; 有兩類:  builtin  的模塊 (存在在 Image 中) 或者  獨立的模塊(存在在  xx.ko 中)

 

根據當前在編譯 builtin 還是 編譯 獨立模塊, module 宏有不同的 定義

  80#ifndef MODULE

  89#define module_init(x)  __initcall(x);

 103#else /* MODULE */

 131#define module_init(initfn)                                     \
 132        static inline initcall_t __maybe_unused __inittest(void)                \
 133        { return initfn; }                                      \
 134        int init_module(void) __copy(initfn) __attribute__((alias(#initfn)));

 142#endif

 

MODULE 宏從哪兒控制? 

如果一個 xx.c 文件在 Kconfig 中配置的是 obj-m += xx.c ,那么編譯這個 xx.c 文件時,就會 定義MODULE 宏。

這個機制是 頂層的  Makefile 和 script/Makefile.lib 配合實現的

Makefile

 503KBUILD_AFLAGS_MODULE  := -DMODULE
 504KBUILD_CFLAGS_MODULE  := -DMODULE

scripts/Makefile.lib

 188part-of-module = $(if $(filter $(basename $@).o, $(real-obj-m)),y)
 189quiet_modtag = $(if $(part-of-module),[M],   )
 190
 191modkern_cflags =                                          \
 192        $(if $(part-of-module),                           \
 193                $(KBUILD_CFLAGS_MODULE) $(CFLAGS_MODULE), \
 194                $(KBUILD_CFLAGS_KERNEL) $(CFLAGS_KERNEL) $(modfile_flags))

$@ 表示正在編譯的目標,如果是 module 的一部分,則使用 KBUILD_CFLAGS_MODULE 作為 cflags ,即 -DMODULE 被引入 gcc 命令行 ;

 

作為  builtin 模塊時的module_init 

就是  __initcall(x); 

include/linux/init.h

 195#define ___define_initcall(fn, id, __sec) \
 196        static initcall_t __initcall_##fn##id __used \
 197                __attribute__((__section__(#__sec ".init"))) = fn;
 

 200#define __define_initcall(fn, id) ___define_initcall(fn, id, .initcall##id)
 
 207#define early_initcall(fn)              __define_initcall(fn, early)

 216#define pure_initcall(fn)               __define_initcall(fn, 0)
 218#define core_initcall(fn)               __define_initcall(fn, 1)
 219#define core_initcall_sync(fn)          __define_initcall(fn, 1s)
 220#define postcore_initcall(fn)           __define_initcall(fn, 2)
 221#define postcore_initcall_sync(fn)      __define_initcall(fn, 2s)
 222#define arch_initcall(fn)               __define_initcall(fn, 3)
 223#define arch_initcall_sync(fn)          __define_initcall(fn, 3s)
 224#define subsys_initcall(fn)             __define_initcall(fn, 4)
 225#define subsys_initcall_sync(fn)        __define_initcall(fn, 4s)
 226#define fs_initcall(fn)                 __define_initcall(fn, 5)
 227#define fs_initcall_sync(fn)            __define_initcall(fn, 5s)
 228#define rootfs_initcall(fn)             __define_initcall(fn, rootfs)
 229#define device_initcall(fn)             __define_initcall(fn, 6)
 230#define device_initcall_sync(fn)        __define_initcall(fn, 6s)
 231#define late_initcall(fn)               __define_initcall(fn, 7)
 232#define late_initcall_sync(fn)          __define_initcall(fn, 7s)

 234#define __initcall(fn) device_initcall(fn)

紅色 標識 出 __initcall 用到的部分 ;

編譯:

234 行 -  __initcall 其實就是  device_initcall 

229 - device_initcall  是 __define_initcall(fn, 6 )  // 6 標識 id ;

200  -  __define_initcall(fn, id) 是  __define_initcall(fn, id, .initcall##id )  // 即 __define_initcall(fn, 6 , initcall6 ) ; 

195 - __define_initcall(fn , id, sec)  做出定義 

      static    initcall_t        __initcall_fn6    __used  __attribute__((__section__(  initcall6.init ))    =   fn

 

只需要將上面的 fn 替換為 module_init(xx) 中填入的 xx 即可。

示例: module_init (i2c_init) ; 

static  int    __initcall_i2c_init6     __used          __attribute__((__section__(  initcall6.init ))    =   i2c_init ; 

在 initcall6.init  這個 section 里面定義了一個函數指針變量  __initcall_i2c_init6  , 指針賦值為  i2c_init 函數地址; 

調用

do_basic_setup -> do_initcalls  里面會 遍歷  __initcall6.init 這個section 里面的函數指針,一個個調用 對應的函數; 即會調用到  i2c_init 函數 ; 

 

作為 外部 xx.ko 模塊時的module_init 

編譯

module_init(xx)  即是 希望 在 insmod xx.ko 時 調用 xx 函數; 

 131#define module_init(initfn)                                     \
 132        static inline initcall_t __maybe_unused __inittest(void)                \
 133        { return initfn; }                                      \
 134        int init_module(void) __copy(initfn) __attribute__((alias(#initfn)));

 

132  定義 一個 static  inline 的函數; 返回值 為 initcall_t  即 int ;  名稱為   __inittest ; 

   static  inline  int  __inittest(){

            return initfn ; 

   }

  這個的關鍵 在於  檢查 返回值;   如果  initfn 返回值 不是 int , 就會打印錯誤信息; 

GPIO/fsl-gpio.c:46: warning: return from incompatible pointer type

 

133 行  - 為  initfn 定義一個別名函數    init_module  ; 關於gcc alias 定義別名函數, 參考這兒

https://www.cnblogs.com/zhangzhiwei122/p/16125210.html

 

這樣,每個 xx.ko 里面肯定有一個 函數   init_module , 它是代碼中的   module_init(xx) 定義的函數的別名 ;

調用流程

insmod 調用 init_module 函數

/include/linux/module.h 有 module 結構體定義; 里面有 init 指針,指向 模塊的 init 函數; 

 361struct module {
 362        enum module_state state;

 
 423        /* Startup function. */
 424        int (*init)(void);

在 kernel/module.c 中  do_init_module 函數 會 觸發  mod->init 函數的調用。

3604static noinline int do_init_module(struct module *mod)
3605{

3622        do_mod_ctors(mod);
3623        /* Start the module */
3624        if (mod->init != NULL)
3625                ret = do_one_initcall(mod->init);

 

從 insertmod 到  do_init_module 的過程,參考:

 

模塊加載流程上

https://blog.csdn.net/lidan113lidan/article/details/119813256

模塊加載流程下

https://blog.csdn.net/lidan113lidan/article/details/119813552

 

do_one_initcall 就是 init/main.c 中定義的,之前在 do_initcalls 里面也使用的,調用 1 個 init 函數; 

1207int __init_or_module do_one_initcall(initcall_t fn)
1208{
1209        int count = preempt_count();
1210        char msgbuf[64];
1211        int ret;
1212
1213        if (initcall_blacklisted(fn))
1214                return -EPERM;
1215
1216        do_trace_initcall_start(fn);
1217        ret = fn();
1218        do_trace_initcall_finish(fn, ret);
1219
1220        msgbuf[0] = 0;
1221
1222        if (preempt_count() != count) {
1223                sprintf(msgbuf, "preemption imbalance ");
1224                preempt_count_set(count);
1225        }
1226        if (irqs_disabled()) {
1227                strlcat(msgbuf, "disabled interrupts ", sizeof(msgbuf));
1228                local_irq_enable();
1229        }
1230        WARN(msgbuf[0], "initcall %pS returned with %s\n", fn, msgbuf);
1231
1232        add_latent_entropy();
1233        return ret;
1234}

 

下一篇:

設備驅動-i2c驅動-module_i2c_driver的使用

https://www.cnblogs.com/zhangzhiwei122/p/16125079.html

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM