真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

module_init/module_exit的用法

一個(gè)驅(qū)動(dòng)可以作為一個(gè)模塊動(dòng)態(tài)的加載到內(nèi)核里,也可以作為內(nèi)核的一部分靜態(tài)的編譯進(jìn)內(nèi)核,而module_init/module_exit的作用就是將驅(qū)動(dòng)以動(dòng)態(tài)的方式將驅(qū)動(dòng)加載到內(nèi)核。

專注于為中小企業(yè)提供成都做網(wǎng)站、網(wǎng)站制作服務(wù),電腦端+手機(jī)端+微信端的三站合一,更高效的管理,為中小企業(yè)蜀山免費(fèi)做網(wǎng)站提供優(yōu)質(zhì)的服務(wù)。我們立足成都,凝聚了一批互聯(lián)網(wǎng)行業(yè)人才,有力地推動(dòng)了上1000家企業(yè)的穩(wěn)健成長(zhǎng),幫助中小企業(yè)通過網(wǎng)站建設(shè)實(shí)現(xiàn)規(guī)模擴(kuò)充和轉(zhuǎn)變。

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

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

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

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

static void rest_init(void)
             {
                     .....
                     kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);
                     unlock_kernel();
                     cpu_idle();
                     .....
             }
             創(chuàng)建了一個(gè)內(nèi)核線程,主函數(shù)kernel_init末尾有個(gè)函數(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();
             這個(gè)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)存管理的事了。

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

#include
             #include
             #include  

其中,init.h 定義了驅(qū)動(dòng)的初始化和退出相關(guān)的函數(shù),(module_init , module_exit)

kernel.h  定義了經(jīng)常用到的函數(shù)原型及宏定義,

module.h 定義了內(nèi)核模塊相關(guān)的函數(shù)、變量及宏。

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

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

如果再加入一個(gè)初始化函數(shù)呢,那么在init_b()后面再加一行:init_c();這樣確實(shí)能完成我們 的功能,但這樣有一定的問題,就是不能獨(dú)立的添加初始化函數(shù),每次添加一個(gè)新的函數(shù)都要修改init函數(shù)??梢圆捎昧硪环N方式來處理這個(gè)問題,只要用一個(gè) 宏來修飾一下:

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

它是怎么樣通過這個(gè)宏來實(shí)現(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 }

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

extern u32 __initlist_start;
             extern u32 __initlist_end;

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

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

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

與此類似,內(nèi)核中也是用到這種方法,所以我們寫驅(qū)動(dòng)的時(shí)候比較獨(dú)立,不用我們自己添加代碼在一個(gè)固定的地方來調(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ū)動(dòng)想以func作為該驅(qū)動(dòng)的入口,則可以如下聲明:module_init(func);被上面的 宏處理過后,變成__initcall_func6 __used加入到內(nèi)核映像的".initcall"區(qū)。內(nèi)核的加載的時(shí)候,會(huì)搜索".initcall"中的所有條目,并按優(yōu)先級(jí)加載它們,普通驅(qū)動(dòng)程 序的優(yōu)先級(jí)是6。其它模塊優(yōu)先級(jí)列出如下:值越小,越先加載。

#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/module_exit的用法
網(wǎng)址分享:http://weahome.cn/article/jgsdhi.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部