linux内核 extern,Linux内核之_attribute_
GCC 的一大特色就是__attribute__機制。
__attribute__可以設置函數屬性(Function Attribute)、變量屬性(Variable Attribute)和類型屬性(Type Attribute)。
attribute前后都有兩個下劃線,后面會緊跟一對原括弧,括弧里面是相應的__attribute__參數。
語法格式為:__attribute__ ((attribute-list))
其位置約束為:放于聲明尾部“;”之前。
函數屬性(Function Attribute)
函數屬性可以幫助開發者把一些特性添加到函數聲明中,從而可以使編譯器在錯誤檢查方面的功能更強大
__attribute__ format
該__attribute__屬性可以給被聲明的函數加上類似printf或者scanf的特征,它可以使編譯器檢查函數聲明和函數實際調用參數之間的格式化字符串是否匹配。該功能十分有用,尤其是處理一些很難發現的bug。
format的語法格式為:
format (archetype, string-index, first-to-check)
format屬性告訴編譯器,按照printf, scanf, strftime或strfmon的參數表格式規則對該函數的參數進行檢查。“archetype”指定是哪種風格;“string-index”指定傳入函數的第幾個參數是格式化字符串;“first-to-check”指定從函數的第幾個參數開始按上述規則進行檢查。
具體使用格式如下:
__attribute__((format(printf,m,n)))
__attribute__((format(scanf,m,n)))
其中參數m與n的含義為:
m:第幾個參數為格式化字符串(format string);
n:參數集合中的第一個,即參數“…”里的第一個參數在函數參數總數排在第幾,注意,有時函數參數里還有“隱身”的呢,后面會提到;
在使用上,__attribute__((format(printf,m,n)))是常用的,而另一種卻很少見到。下面舉例說明,其中myprint為自己定義的一個帶有可變參數的函數,其功能類似于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是一個函數的成員函數,那么m和n的值可有點“懸乎”了,例如:
//m=3;n=4
extern void myprint(int l,const char *format,...) __attribute__((format(printf,3,4)));
其原因是,類成員函數的第一個參數實際上一個“隱身”的“this”指針。(有點C++基礎的都知道點this指針,不知道你在這里還知道嗎?)
__attribute__ noreturn
該屬性通知編譯器函數從不返回值,當遇到類似函數需要返回值而卻不可能運行到返回值處就已經退出來的情況,該屬性可以避免出現錯誤信息。C庫函數中的abort()和exit()的聲明格式就采用了這種格式,如下所示:
extern void exit(int) __attribute__((noreturn));
extern void abort(void) __attribute__((noreturn));
__attribute__ const
該屬性只能用于帶有數值類型參數的函數上。當重復調用帶有數值參數的函數時,由于返回值是相同的,所以此時編譯器可以進行優化處理,除第一次需要運算外,其它只需要返回第一次的結果就可以了,進而可以提高效率。該屬性主要適用于沒有靜態狀態(static state)和副作用的一些函數,并且返回值僅僅依賴輸入的參數。
為了說明問題,下面舉個非常“糟糕”的例子,該例子將重復調用一個帶有相同參數值的函數,具體如下:
extern int square(int n) __attribute__((const));
for (i = 0; i < 100; i++ )
{
total += square(5) + i;
}
通過添加__attribute__((const))聲明,編譯器只調用了函數一次,以后只是直接得到了相同的一個返回值。
同時使用多個屬性
可以在同一個函數聲明里使用多個__attribute__,并且實際應用中這種情況是十分常見的。使用方式上,你可以選擇兩個單獨的__attribute__,或者把它們寫在一起,可以參考下面的例子:
extern void die(const char *format, ...)
__attribute__((noreturn))
__attribute__((format(printf, 1, 2)));
section ("section-name")
屬性 section 用于函數和變量,通常編譯器將函數放在 .text 節,變量放在.data 或 .bss 節,使用 section 屬性,可以讓編譯器將函數或變量放在指定的節中。
例如:
++++ 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")))
連接器可以把相同節的代碼或數據安排在一起,Linux 內核很喜歡使用這種技術,
例如系統的初始化代碼被安排在單獨的一個節,在初始化結束后就可以釋放這部分內存。
* aligned (ALIGNMENT)
屬性 aligned 用于變量、結構或聯合類型,指定變量、結構域、結構或聯合的對齊量,以字節為單位,
例如:
++++ 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)));
表示該結構類型的變量以 16 字節對齊。通常編譯器會選擇合適的對齊量,顯示指
定對齊通常是由于體系限制、優化等原因。
* packed
屬性 packed 用于變量和類型,用于變量或結構域時表示使用最小可能的對齊,用
于枚舉、結構或聯合類型時表示該類型使用最小的內存。例如:
++++ include/asm-i386/desc.h
51: struct Xgt_desc_struct {
52: unsigned short size;
53: unsigned long address __attribute__((packed));
54: };
域 address 將緊接著 size 分配。屬性 packed 的用途大多是定義硬件相關的結
構,使元素之間沒有因對齊而造成的空洞。
當前函數名
==========
GNU CC 預定義了兩個標志符保存當前函數的名字,__FUNCTION__ 保存函數在源碼
中的名字,__PRETTY_FUNCTION__ 保存帶語言特色的名字。在 C 函數中,這兩個
名字是相同的,在 C++ 函數中,__PRETTY_FUNCTION__ 包括函數返回類型等額外
信息,Linux 內核只使用了 __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__ 看起來類似于標準 C 中的 __FILE__,但實際上 __FUNCTION__
是被編譯器替換的,不象 __FILE__ 被預處理器替換。
內建函數
GNU C 提供了大量的內建函數,其中很多是標準 C 庫函數的內建版本,例如
memcpy,它們與對應的 C 庫函數功能相同,本文不討論這類函數,其他內建函數
的名字通常以 __builtin 開始。
* __builtin_return_address (LEVEL)
內建函數 __builtin_return_address 返回當前函數或其調用者的返回地址,參數
LEVEL 指定在棧上搜索框架的個數,0 表示當前函數的返回地址,1 表示當前函數
的調用者的返回地址,依此類推。例如:
++++ 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)
內建函數 __builtin_constant_p 用于判斷一個值是否為編譯時常數,如果參數
EXP 的值是常數,函數返回 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)))
很多計算或操作在參數為常數時有更優化的實現,在 GNU C 中用上面的方法可以
根據參數是否為常數,只編譯常數版本或非常數版本,這樣既不失通用性,又能在
參數是常數時編譯出最優化的代碼。
* __builtin_expect(EXP, C)
內建函數 __builtin_expect 用于為編譯器提供分支預測信息,其返回值是整數表
達式 EXP 的值,C 的值必須是編譯時常數。例如:
++++ 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: }
這個內建函數的語義是 EXP 的預期值是 C,編譯器可以根據這個信息適當地重排
語句塊的順序,使程序在預期的情況下有更高的執行效率。上面的例子表示處于中
斷上下文是很少發生的,第 565-566 行的目標碼可能會放在較遠的位置,以保證
經常執行的目標碼更緊湊。
總結
以上是生活随笔為你收集整理的linux内核 extern,Linux内核之_attribute_的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 软件测试工作常用linux命令,软件测试
- 下一篇: gitlab linux版本下载,Lin