您好,登錄后才能下訂單哦!
這篇文章主要介紹“怎么使用Linux內核模塊”,在日常操作中,相信很多人在怎么使用Linux內核模塊問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”怎么使用Linux內核模塊”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!
Linux內核使用xx_initcall_xx(fn)
的宏來定義內核模塊。
宏定義文件:include/linux/init.h
,定義如下:
/* * Early initcalls run before initializing SMP. * * Only for built-in code, not modules. */#define early_initcall(fn) __define_initcall(fn, early)/* * A "pure" initcall has no dependencies on anything else, and purely * initializes variables that couldn't be statically initialized. * * This only exists for built-in code, not for modules. * Keep main.c:initcall_level_names[] in sync. */#define pure_initcall(fn) __define_initcall(fn, 0)#define core_initcall(fn) __define_initcall(fn, 1)#define core_initcall_sync(fn) __define_initcall(fn, 1s)#define postcore_initcall(fn) __define_initcall(fn, 2)#define postcore_initcall_sync(fn) __define_initcall(fn, 2s)#define arch_initcall(fn) __define_initcall(fn, 3)#define arch_initcall_sync(fn) __define_initcall(fn, 3s)#define subsys_initcall(fn) __define_initcall(fn, 4)#define subsys_initcall_sync(fn) __define_initcall(fn, 4s)#define fs_initcall(fn) __define_initcall(fn, 5)#define fs_initcall_sync(fn) __define_initcall(fn, 5s)#define rootfs_initcall(fn) __define_initcall(fn, rootfs)#define device_initcall(fn) __define_initcall(fn, 6)#define device_initcall_sync(fn) __define_initcall(fn, 6s)#define late_initcall(fn) __define_initcall(fn, 7)#define late_initcall_sync(fn) __define_initcall(fn, 7s)
其中:early_initcall(fn)
只針對內核的核心代碼,不能描述模塊。
從上面代碼可以看出,每個宏的實現都是__define_initcall()
,其定義如下:
#define __define_initcall(fn, id) \ static initcall_t __initcall_##fn##id __used \ __attribute__((__section__(".initcall" #id ".init"))) = fn; \ LTO_REFERENCE_INITCALL(__initcall_##fn##id) typedef int (*initcall_t)(void);
對于上面定義,需要關注以下幾點:
1、initcall_t
:是一個函數指針類型,定義__initcall_##fn##id
,指向fn
。
2、__used
:在文件include/linux/compiler-gcc.h
中定義為:# define __used __attribute__((__used__))
,通知編譯器在目標文件中保留一個靜態函數,即使該函數未被使用。
3、__attribute__((__section__(".initcall" #id ".init")))
:定義的函數指針位于.initcall*.init
段中。
在文件include/asm-generic/vmlinux.lds.h
中,定義了宏INIT_CALLS
,定義如下:
#define INIT_CALLS_LEVEL(level) \ VMLINUX_SYMBOL(__initcall##level##_start) = .; \ *(.initcall##level##.init) \ *(.initcall##level##s.init) \#define INIT_CALLS \ VMLINUX_SYMBOL(__initcall_start) = .; \ *(.initcallearly.init) \ INIT_CALLS_LEVEL(0) \ INIT_CALLS_LEVEL(1) \ INIT_CALLS_LEVEL(2) \ INIT_CALLS_LEVEL(3) \ INIT_CALLS_LEVEL(4) \ INIT_CALLS_LEVEL(5) \ INIT_CALLS_LEVEL(rootfs) \ INIT_CALLS_LEVEL(6) \ INIT_CALLS_LEVEL(7) \ VMLINUX_SYMBOL(__initcall_end) = .;
在文件arch/arm64/kernel/vmlinux.lds.S
中,設置了宏INIT_CALLS
在.init.data
段中的分布位置,內容如下:
.init.data : { INIT_DATA INIT_SETUP(16) INIT_CALLS CON_INITCALL SECURITY_INITCALL INIT_RAM_FS }
在Linux內核編譯鏈接后,會生成文件arch/arm64/kernel/vmlinux.lds
。在該文件中將展開宏INIT_CALLS
,并分配到.init.data
段中,內容如下:
.init.data : {... __initcall_start = .; *(.initcallearly.init) __initcall0_start = .; *(.initcall0.init) *(.initcall0s.init) __initcall1_start = .; *(.initcall1.init) *(.initcall1s.init) __initcall2_start = .; *(.initcall2.init) *(.initcall2s.init) __initcall3_start = .; *(.initcall3.init) *(.initcall3s.init) __initcall4_start = .; *(.initcall4.init) *(.initcall4s.init) __initcall5_start = .; *(.initcall5.init) *(.initcall5s.init) __initcallrootfs_start = .; *(.initcallrootfs.init) *(.initcallrootfss.init) __initcall6_start = .; *(.initcall6.init) *(.initcall6s.init) __initcall7_start = .; *(.initcall7.init) *(.initcall7s.init) __initcall_end = .;... }
注:在__initcall_start
和__initcall_end
之間是按照從0到7的順序進行排列,對應pure_initcall(fn)
到late_initcall_sync(fn)
。
Linux內核模塊可以直接編譯到內核映像,在內核啟動時加載;也可以在系統啟動后,通過insmod
命令加載到內核。
Linux內核啟動時,kernel_init
線程會實現靜態編譯的內核模塊加載。
1、程序調用流程
## kernel/init/main.cstart_kernel()-> rest_init()-> kernel_thread(kernel_init, NULL, CLONE_FS) ## 1.創建內核線程kernel_init()-> kernel_init_freeable()-> do_basic_setup()-> do_initcalls()-> ## 2.模塊加載過程 do_initcall_level()-> do_one_initcall()
2、do_initcalls()
函數
函數功能:按順序掃描.init.data
段中的每個等級,即:從__initcall0_start
到__initcall_end
。
## __initdata中的定義在static initcall_t *initcall_levels[] __initdata = { __initcall0_start, __initcall1_start, __initcall2_start, __initcall3_start, __initcall4_start, __initcall5_start, __initcall6_start, __initcall7_start, __initcall_end,};static void __init do_initcalls(void){ int level; for (level = 0; level < ARRAY_SIZE(initcall_levels) - 1; level++) do_initcall_level(level);}
3、do_initcall_level()
函數
函數功能:在同一個等級中,按順序掃描.initcall*.init
到.initcall*s.init
static void __init do_initcall_level(int level){ ... for (fn = initcall_levels[level]; fn < initcall_levels[level+1]; fn++) do_one_initcall(*fn);}
4、do_one_initcall()
函數
函數功能:調用每個定義的模塊函數。
int __init_or_module do_one_initcall(initcall_t fn){ ... if (initcall_debug) ret = do_one_initcall_debug(fn); else ret = fn(); ##調用某個定義的initcall函數 ... return ret;}
注:每個fn()
對應第一部分模塊定義中的fn
,例:device_initcall(fn)
。
從上面函數的執行流程可以看出內核模塊加載優先級如下:
early_initcall(fn) ## 優先級最高,后續優先級依次降低pure_initcall(fn)core_initcall(fn)core_initcall_sync(fn)postcore_initcall(fn)postcore_initcall_sync(fn)arch_initcall(fn)arch_initcall_sync(fn)subsys_initcall(fn)subsys_initcall_sync(fn)fs_initcall(fn)fs_initcall_sync(fn)rootfs_initcall(fn)device_initcall(fn)device_initcall_sync(fn)late_initcall(fn)late_initcall_sync(fn) ## 優先級最低
Linux內核模塊的優先級決定了模塊的加載順序,在驅動開發時,需要關注有啟動順序要求的模塊定義。
到此,關于“怎么使用Linux內核模塊”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。