<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                > 引子:模塊化機制優點 模塊化機制(module)是Linux系統的一大創新,是Linux驅動開發和運行的基礎(當然,module并不僅僅是支撐驅動)。其優點在于: 1.在系統運行動態加載模塊,擴充內核的功能。不需要時可以卸載。 2\. 修改內核功能,不必重新全部編譯整改內核,只需要編譯相應模塊即可。 3.模塊目標代碼一旦被加載重定位到內核,其作用域和靜態鏈接的代碼完全等價。 本文重點闡述Linux module加載的來龍去脈,其中的奧秘就在于對宏module_init的解讀。 > 一、模塊例子 hello_module.c代碼如下: ~~~ #include /* Needed by all modules */ #include /* Needed for KERN_ALERT */ #include /*Needed for __init */ static int __init test_init(void){ printk(KERN_ALERT"Hello world!\n"); return 0; } static void __exit test_exit(void){ printk(KERN_ALERT"Goodbye world!\n"); } module_init(test_init); module_exit(test_exit); ~~~ > 二、模塊編程要點 1.頭文件 linux/module.h、linux/kernel.h、linux/init.h 2.?定義模塊的初始化函數test_init(名字任意)和卸載函數test_exit(名字任意)。 3.?用宏module_init聲明初始化函數,用宏module_exit聲明卸載函數。 > 三、模塊運行 模塊代碼有兩種運行的方式: 1.?編譯成可動態加載的module,并通過insmod來動態加載,接著進行初始化。 2.?靜態編譯鏈接進內核,在系統啟動過程中進行初始化。 有些模塊是必須要編譯到內核,和內核一起運行的,從不卸載,如vfs、platform_bus等等。 > 四、靜態鏈接和初始化 Make menuconfig時選擇將模塊編譯到內核即為靜態鏈接,或者直接在makefile文件中指定為obj-y +=hello_module.o 1module宏展開 頭文件路徑:include/linux/init.h //靜態編譯鏈接時沒有定義宏MODULE ~~~ #ifndef MODULE typedef int (*initcall_t)(void); #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); ~~~ 所以: ~~~ module_init(test_init)展開為: __initcall(test _init)-> device_initcall(test _init)-> __define_initcall("6", test _init,6)-> static?initcall_t?__initcall_test_init_6 __attribute__((__section__(".initcall6.init"))) = test_init; ~~~ 即是定義了一個類型為initcall_t的函數指針變量__initcall_test_init_6,并賦值為test_init,該變量在鏈接時會鏈接到section(.initcall6.init). 2linux鏈接腳本 路徑 arch/arm/kernel/vmlinux.ld.S ~~~ #include SECTIONS{ … INIT_CALLS … } ~~~ 路徑:include/ asm-generic/vmlinux.lds.h ~~~ #define INIT_CALLS \ VMLINUX_SYMBOL(__initcall_start) = .; \ INITCALLS \ VMLINUX_SYMBOL(__initcall_end) = .; #define INITCALLS \ …. *(.initcall6.init) \ … ~~~ 可見__initcall_test_init_6將會鏈接到section(.initcall6.init). 3初始化 在linux啟動的第三個階段kernel_init的函數里會調用: 路徑init/main.c ~~~ Kernel_init do_basic_setup do_initcalls static void __init do_initcalls(void){ initcall_t *fn; for (fn = __early_initcall_end; fn do_one_initcall(*fn); } ~~~ 即取出函數指針__initcall_test_init_6的值并進行調用,即執行test_init。 > 五、動態鏈接加載和初始化 Make menuconfig時選擇將模塊編譯成模塊即為動態鏈接,或者直接在makefile文件中指定為obj-m +=hello_module.o 編譯成模塊的命令是: make –C $KERNEL_PATH M=$MODULE_PATH modules 即使用linux根目錄下的makefile,執行該makefile下的modules偽目標,對當前模塊進行編譯。編譯的結果是可重定位文件,insmod加載時才完成最終的鏈接動作。 1Module編譯選項 Linux根目錄下的makefile定義了modules偽目標會用到的編譯選項。 //即定義宏MODULE,-D是GCC定義宏的語法。 MODFLAGS = -DMODULE //GCC編譯模塊代碼時會用到該選項,即定義宏MODULE。這與在頭文件中用#define MODULE是一樣的。 CFLAGS_MODULE = $(MODFLAGS) 2Module_init宏展開 頭文件路徑:include/linux/init.h ~~~ #ifndef MODULE /*編譯成module時定義了宏MODULE*/ #else /* MODULE obj-m*/ typedef int (*initcall_t)(void); #define module_init(initfn) \ static inline initcall_t __inittest(void) \ { return initfn; } \ int init_module(void) __attribute__((alias(#initfn))); ~~~ __inittest僅僅是為了檢測定義的函數是否符合initcall_t類型,如果不是__inittest類型在編譯時將會報錯。所以真正的宏定義是: ~~~ #define module_init(initfn) int init_module(void) __attribute__((alias(#initfn))); ~~~ alias屬性是GCC的特有屬性,將定義init_module為函數initfn的別名。所以module_init(test_init)的作用就是定義一個變量名init_module,其地址和test_init是一樣的。 3Hello_module.mod.c 編譯成module的模塊都會自動產生一個*.mod.c的文件,Hello_module.mod.c的內容如下: ~~~ struct module __this_module __attribute__((section(".gnu.linkonce.this_module"))) = { .name = KBUILD_MODNAME, .init = init_module, #ifdef CONFIG_MODULE_UNLOAD .exit = cleanup_module, #endif .arch = MODULE_ARCH_INIT, }; ~~~ 即定義了一個類型為module的全局變量__this_module,其成員init即為init_module,也即是test_init.并且該變量會鏈接到section(".gnu.linkonce.this_module"). 4動態加載 insmod是busybox提供的用戶層命令: 路徑busybox/modutils/ insmod.c ~~~ insmod_main bb_init_module init_module 路徑busybox/modutils/modutils.c: # define init_module(mod, len, opts) .\ syscall(__NR_init_module, mod, len, opts) ~~~ 該系統調用對應內核層的sys_init_module函數。 路徑:kernel/module.c SYSCALL_DEFINE3(init_module,…) //加載模塊的ko文件,并解釋各個section,重定位 mod = load_module(umod, len, uargs); //查找section(".gnu.linkonce.this_module") modindex = find_sec(hdr, sechdrs, secstrings, ".gnu.linkonce.this_module"); //找到Hello_module.mod.c定義的module數據結構 mod = (void *)sechdrs[modindex].sh_addr; if (mod->init != NULL) ret = do_one_initcall(mod->init); //調用test_init. 模塊的傳參、符號導出、模塊依賴等機制以后再另文描述 更多原創技術分享敬請關注微信公眾號:嵌入式企鵝圈 ![](https://box.kancloud.cn/2016-01-13_5695f8f8d24b2.jpg)
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看