日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

module_init和module_exit的作用

發(fā)布時間:2024/8/1 编程问答 45 豆豆
生活随笔 收集整理的這篇文章主要介紹了 module_init和module_exit的作用 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

轉(zhuǎn)自:http://www.360doc.com/content/11/0917/11/7473909_148946026.shtml

就像你寫C程序需要包含C庫的頭文件那樣,Linux內(nèi)核編程也需要包含Kernel頭文件,大多的Linux驅(qū)動程序需要包含下面三個頭文件:

#include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h>

其中,init.h 定義了驅(qū)動的初始化和退出相關(guān)的函數(shù),kernel.h 定義了經(jīng)常用到的函數(shù)原型及宏定義,module.h 定義了內(nèi)核模塊相關(guān)的函數(shù)、變量及宏。

幾乎每個linux驅(qū)動都有個module_init(與module_exit的定義在Init.h (/include/linux) 中)。沒錯,驅(qū)動的加載就靠它。為什么需要這樣一個宏?原因是按照一般的編程想法,各部分的初始化函數(shù)會在一個固定的函數(shù)里調(diào)用比如:

void init(void){init_a();init_b();}

如果再加入一個初始化函數(shù)呢,那么在init_b()后面再加一行:init_c();這樣確實能完成我們的功能,但這樣有一定的問題,就是不能獨立的添加初始化函數(shù),每次添加一個新的函數(shù)都要修改init函數(shù)。可以采用另一種方式來處理這個問題,只要用一個宏來修飾一下:

void init_a(void){}__initlist(init_a, 1);

它是怎么樣通過這個宏來實現(xiàn)初始化函數(shù)列表的呢?先來看__initlist的定義:

#define __init __attribute__((unused, __section__(".initlist")))#define __initlist(fn, lvl) / static initlist_t __init_##fn __init = { /magic: INIT_MAGIC, /callback: fn, /level: lvl }

請注意:section(".initlist"),這個屬性起什么作用呢?它告訴連接器這個變量存放在.initlist區(qū)段,如果所有的初始化函數(shù)都是用這個宏,那么每個函數(shù)會有對應的一個initlist_t結(jié)構(gòu)體變量存放在.initlist區(qū)段,也就是說我們可以在.initlist區(qū)段找到所有初始化函數(shù)的指針。怎么找到.initlist區(qū)段的地址呢?

extern u32 __initlist_start; extern u32 __initlist_end;

這兩個變量起作用了,__initlist_start是.initlist區(qū)段的開始,__initlist_end是結(jié)束,通過這兩個變量我們就可以訪問到所有的初始化函數(shù)了。這兩個變量在那定義的呢?在一個連接器腳本文件里

. = ALIGN(4);.initlist : {__initlist_start = .;*(.initlist)__initlist_end = .;}

這兩個變量的值正好定義在.initlist區(qū)段的開始和結(jié)束地址,所以我們能通過這兩個變量訪問到所有的初始化函數(shù)。

與此類似,內(nèi)核中也是用到這種方法,所以我們寫驅(qū)動的時候比較獨立,不用我們自己添加代碼在一個固定的地方來調(diào)用我們自己的初始化函數(shù)和退出函數(shù),連接器已經(jīng)為我們做好了。先來分析一下module_init。定義如下:

#define module_init(x) __initcall(x); //include/linux/init.h#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

如果某驅(qū)動想以func作為該驅(qū)動的入口,則可以如下聲明:module_init(func);被上面的宏處理過后,變成__initcall_func6 __used加入到內(nèi)核映像的".initcall"區(qū)。內(nèi)核的加載的時候,會搜索".initcall"中的所有條目,并按優(yōu)先級加載它們,普通驅(qū)動程序的優(yōu)先級是6。其它模塊優(yōu)先級列出如下:值越小,越先加載。

#define pure_initcall(fn) __define_initcall("0",fn,0)#define core_initcall(fn) __define_initcall("1",fn,1)#define core_initcall_sync(fn) __define_initcall("1s",fn,1s)#define postcore_initcall(fn) __define_initcall("2",fn,2)#define postcore_initcall_sync(fn) __define_initcall("2s",fn,2s)#define arch_initcall(fn) __define_initcall("3",fn,3)#define arch_initcall_sync(fn) __define_initcall("3s",fn,3s)#define subsys_initcall(fn) __define_initcall("4",fn,4)#define subsys_initcall_sync(fn) __define_initcall("4s",fn,4s)#define fs_initcall(fn) __define_initcall("5",fn,5)#define fs_initcall_sync(fn) __define_initcall("5s",fn,5s)#define rootfs_initcall(fn) __define_initcall("rootfs",fn,rootfs)#define device_initcall(fn) __define_initcall("6",fn,6)#define device_initcall_sync(fn) __define_initcall("6s",fn,6s)#define late_initcall(fn) __define_initcall("7",fn,7)#define late_initcall_sync(fn) __define_initcall("7s",fn,7s)

可以看到,被聲明為pure_initcall的最先加載。

module_init除了初始化加載之外,還有后期釋放內(nèi)存的作用。linux kernel中有很大一部分代碼是設(shè)備驅(qū)動代碼,這些驅(qū)動代碼都有初始化和反初始化函數(shù),這些代碼一般都只執(zhí)行一次,為了有更有效的利用內(nèi)存,這些代碼所占用的內(nèi)存可以釋放出來。

linux就是這樣做的,對只需要初始化運行一次的函數(shù)都加上__init屬性,__init 宏告訴編譯器如果這個模塊被編譯到內(nèi)核則把這個函數(shù)放到(.init.text)段,module_exit的參數(shù)卸載時同__init類似,如果驅(qū)動被編譯進內(nèi)核,則__exit宏會忽略清理函數(shù),因為編譯進內(nèi)核的模塊不需要做清理工作,顯然__init和__exit對動態(tài)加載的模塊是無效的,只支持完全編譯進內(nèi)核。

在kernel初始化后期,釋放所有這些函數(shù)代碼所占的內(nèi)存空間。連接器把帶__init屬性的函數(shù)放在同一個section里,在用完以后,把整個section釋放掉。當函數(shù)初始化完成后這個區(qū)域可以被清除掉以節(jié)約系統(tǒng)內(nèi)存。Kenrel啟動時看到的消息“Freeing unused kernel memory: xxxk freed”同它有關(guān)。

我們看源碼,init/main.c中start_kernel是進入kernel()的第一個c函數(shù),在這個函數(shù)的最后一行是rest_init();

static void rest_init(void) {.....kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);unlock_kernel();cpu_idle();..... }

創(chuàng)建了一個內(nèi)核線程,主函數(shù)kernel_init末尾有個函數(shù):

/** Ok, we have completed the initial bootup, and* we're essentially up and running. Get rid of the* initmem segments and start the user-mode stuff..*/init_post();

這個init_post中的第一句就是free_initmem();就是用來釋放初始化代碼和數(shù)據(jù)的。

void free_initmem(void) {if (!machine_is_integrator() && !machine_is_cintegrator()) {free_area((unsigned long)(&__init_begin),(unsigned long)(&__init_end),"init");} }

接下來就是kernel內(nèi)存管理的事了。

總結(jié)

以上是生活随笔為你收集整理的module_init和module_exit的作用的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。