亚洲av成人无遮挡网站在线观看,少妇性bbb搡bbb爽爽爽,亚洲av日韩精品久久久久久,兔费看少妇性l交大片免费,无码少妇一区二区三区

  免費注冊 查看新帖 |

Chinaunix

  平臺 論壇 博客 文庫
最近訪問板塊 發(fā)新帖
查看: 35231 | 回復(fù): 3
打印 上一主題 下一主題

linux kernel代碼分段之a(chǎn)ttribute宏 [復(fù)制鏈接]

論壇徽章:
0
跳轉(zhuǎn)到指定樓層
1 [收藏(0)] [報告]
發(fā)表于 2011-02-06 18:59 |只看該作者 |倒序瀏覽

1. gcc__attribute__編繹屬性

  要了解Linux Kernel代碼的分段信息,需要了解一下gcc__attribute__的編繹屬性,__attribute__主要用于改變所聲明或定義的函數(shù)或 數(shù)據(jù)的特性,它有很多子項,用于改變作用對象的特性。比如對函數(shù),noline將禁止進行內(nèi)聯(lián)擴展、noreturn表示沒有返回值、pure表明函數(shù)除 返回值外,不會通過其它(如全局變量、指針)對函數(shù)外部產(chǎn)生任何影響。但這里我們比較感興趣的是對代碼段起作用子項section。

__attribute__section子項的使用格式為:

__attribute__((section("section_name")))

其作用是將作用的函數(shù)或數(shù)據(jù)放入指定名為"section_name"輸入段。

這里還要注意一下兩個概念:輸入段和輸出段

輸入段和輸出段是相對于要生成最終的elfbinary時的Link過程說 的,Link過程的輸入大都是由源代碼編繹生成的目標文件.o,那么這些.o文件中包含的段相對link過程來說就是輸入段,而Link的輸出一般是可執(zhí) 行文件elf或庫等,這些輸出文件中也包含有段,這些輸出文件中的段就叫做輸出段。輸入段和輸出段本來沒有什么必然的聯(lián)系,是互相獨立,只是在Link過 程中,Link程序會根據(jù)一定的規(guī)則(這些規(guī)則其實來源于Link Script),將不同的輸入段重新組合到不同的輸出段中,即使是段的名字,輸入段和輸出段可以完全不同。

其用法舉例如下:

int  var __attribute__((section(".xdata"))) = 0;

這樣定義的變量var將被放入名為.xdata的輸入段,(注意:__attribute__這種用法中的括號好像很嚴格,這里的幾個括號好象一個也不能少。)

static int __attribute__((section(".xinit"))) functionA(void)

{

 .....

}

這個例子將使函數(shù)functionA被放入名叫.xinit的輸入段。

需要著重注意的是,__attribute__section屬性只指定對象的輸入段,它并不能影響所指定對象最終會放在可執(zhí)行文件的什么段。

2. Linux Kernel源代碼中與段有關(guān)的重要宏定義

關(guān)于__init__initdata、__exit__exitdata及類似的宏

 打開Linux Kernel源代碼樹中的文件:include/linux/init.h,可以看到有下面的宏定議:

#define __init  __attribute__ ((__section__ (".init.text")))  __cold

#define __initdata    __attribute__ (( __section__ (".init.data")))

#define __exitdata   __attribute__ (( __section__ (".exit.data")))

#define __exit_call  __attribute_used__ __attribute__ (( __section__ (".exitcall.exit")))

#define __init_refok  oninline __attribute__ ((__section__ (".text.init.refok")))

#define __initdata_refok __attribute__ ((__section__ (".data.init.refok")))

#define __exit_refok noinline __attribute__ ((__section__ (".exit.text.refok")))

.........

#ifdef MODULE

#define __exit  __attribute__ (( __section__ (".exit.text"))) __cold

#else

#define __exit __attribute_used__ __attribute__ ((__section__ (".exit.text"))) __cold

#endif

 對于經(jīng)常寫驅(qū)動模塊或翻閱Kernel源代碼的人,看到熟悉的宏了吧:__init, __initdata, __exit, __exitdata。

__init 宏最常用的地方是驅(qū)動模塊初始化函數(shù)的定義處,其目的是將驅(qū)動模塊的初始化函數(shù)放入名叫.init.text的輸入段。對于__initdata來說,用 于數(shù)據(jù)定義,目的是將數(shù)據(jù)放入名叫.init.data的輸入段。其它幾個宏也類似。另外需要注意的是,在以上定意中,用__section__代替了 section。還有其它一些類似的宏定義,這里不一一列出,其作用都是類似的。

關(guān)于initcall的一些宏定義

在該文件中,下面這條宏定議更為重要,它是一條可擴展的宏:

#define  __define_initcall(level,fn,id) \

     static initcall_t __initcall_##fn##id  __attribute_used__ \

    __attribute__ ((__section__(".initcall" level ".init"))) = fn

這條宏帶有3個參數(shù):level,fn, id,分析該宏可以看出:

。.其用來定義類型為initcall_tstatic函數(shù)指針,函數(shù)指針的名稱由參數(shù)fnid決 定:__initcall_##fn##id,這就是函數(shù)指針的名稱,它其實是一個變量名稱。從該名稱的定義方法我們其學(xué)到了宏定義的一種高級用法,即利 用宏的參數(shù)產(chǎn)生名稱,這要借助于"##"這一符號組合的作用。

。.  這一函數(shù)指針變量放入什么輸入段呢,請看__attribute__ ((__section__ (".initcall" levle ".init"))),輸入段的名稱由level決定,如果level="1",則輸入段是.initcall1.init,如果level="3s", 則輸入段是.initcall3s.init。這一函數(shù)指針變量就是放在用這種方法決定的輸入段中的。

 3這一定義的函數(shù)指針變量的初始值是什么叫,其實就是宏參數(shù)fn,實際使用中,fn其實就是真實定義好的函數(shù)。

該宏定義并不直接使用,請看接下來的這些宏定義:

 

#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)

這些宏定義出來是為了方便的使用__define_initcall宏定義的,上面每條宏第一次使用時都會產(chǎn)生一個新的輸入段。

接下來還有一條

#define __initcall(fn) device_initcall(fn)

這一條其實只是定義了另一個別名,即平常使用的__initcall其實就是這兒的device_initcall,用它定義的函數(shù)指定位于段.initcall6.init中。

. __setup宏的來源及使用

__setup這條宏在Linux Kernel中使用最多的地方就是定義處理Kernel啟動參數(shù)的函數(shù)及數(shù)據(jù)結(jié)構(gòu),請看下面的宏定義:

#define __setup_param(str, unique_id, fn, early)   \

 static char __setup_str_##unique_id[] __initdata __aligned(1) = str; \

 static struct obs_kernel_param __setup_##unique_id \

  __used __section(.init.setup)   \

  __attribute__((aligned((sizeof(long))))) \

  = { __setup_str_##unique_id, fn, early }

#define __setup(str, fn)     \

 __setup_param(str, fn, fn, 0)

使用Kernel中的例子分析一下這兩條定義:

__setup("root=",root_dev_setup);

這條語句出現(xiàn)在init/do_mounts.c中,其作用是處理Kernel啟動時的像root=/dev/mtdblock3之類的參數(shù)的。

分解一下這條語句,首先變?yōu)椋?/span>

__setup_param("root=",root_dev_setup,root_dev_setup,0);

繼續(xù)分解,將得到下面這段代嗎:

static char __setup_str_root_dev_setup_id[] __initdata __aligned(1) = "root=";

static struct obs_kernel_param __setup_root_dev_setup_id

  __used __section(.init.setup)

  __attribute__((aligned((sizeof(long)))))

  = { __setup_str_root_dev_setup_id, root_dev_setup, 0 };

這段代碼定義了兩個變量:字符數(shù)組變量__setup_str_root_dev_setup_id,其初始化內(nèi)容為"root=",由于該變量用 __initdata修飾,它將被放入.init.data輸入段;另一變量是結(jié)構(gòu)變量__setup_root_dev_setup_id,其類型為 struct obs_kernel_param, 該變理被放入輸入段.init.setup中。結(jié)構(gòu)struct struct obs_kernel_param也在該文件中定義如下:

struct obs_kernel_param {

 const char *str;

 int (*setup_func)(char *);

 int early;

};

變量__setup_root_dev_setup_id的三個成員分別被初始化為:

__setup_str_root_dev_setup_id --前面定義的字符數(shù)組變量,初始內(nèi)容為"root="。

root_dev_setup --通過宏傳過來的處理函數(shù)。

-->常量0,該成員的作用以后分析。

現(xiàn)在不難想像內(nèi)核啟動時怎么處理啟動參數(shù)的了:通過__setup宏定義obs_kernel_param結(jié)構(gòu)變量都被放入.init.setup 段中,這樣一來實際是使.init.setup段變成一張表,Kernel在處理每一個啟動參數(shù)時,都會來查找這張表,與每一個數(shù)據(jù)項中的成員str進行 比較,如果完全相同,就會調(diào)用該數(shù)據(jù)項的函數(shù)指針成員setup_func所指向的函數(shù)(該函數(shù)是在使用__setup宏定義該變量時傳入的函數(shù)參數(shù)), 并將啟動參數(shù)如root=后面的內(nèi)容傳給該處理函數(shù)。

論壇徽章:
0
2 [報告]
發(fā)表于 2016-07-10 14:50 |只看該作者
好文 多謝樓主分享

論壇徽章:
0
3 [報告]
發(fā)表于 2017-02-18 19:13 |只看該作者
您需要登錄后才可以回帖 登錄 | 注冊

本版積分規(guī)則 發(fā)表回復(fù)

  

北京盛拓優(yōu)訊信息技術(shù)有限公司. 版權(quán)所有 京ICP備16024965號-6 北京市公安局海淀分局網(wǎng)監(jiān)中心備案編號:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年舉報專區(qū)
中國互聯(lián)網(wǎng)協(xié)會會員  聯(lián)系我們:huangweiwei@itpub.net
感謝所有關(guān)心和支持過ChinaUnix的朋友們 轉(zhuǎn)載本站內(nèi)容請注明原作者名及出處

清除 Cookies - ChinaUnix - Archiver - WAP - TOP