歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

module_init宏分析

一直在使用module_init和module_exit宏,但是對於這兩個宏依然比較陌生,不知道它到底為程序員做了什麼東西,今天閒來無事,就打開source insight分析了下它的具體實現,瞬間明白了,現在將筆記整理下。以經典的hello_world模塊為例,在模塊實現文件中,我們編寫了如下語句:

module_init(hello_init);

那麼它會被編譯器展開成什麼樣子呢?

在文件linux/init.h中定義了module_init.

#define module_init(x) __initcall(x);
#define __initcall(fn) device_initcall(fn)

#define device_initcall(fn) __define_initcall("6",fn,6)

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

那麼在編譯時刻,編譯器會將其轉化為如下語句
static initcall_t __initcall_hello_init6 __used __attribute__((__section__(".initcall6 .init"))) = hello_init;
實際上定義了一個類型為函數指針的變量__initcall_hello_init6,存放的是hello_init函數地址.
其中initcall_t是一個函數指針 typedef int (*initcall_t)(void); //linux/init.h中定義,
__used也是一個宏,在使用GCC3.4之前的編譯器被展開成__attribute__((unused))來禁止編譯器彈出有關函數沒有被用到的的警告信息。在3.4之後被展開成__attribute__((used))功能一樣
__attribute__((__section__(".initcall6 .init")))將該變量加載到段.initcall6.init上,在鏈接腳本頭文件上可以找到如下語句:

#define INITCALLS \
*(.initcallearly.init)\
VMLINUX_SYMBOL(__early_initcall_end) = .;\
  *(.initcall0.init)\
  *(.initcall0s.init)\
  *(.initcall1.init)\
  *(.initcall1s.init)\
  *(.initcall2.init)\
  *(.initcall2s.init)\
  *(.initcall3.init)\
  *(.initcall3s.init)\
  *(.initcall4.init)\
  *(.initcall4s.init)\
  *(.initcall5.init)\
  *(.initcall5s.init)\
*(.initcallrootfs.init)\
  *(.initcall6.init)\
  *(.initcall6s.init)\
  *(.initcall7.init)\
  *(.initcall7s.init)

編譯過內核後,生成的vmlinux.lds文件中有如下語句:

__initcall_start = .; *(.initcallearly.init) __early_initcall_end = .; *(.initcall0.init) *(.initcall0s.init) *(.initcall1.init) *(.initcall1s.init) *(.initcall2.init) *(.initcall2s.init) *(.initcall3.init) *(.initcall3s.init) *(.initcall4.init) *(.initcall4s.init) *(.initcall5.init) *(.initcall5s.init) *(.initcallrootfs.init) *(.initcall6.init) *(.initcall6s.init) *(.initcall7.init) *(.initcall7s.init) __initcall_end = .;

當insmod的時候,內核從initcall6.init段中讀取到該地址,然後跳轉到該地址去執行,所以就打印出hello_init語句中實現的輸出.

Copyright © Linux教程網 All Rights Reserved