linux内核 extern,Linux内核之_attribute_
GCC 的一大特色就是__attribute__機(jī)制。
__attribute__可以設(shè)置函數(shù)屬性(Function Attribute)、變量屬性(Variable Attribute)和類(lèi)型屬性(Type Attribute)。
attribute前后都有兩個(gè)下劃線(xiàn),后面會(huì)緊跟一對(duì)原括弧,括弧里面是相應(yīng)的__attribute__參數(shù)。
語(yǔ)法格式為:__attribute__ ((attribute-list))
其位置約束為:放于聲明尾部“;”之前。
函數(shù)屬性(Function Attribute)
函數(shù)屬性可以幫助開(kāi)發(fā)者把一些特性添加到函數(shù)聲明中,從而可以使編譯器在錯(cuò)誤檢查方面的功能更強(qiáng)大
__attribute__ format
該__attribute__屬性可以給被聲明的函數(shù)加上類(lèi)似printf或者scanf的特征,它可以使編譯器檢查函數(shù)聲明和函數(shù)實(shí)際調(diào)用參數(shù)之間的格式化字符串是否匹配。該功能十分有用,尤其是處理一些很難發(fā)現(xiàn)的bug。
format的語(yǔ)法格式為:
format (archetype, string-index, first-to-check)
format屬性告訴編譯器,按照printf, scanf, strftime或strfmon的參數(shù)表格式規(guī)則對(duì)該函數(shù)的參數(shù)進(jìn)行檢查。“archetype”指定是哪種風(fēng)格;“string-index”指定傳入函數(shù)的第幾個(gè)參數(shù)是格式化字符串;“first-to-check”指定從函數(shù)的第幾個(gè)參數(shù)開(kāi)始按上述規(guī)則進(jìn)行檢查。
具體使用格式如下:
__attribute__((format(printf,m,n)))
__attribute__((format(scanf,m,n)))
其中參數(shù)m與n的含義為:
m:第幾個(gè)參數(shù)為格式化字符串(format string);
n:參數(shù)集合中的第一個(gè),即參數(shù)“…”里的第一個(gè)參數(shù)在函數(shù)參數(shù)總數(shù)排在第幾,注意,有時(shí)函數(shù)參數(shù)里還有“隱身”的呢,后面會(huì)提到;
在使用上,__attribute__((format(printf,m,n)))是常用的,而另一種卻很少見(jiàn)到。下面舉例說(shuō)明,其中myprint為自己定義的一個(gè)帶有可變參數(shù)的函數(shù),其功能類(lèi)似于printf:
//m=1;n=2
extern void myprint(const char *format,...) __attribute__((format(printf,1,2)));
//m=2;n=3
extern void myprint(int l,const char *format,...) __attribute__((format(printf,2,3)));
需要特別注意的是,如果myprint是一個(gè)函數(shù)的成員函數(shù),那么m和n的值可有點(diǎn)“懸乎”了,例如:
//m=3;n=4
extern void myprint(int l,const char *format,...) __attribute__((format(printf,3,4)));
其原因是,類(lèi)成員函數(shù)的第一個(gè)參數(shù)實(shí)際上一個(gè)“隱身”的“this”指針。(有點(diǎn)C++基礎(chǔ)的都知道點(diǎn)this指針,不知道你在這里還知道嗎?)
__attribute__ noreturn
該屬性通知編譯器函數(shù)從不返回值,當(dāng)遇到類(lèi)似函數(shù)需要返回值而卻不可能運(yùn)行到返回值處就已經(jīng)退出來(lái)的情況,該屬性可以避免出現(xiàn)錯(cuò)誤信息。C庫(kù)函數(shù)中的abort()和exit()的聲明格式就采用了這種格式,如下所示:
extern void exit(int) __attribute__((noreturn));
extern void abort(void) __attribute__((noreturn));
__attribute__ const
該屬性只能用于帶有數(shù)值類(lèi)型參數(shù)的函數(shù)上。當(dāng)重復(fù)調(diào)用帶有數(shù)值參數(shù)的函數(shù)時(shí),由于返回值是相同的,所以此時(shí)編譯器可以進(jìn)行優(yōu)化處理,除第一次需要運(yùn)算外,其它只需要返回第一次的結(jié)果就可以了,進(jìn)而可以提高效率。該屬性主要適用于沒(méi)有靜態(tài)狀態(tài)(static state)和副作用的一些函數(shù),并且返回值僅僅依賴(lài)輸入的參數(shù)。
為了說(shuō)明問(wèn)題,下面舉個(gè)非常“糟糕”的例子,該例子將重復(fù)調(diào)用一個(gè)帶有相同參數(shù)值的函數(shù),具體如下:
extern int square(int n) __attribute__((const));
for (i = 0; i < 100; i++ )
{
total += square(5) + i;
}
通過(guò)添加__attribute__((const))聲明,編譯器只調(diào)用了函數(shù)一次,以后只是直接得到了相同的一個(gè)返回值。
同時(shí)使用多個(gè)屬性
可以在同一個(gè)函數(shù)聲明里使用多個(gè)__attribute__,并且實(shí)際應(yīng)用中這種情況是十分常見(jiàn)的。使用方式上,你可以選擇兩個(gè)單獨(dú)的__attribute__,或者把它們寫(xiě)在一起,可以參考下面的例子:
extern void die(const char *format, ...)
__attribute__((noreturn))
__attribute__((format(printf, 1, 2)));
section ("section-name")
屬性 section 用于函數(shù)和變量,通常編譯器將函數(shù)放在 .text 節(jié),變量放在.data 或 .bss 節(jié),使用 section 屬性,可以讓編譯器將函數(shù)或變量放在指定的節(jié)中。
例如:
++++ include/linux/init.h
78: #define __init __attribute__ ((__section__ (".text.init")))
79: #define __exit __attribute__ ((unused, __section__(".text.exit")))
80: #define __initdata __attribute__ ((__section__ (".data.init")))
81: #define __exitdata __attribute__ ((unused, __section__ (".data.exit")))
82: #define __initsetup __attribute__ ((unused,__section__ (".setup.init")))
83: #define __init_call __attribute__ ((unused,__section__ (".initcall.init")))
84: #define __exit_call __attribute__ ((unused,__section__ (".exitcall.exit")))
連接器可以把相同節(jié)的代碼或數(shù)據(jù)安排在一起,Linux 內(nèi)核很喜歡使用這種技術(shù),
例如系統(tǒng)的初始化代碼被安排在單獨(dú)的一個(gè)節(jié),在初始化結(jié)束后就可以釋放這部分內(nèi)存。
* aligned (ALIGNMENT)
屬性 aligned 用于變量、結(jié)構(gòu)或聯(lián)合類(lèi)型,指定變量、結(jié)構(gòu)域、結(jié)構(gòu)或聯(lián)合的對(duì)齊量,以字節(jié)為單位,
例如:
++++ include/asm-i386/processor.h
294: struct i387_fxsave_struct {
295: unsigned short cwd;
296: unsigned short swd;
297: unsigned short twd;
298: unsigned short fop;
299: long fip;
300: long fcs;
301: long foo;
......
308: } __attribute__ ((aligned (16)));
表示該結(jié)構(gòu)類(lèi)型的變量以 16 字節(jié)對(duì)齊。通常編譯器會(huì)選擇合適的對(duì)齊量,顯示指
定對(duì)齊通常是由于體系限制、優(yōu)化等原因。
* packed
屬性 packed 用于變量和類(lèi)型,用于變量或結(jié)構(gòu)域時(shí)表示使用最小可能的對(duì)齊,用
于枚舉、結(jié)構(gòu)或聯(lián)合類(lèi)型時(shí)表示該類(lèi)型使用最小的內(nèi)存。例如:
++++ include/asm-i386/desc.h
51: struct Xgt_desc_struct {
52: unsigned short size;
53: unsigned long address __attribute__((packed));
54: };
域 address 將緊接著 size 分配。屬性 packed 的用途大多是定義硬件相關(guān)的結(jié)
構(gòu),使元素之間沒(méi)有因?qū)R而造成的空洞。
當(dāng)前函數(shù)名
==========
GNU CC 預(yù)定義了兩個(gè)標(biāo)志符保存當(dāng)前函數(shù)的名字,__FUNCTION__ 保存函數(shù)在源碼
中的名字,__PRETTY_FUNCTION__ 保存帶語(yǔ)言特色的名字。在 C 函數(shù)中,這兩個(gè)
名字是相同的,在 C++ 函數(shù)中,__PRETTY_FUNCTION__ 包括函數(shù)返回類(lèi)型等額外
信息,Linux 內(nèi)核只使用了 __FUNCTION__。
++++ fs/ext2/super.c
98: void ext2_update_dynamic_rev(struct super_block *sb)
99: {
100: struct ext2_super_block *es = EXT2_SB(sb)->s_es;
101:
102: if (le32_to_cpu(es->s_rev_level) > EXT2_GOOD_OLD_REV)
103: return;
104:
105: ext2_warning(sb, __FUNCTION__,
106: "updating to rev %d because of new feature flag, "
107: "running e2fsck is recommended",
108: EXT2_DYNAMIC_REV);
這里 __FUNCTION__ 將被替換為字符串 "ext2_update_dynamic_rev"。雖然
__FUNCTION__ 看起來(lái)類(lèi)似于標(biāo)準(zhǔn) C 中的 __FILE__,但實(shí)際上 __FUNCTION__
是被編譯器替換的,不象 __FILE__ 被預(yù)處理器替換。
內(nèi)建函數(shù)
GNU C 提供了大量的內(nèi)建函數(shù),其中很多是標(biāo)準(zhǔn) C 庫(kù)函數(shù)的內(nèi)建版本,例如
memcpy,它們與對(duì)應(yīng)的 C 庫(kù)函數(shù)功能相同,本文不討論這類(lèi)函數(shù),其他內(nèi)建函數(shù)
的名字通常以 __builtin 開(kāi)始。
* __builtin_return_address (LEVEL)
內(nèi)建函數(shù) __builtin_return_address 返回當(dāng)前函數(shù)或其調(diào)用者的返回地址,參數(shù)
LEVEL 指定在棧上搜索框架的個(gè)數(shù),0 表示當(dāng)前函數(shù)的返回地址,1 表示當(dāng)前函數(shù)
的調(diào)用者的返回地址,依此類(lèi)推。例如:
++++ kernel/sched.c
437: printk(KERN_ERR "schedule_timeout: wrong timeout "
438: "value %lx from %p\n", timeout,
439: __builtin_return_address(0));
* __builtin_constant_p(EXP)
內(nèi)建函數(shù) __builtin_constant_p 用于判斷一個(gè)值是否為編譯時(shí)常數(shù),如果參數(shù)
EXP 的值是常數(shù),函數(shù)返回 1,否則返回 0。例如:
++++ include/asm-i386/bitops.h
249: #define test_bit(nr,addr) \
250: (__builtin_constant_p(nr) ? \
251: constant_test_bit((nr),(addr)) : \
252: variable_test_bit((nr),(addr)))
很多計(jì)算或操作在參數(shù)為常數(shù)時(shí)有更優(yōu)化的實(shí)現(xiàn),在 GNU C 中用上面的方法可以
根據(jù)參數(shù)是否為常數(shù),只編譯常數(shù)版本或非常數(shù)版本,這樣既不失通用性,又能在
參數(shù)是常數(shù)時(shí)編譯出最優(yōu)化的代碼。
* __builtin_expect(EXP, C)
內(nèi)建函數(shù) __builtin_expect 用于為編譯器提供分支預(yù)測(cè)信息,其返回值是整數(shù)表
達(dá)式 EXP 的值,C 的值必須是編譯時(shí)常數(shù)。例如:
++++ include/linux/compiler.h
13: #define likely(x) __builtin_expect((x),1)
14: #define unlikely(x) __builtin_expect((x),0)
++++ kernel/sched.c
564: if (unlikely(in_interrupt())) {
565: printk("Scheduling in interrupt\n");
566: BUG();
567: }
這個(gè)內(nèi)建函數(shù)的語(yǔ)義是 EXP 的預(yù)期值是 C,編譯器可以根據(jù)這個(gè)信息適當(dāng)?shù)刂嘏?/p>
語(yǔ)句塊的順序,使程序在預(yù)期的情況下有更高的執(zhí)行效率。上面的例子表示處于中
斷上下文是很少發(fā)生的,第 565-566 行的目標(biāo)碼可能會(huì)放在較遠(yuǎn)的位置,以保證
經(jīng)常執(zhí)行的目標(biāo)碼更緊湊。
總結(jié)
以上是生活随笔為你收集整理的linux内核 extern,Linux内核之_attribute_的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 软件测试工作常用linux命令,软件测试
- 下一篇: gitlab linux版本下载,Lin