linux初始化宏__init, __exit

咱們在內核中常常遇到初始化函數是這樣定義的:static int __init init_func(); ,與普通函數相比,定義中多了__init。那麼,__init是什麼意思呢?還有與其匹配的__exit呢?html

__init* macrolinux

__init定義在:include/linux/init.h編程

#define __init __attribute__ ((__section__ (".init.text")))
#define __initdata __attribute__ ((__section__ (".init.data")))數組

It tells the compiler to put the variable or the function in a special section, which is declared in vmlinux.lds. init puts the function in the ".init.text" section and initdata puts the data in the ".init.data" section.
譯文:__init宏告知編譯器,將變量或函數放在一個特殊的區域,這個區域定義在vmlinux.lds中。__init將函數放在".init.text"這個代碼區中,__initdata將數據放在".init.data"這個數據區中。數據結構

標記爲初始化的函數,代表該函數供在初始化期間使用。在模塊裝載以後,模塊裝載就會將初始化函數扔掉。這樣能夠將該函數佔用的內存釋放出來。函數

__exit* macro
__exit定義在:include/linux/init.hpost

#ifdef MODULE #define __exit __attribute__ ((__section__(".exit.text")))
#else
#define __exit __attribute_used__ __attribute__((__section__(".exit.text")))
#endif

The exit macro tells the compiler to put the function in the ".exit.text" section. The exit_data macro tells the compiler to put the data in the ".exit.data" section.
exit.* sections make sense only for the modules : exit functions will never be called if compiled statically. That's why there is a ifdef : exit.* sections will be discarded only if modules support is disabled.
譯文:__exit宏告知編譯器,將函數放在".exit.text"這個區域中。__exitdata宏則告知編譯器將數據放在".exit.data"這個區域中。
exit.*區域僅僅對於模塊是有用的:若是編譯穩定的話,exit函數將永遠不會被調用。只有當模塊支持無效的時候,exit.*區域將被丟棄。這就是爲何定義中會出現ifdef。ui

Prototype of a module
A module must use the init and exit macros. Here is a prototype of a module :spa

#include <linux/module.h> #include <linux/kernel.h> #define MODULE_AUTHOR "tyler@agat.net" #define MODULE_DESC "Description of the module" int __init init_function(void) {   /* Do something */   if (err)     return -ERR;   return 0; } void __exit exit_function() {   /* Do something */ } module_init(init_function); module_exit(exit_function); MODULE_LICENSE("GPL"); MODULE_AUTHOR(MODULE_AUTHOR); MODULE_DESCRIPTION(MODULE_DESC);

1)全部標識爲__init的函數,在連接的時候,都放在.init.text這個區域中。在這個區域中,函數的擺放順序是和連接順序有關的,是不肯定的。
2)全部的__init函數在區域.initcall.init中還保存了一份函數指針。在初始化時,內核會經過這些函數指針調用這些__init函數,並在整個初始化完成後,釋放整個init區域 (包括.init.text, .initcall.init...)
注:這些函數在內核初始化過程當中的調用順序只和這裏的函數指針順序有關,和1)中所述的這些函數代碼自己在.init.text區域中的順序無關。.net

在2.4內核中,這些函數指針的順序也是和連接順序有關的,是不肯定的。
在2.6內核中,.initcall.init區域又分紅了7個子區域,分別是:

.initcall1.init .initcall2.init .initcall3.init .initcall4.init .initcall5.init .initcall6.init .initcall7.init

當須要把函數fn放到.initcall1.init區域時,只要聲明core_initcall(fn); 便可。
其餘的各個區域的定義方法分別是:

core_initcall(fn)-->.initcall1.init postcore_initcall(fn)-->.initcall2.init arch_initcall(fn)-->.initcall3.init subsys_initcall(fn)-->.initcall4.init fs_initcall(fn)-->.initcall5.init device_initcall(fn)-->.initcall6.init late_initcall(fn)-->.initcall7.init

而與2.4兼容的initcall(fn)則等價於device_initcall(fn).
各個子區域之間的順序是肯定的,即先調用.initcall1.init中的函數指針,再調用.initcall2.init中的函數指針,等等。而在每一個子區域中的函數指針的順序是和連接順序相關的,是不肯定的。
在內核中,不一樣的init函數被放在不一樣的子區域中,所以也就決定了他們的調用順序。這樣也就解決了一些init函數之間必須保證必定的調用順序問題。
linux下 container_of()宏的簡要解析
ARRAY_SIZE 宏仍是比較有意思的,實際上是個c 的編程技巧,這個技巧頗有用哦!能夠在include/linux/kernel.h 中找到它的定義:
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
該宏能夠方便的求出一個數組中有多少數據成員,這在不少狀況下是頗有用的,好比對於 int a[]={1,5,65,23,12,20,3} 數組,可使用該宏求出a[] 有7 個元素。
Linux中__init、__devinit等初始化宏
在內核裏常常能夠看到__init, __devinit這樣的語句,這都是在init.h中定義的宏,gcc在編譯時會將被修飾的內容放到這些宏所表明的section。
其典型的定義以下:

#define __init __section(.init.text) __cold notrace
#define __initdata __section(.init.data)
#define __initconst __section(.init.rodata)
#define __exitdata __section(.exit.data)
#define __exit_call __used __section(.exitcall.exit)

其典型用法以下:

static int __init xxx_drv_init(void) {   return pci_register_driver(&xxx_driver); }

根據上面的定義與用法,xxx_drv_init()函數將會被link到.init.text段。
之因此加入這樣的宏,緣由有2:

1,一部份內核初始化機制依賴與它。
如kernel將初始化要執行的init函數,分爲7個級別,core_initcall, postcore_initcall, arch_initcall, subsys_initcall, fs_iitcall, device_initcall, late_initcall。這7個級別優先級遞減,即先執行core_initcall, 最後執行late_initcall。經過使用文中提到的宏,gcc會將初始化代碼按下面的結構安排:

在內核初始化時,從__initcall_start到__initcall_end之間的initcall被一次執行。
2,提升系統效率
初始化代碼的特色是,在系統啓動時運行,且一旦運行後立刻推出內存,再也不佔用內存。
================================================================================
經常使用的宏:
__init,標記內核啓動時所用的初始化代碼,內核啓動完成後就再也不使用。其所修飾的內容被放到.init.text section中。
__exit,標記模塊退出代碼,對非模塊無效。
__initdata,標記內核啓動時所用的初始化數據結構,內核啓動完成後再也不使用。其所修飾的內容被放到.init.data section中。
__devinit,標記設備初始化所用的代碼。
__devinitdata,標記設備初始化所用的數據結構。
__devexit,標記設備移除時所用的代碼。
xxx_initcall,7個級別的初始化函數
==================================================================================
driver中的使用:
module_init, module_exit函數所調用的函數,須要分別用__init和__exit來標記。
pci_driver數據結構不須要標記。
probe和remove函數用__devinit和__devexit來標記。
若是remove使用__devexit標記,則在pci_drvier結構中要用__devexit_p(remove)來引用remove函數。
若是不肯定需不須要添加宏,則不要添加。

轉自:http://blog.chinaunix.net/uid-24807808-id-3127876.html

相關文章
相關標籤/搜索