01、編譯選項(xiàng)和內(nèi)核編譯
Linux內(nèi)核(英語(yǔ):linux kernel),是一種計(jì)算機(jī)操作系統(tǒng)內(nèi)核,已C語(yǔ)言和匯編語(yǔ)言寫(xiě)成,匹配POSIX標(biāo)準(zhǔn),以GNU通用公共許可證發(fā)布。從技術(shù)上說(shuō)Linux只是一個(gè)內(nèi)核?!皟?nèi)核”指的是一個(gè)提供硬件抽象層、磁盤(pán)及文件控制、多任務(wù)等功能的系統(tǒng)軟件。
所以首先我們都知道,Linux內(nèi)核如果用O0編譯,是無(wú)法編譯過(guò)的,Linux的內(nèi)核編譯,要么是O2,要么是Os,這點(diǎn)從Linux的Makefile里面可以看出:
![](http://img.jbzj.com/file_images/article/201809/20189793946103.jpg?20188793955)
當(dāng)選擇了
CONFIG_CC_OPTIMIZE_FOR_SIZE
它會(huì)是Os,否則就是O2。
其實(shí)O2和Os,都是一些優(yōu)化選項(xiàng)的集合:
gcc -c -Q -O2 --help=optimizers > /tmp/O2-opts
gcc -c -Q -Os --help=optimizers > /tmp/Os-opts
前者傾向于基于速度的優(yōu)化,后者傾向于基于size更小的優(yōu)化。對(duì)比二者的開(kāi)關(guān)選項(xiàng):
meld /tmp/O2-opts /tmp/Os-opts
發(fā)現(xiàn)差異小的可憐:
![](http://img.jbzj.com/file_images/article/201809/20189794039168.jpg?20188794117)
![](http://img.jbzj.com/file_images/article/201809/20189794128937.jpg?20188794147)
O2和Os都使能了inline small函數(shù)和called once的函數(shù),但是O2里面-finline-functions是關(guān)閉的,而Os里面是開(kāi)的。O2里面optimize-strlen是開(kāi)的,Os里面這個(gè)選項(xiàng)是關(guān)閉的。相關(guān)選項(xiàng)的含義可以通過(guò)"man gcc"看出(有問(wèn)題,找男人),譬如man gcc后檢索inline-functions:
![](http://img.jbzj.com/file_images/article/201809/20189794206627.jpg?20188794226)
從O0到O1,O2,O3,是一個(gè)開(kāi)啟的優(yōu)化選項(xiàng)逐步加大的過(guò)程:
![](http://img.jbzj.com/file_images/article/201809/20189794245728.jpg?2018879435)
kernel用O0編譯不過(guò),是因?yàn)閗ernel本身也沒(méi)有想用O0能夠編譯過(guò),它的設(shè)計(jì)里面包含了編譯會(huì)優(yōu)化的假想。下面我們用一個(gè)簡(jiǎn)單的例子來(lái)說(shuō)明。
02、一個(gè)簡(jiǎn)單的例子
下面的代碼:
![](http://img.jbzj.com/file_images/article/201809/20189794632640.jpg?20188794640)
O0編譯會(huì)報(bào)如下錯(cuò),說(shuō)f()函數(shù)沒(méi)有定義:
$ gcc -O0 cc.c
cc.c:1:13: warning: ‘f' used but never defined [enabled by default]
void f(void);
^
/tmp/ccTwwtHG.o: In function `main':
cc.c:(.text+0x19): undefined reference to `f'
collect2: error: ld returned 1 exit status
但是用O2編譯,則沒(méi)有問(wèn)題:
原因在于,O2編譯,它意識(shí)到a==1,所以if(a>2),它不會(huì)成立,所以f()沒(méi)有定義也沒(méi)有關(guān)系。
把代碼稍微改一下后:
![](http://img.jbzj.com/file_images/article/201809/20189794718793.jpg?20188794724)
O2這個(gè)時(shí)候也不行了:
$ gcc -O2 cc.c
/tmp/ccXiyBHn.o: In function `main':
cc.c:(.text.startup+0x7): undefined reference to `f'
collect2: error: ld returned 1 exit status
所以,通過(guò)這個(gè)例子,大家可以看出來(lái)為什么同樣的代碼,用O2就可以過(guò),用O0就過(guò)不了。內(nèi)核里面有許多類(lèi)似設(shè)想編譯器會(huì)進(jìn)行優(yōu)化的代碼。
3.我們不想inline了
由于編譯的優(yōu)化,有些函數(shù)(比如小函數(shù)和全工程里面只被一個(gè)人調(diào)用的函數(shù))雖然沒(méi)有顯示地寫(xiě)成inline,但是編譯器優(yōu)化為inline了,這給調(diào)試造成了一些麻煩,因?yàn)檎也坏竭@個(gè)函數(shù)對(duì)應(yīng)的symbol了。
這個(gè)時(shí)候,我們可以顯示地寫(xiě)明某些函數(shù)我們不想inline:
![](http://img.jbzj.com/file_images/article/201809/20189794754409.jpg?2018879481)
否則,上面2個(gè)函數(shù),即便你代碼里面沒(méi)有寫(xiě)inline,由于O2和Os使能了相關(guān)的inline選項(xiàng),也可能被編譯器自動(dòng)inline掉,如果我們想拒絕inline,可以通過(guò)noline來(lái)標(biāo)識(shí)。
4.我不想被優(yōu)化
在全局已經(jīng)使能O1, O2, O3, Os的情況下,某個(gè)單獨(dú)的函數(shù)我們不想做任何的優(yōu)化,可以用__attribute__((optimize("O0")))
來(lái)修飾這個(gè)函數(shù),比如我們把上述用O2可以編譯過(guò)的代碼進(jìn)行如下修改:
![](http://img.jbzj.com/file_images/article/201809/20189794828824.jpg?20188794839)
重新用O2編譯:
$ gcc -O2 cc.c
/tmp/cc8M338p.o: In function `main':
cc.c:(.text+0x19): undefined reference to `f'
collect2: error: ld returned 1 exit status
5. 總結(jié)的話
下面給幾條實(shí)踐指南:
- 盡量不要嘗試用O0去編譯內(nèi)核,這不符合真實(shí)的工程實(shí)踐,也不太被主流Linux社區(qū)所支持;內(nèi)核依賴O2/Os去做較多的優(yōu)化;
- 追求你的代碼在O2的情況下,仍然是正確的,代碼要經(jīng)得起編譯優(yōu)化;比如O0工作正常,而O2不正常,應(yīng)該盡可能從自身找原因,分析匯編;
- 如果在全局優(yōu)化的情況下,想針對(duì)某個(gè)局部避免優(yōu)化,可以嘗試用noinline,__attribute__((optimize("O0")))等進(jìn)行外科手術(shù)式地調(diào)整。
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)腳本之家的支持。