?? 移植(porting)與編譯(compiling)程式.htm
字號:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<!-- saved from url=(0063)http://member.netease.com/~jnkey/linuxflr/howto/GCC-HOWTO4.html -->
<HTML><HEAD><TITLE>移植(Porting)與編譯(Compiling)程式</TITLE>
<META http-equiv=Content-Type content="text/html; charset=gb2312">
<META content="MSHTML 6.00.2800.1106" name=GENERATOR></HEAD>
<BODY text=#000000 bgColor=#ffeedd><A
href="http://member.netease.com/~jnkey/linuxflr/howto/GCC-HOWTO.html"><EM>The
Linux GCC HOWTO中譯版V0.1</EM></A> <B>:</B>
<EM>移植(Porting)與編譯(Compiling)程式</EM><BR><B>Previous:</B> <A
href="http://member.netease.com/~jnkey/linuxflr/howto/GCC-HOWTO3.html"><EM>GCC的安裝(installation)與啟用(setup)</EM></A><BR><B>Next:</B>
<A
href="http://member.netease.com/~jnkey/linuxflr/howto/GCC-HOWTO5.html"><EM>Debugging
and Profiling</EM></A>
<HR noShade>
<H2><A name=18></A>4. 移植(Porting)與編譯(Compiling)程式</H2>
<H3><A name=19></A>4.1. gcc自行定義的符號<A name=index.25></A> </H3>
<P>只要執行gcc時,附加<CODE>
-v</CODE>這個參數(switch),就能找出你所用的這版gcc,自動幫你定義了什麼符號(symbols).例如,我的機器看起來會像這樣:</P>
<P>
<BLOCKQUOTE><CODE><PRE>$ echo 'main(){printf("hello world\n");}' | gcc -E -v -
Reading specs from /usr/lib/gcc-lib/i486-box-linux/2.7.2/specs
gcc version 2.7.2
/usr/lib/gcc-lib/i486-box-linux/2.7.2/cpp -lang-c -v -undef
-D__GNUC__=2 -D__GNUC_MINOR__=7 -D__ELF__ -Dunix -Di386 -Dlinux
-D__ELF__ -D__unix__ -D__i386__ -D__linux__ -D__unix -D__i386
-D__linux -Asystem(unix) -Asystem(posix) -Acpu(i386)
-Amachine(i386) -D__i486__ -
</PRE></CODE></BLOCKQUOTE>
<P></P>
<P>假若目前你正在寫的程式碼,會用到一些Linux獨有的特性(Linux-specific
features),那麼把那些無法移植的程式碼(non-portable bits),以條件式編譯(conditional
compilation)的前置命令封括(enclose in)起來,可是個不錯的主意呢!</P>
<P>
<BLOCKQUOTE><CODE><PRE>#ifdef __linux__
/* ... funky stuff ... */
#endif /* linux */
</PRE></CODE></BLOCKQUOTE>
<P></P>
<P>用<CODE>__linux__</CODE>即可達成目的;看仔細一點,<EM>不是</EM><CODE>linux</CODE>啊.僅管後者也有定義,畢竟,仍然不是POSIX的標準(not
POSIX compliant).</P>
<H3><A name=20></A>4.2. 線上求助說明(invocation)</H3>
<P>gcc編譯器參數(switches)的說明文件是gcc info page(在Emacs內,按下<CODE>C-h
i</CODE>,然後選'gcc'的選項).要是弄不出來,不是賣你CD-ROM的人,沒把這個東東壓給你,不然就是你現在用的是舊版的.這種情況下,最好的方法是移動尊臀到archive<A
href="ftp://prep.ai.mit.edu/pub/gnu">ftp://prep.ai.mit.edu/pub/gnu</A>或是它的mirrors站臺上,把gcc的原始檔案抓回家,重新烹飪一番.</P>
<P>gcc manual page (<CODE>gcc.1</CODE>)
可以說是已經過時了.一旦你吃飽撐著沒事干要去看看它的話,它就會告訴你這件事,叫你別無聊了.</P>
<H4><A name=21></A>4.2.1. 旗正飄飄~(flags)<A name=index.26></A> <A
name=index.27></A></H4>
<P>在命令列(command
line)上執行gcc時,只要在它的屁股後面加上<CODE>-O</CODE><EM>n</EM>的選項,就能讓gcc乖乖的替你生出最佳化後的機器碼(output
code).這里的<EM>n</EM>是一個可有可無的小整數.不同的gcc版本,<EM>n</EM>的意義與其正確的(exact)功效都不一樣;不過,典型的□圍是從0(不要雞婆,我不要最佳化)變化到2(最佳化要多一點),再到3(最佳化要再多一點,多一點).</P>
<P>gcc在其內部會將這些轉譯成一系列的<CODE>-f</CODE>
與<CODE>-m</CODE>選項(options).執行gcc時帶上旗號(flags)<CODE>-v</CODE>與<CODE>-Q</CODE>,你就能很清楚的看出每一種等級的<CODE>-O</CODE>是對應(maps)到那些選項(options).例如,就<CODE>-O2</CODE>來講,我的gcc告訴我說:</P>
<P>
<BLOCKQUOTE><CODE><PRE>enabled: -fdefer-pop -fcse-follow-jumps -fcse-skip-blocks
-fexpensive-optimizations
-fthread-jumps -fpeephole -fforce-mem -ffunction-cse -finline
-fcaller-saves -fpcc-struct-return -frerun-cse-after-loop
-fcommon -fgnu-linker -m80387 -mhard-float -mno-soft-float
-mno-386 -m486 -mieee-fp -mfp-ret-in-387
</PRE></CODE></BLOCKQUOTE>
<P></P>
<P>要是你用的最佳化等級(optimization level)高於你的編譯器所能支援的(e.g.
<CODE>-O6</CODE>),那麼它的效果,就跟你用你的編譯器<EM>所能</EM>提供的最高等級的,是一樣的結果.說實在的,發行出去的gcc程式碼,用在編譯時竟是如此處理這等問題,實非什麼好的構想.日後若是有更進步的最佳化方法具體整合到新的版本里,而你(或你的users)還是試著這樣做的話,可能就會發現,gcc會中斷你的程式(break
your code)了.</P>
<P><A name=index.28></A>從gcc
2.7.0到2.7.2的users應該注意到,使用時<CODE>-O2</CODE>會有一個bug存在.更糟糕的是,強度折減(strength
reduction)居然沒有用(doesn't
work)!要是你喜歡重新編譯gcc的話,是有那麼一個修正的版本(patch)可以更正這項錯誤;不然的話,一定要確定每次編譯時都會加上<CODE>-fno-strength-reduce</CODE>喔!</P>
<P>11/12/97譯</P>
<H5><A name=22></A>4.2.1.1. 有個性的微處理器(Processor-specific)</H5>
<P>有一些<CODE>-m</CODE>的旗號無法藉由各種等級的<CODE>-O</CODE>來打開,然而卻是十分有用的.這之中最主要的是<CODE>-m386</CODE>與<CODE>-m486</CODE>兩種,用來告訴gcc該把正在編譯的程式碼視作專為386或是486機器所寫的.不論是用哪一種來編譯程式碼,都可以在彼此的機器上執行,-m486編譯出來的碼會比較大,可是拿來在386的機器上跑也不會比較慢就是了.</P>
<P>目前尚無<CODE>-mpentium</CODE>或是<CODE>-m586</CODE>的旗號.Linus建議我們,可以用<CODE>-m486
-malign-loops=2 -malign-jumps=2 -malign-functions=2</CODE>,來得到最佳化的486程式碼(486
code optimizations),而這樣做正好就可以避免alignment(Pentium并不需要)有過大的gaps發生. Michael
Meissner說:</P>
<P>
<BLOCKQUOTE>我的第六感(hunch)告訴我,
<CODE>-mno-strength-reduce</CODE>(嘿!我可不是在談強度折減的bug啊,那已經是另外一個爭論的戰場了.)一樣也可以在x86的機器上,產生較快的程式碼,這是因為x86的機器對暫存器(register)有著不可磨滅的□渴在(and
GCC's method of grouping registers into spill registers vs. other registers
doesn't help either).傳統上,強度折減的結果會使得編譯器利用加法暫存器(additional
registers)以加法運算(addition)來取代乘法運算(multiplication).而且,我也在懷疑(suspect)<CODE>-fcaller-saves</CODE>,可能也只是個漏洞(loss)也說不定.
</BLOCKQUOTE>
<BLOCKQUOTE>而我的第七感則再度的告訴我,
<CODE>-fomit-frame-pointer</CODE>可能會,也可能不會有任何的賺頭.從這點來看,即意謂著有另一個暫存器可用來處理記憶體分配(allocation)的問題.另方面,若純粹從x86的機器在轉換(encodes)它的指令集(instruction
set)成為機器碼的方法上來看,便意謂著堆疊(stack)所用到的記憶體空間要比frame所用到的還要來的多;換句話說,Icache對程式碼而言并沒有實質上的益處.若是閣下用了<CODE>-fomit-frame-pointer</CODE>的話,同時,也就是告訴編譯器在每次呼叫函數(calls)之後,就必須修正堆疊的指標(stack
pointer);然而,就frame來講,若呼叫的次數不多的話,則允許堆疊暫時堆積(accumulate)起來. </BLOCKQUOTE>
<P></P>
<P>有關這方面主題的最後一段話仍是來自於Linus:</P>
<P>
<BLOCKQUOTE>要注意的是,如果你想要得到最佳狀況的執行成果(optimal
performance),可千萬別相信我的話.無論如何,一定要進行測試.gcc編譯器還有許多的參數(switches)可用,其中可能就有一種最特別的組合(set),可以給你最佳化的結果喔.
</BLOCKQUOTE>
<P></P>
<P>11/14/97譯</P>
<H4><A name=23></A>4.2.2. <CODE>Internal compiler error: cc1 got fatal signal
11</CODE><A name=index.29></A> <A name=index.30></A><A name=index.31></A><A
name=index.32></A><A name=index.33></A></H4>
<P>Signal 11是指 SIGSEGV, 或者 `segmentation
violation'.通常這是指說gcc對自己所用的指標感到困惑起來,而且還嘗試著寫入不屬於它的記憶體.所以,這可能是一個gcc的bug.</P>
<P>然而,大部份而言,gcc是一件經過嚴密測試且可靠度佳的軟體佳作.它也用了大量復雜的資料結構與驚人的指標數量.簡言之,若是要評選本世紀最挑惕與最一絲不□的RAM測試程式(RAM
tester)的話,gcc絕對可以一摘后冠的.假如你<EM>無法重新復制這只bug</EM>---當你重新開始編譯時,錯誤的訊息并沒有一直出現在同一個地方---那幾乎可以確定,是你的硬體本身有問題(CPU,記憶體,主機板或是快取記憶體).<B>千萬不要</B>因為你的電腦可以通過開機程序的測試(power-on
checks),或者Windows可以跑得很順,或者其它什麼的,就回過頭來大肆宣傳說這是gcc的一個bug;你所做的這些測試動作,通常沒有什麼實際上的價值,而且沒有價值也是很合理的結論.另外,也不要因為編譯核心時,總是停留在`<CODE>make
zImage</CODE>'的階段,就要大罵這是gcc的bug---當然它會停在那兒啊!做`<CODE>make
zImage</CODE>'時,需要編譯的檔案可能超過200檔案;我們正在研擬一個<EM>比較小的</EM>地方來取代.</P>
<P>如果你可以重覆產生這個bug,而且(最好是這樣啦)可以寫一個短小的程式來展示這只bug的話,你就可以把它做成bug報表(bug
report),然後email給FSF,或者是linux-gcc郵件表列(linux-gcc mailing
list).你可以去參考gcc的說明文件,看看有什麼詳細的資訊,是他們所需要的.</P>
<H3><A name=24></A>4.3. 移植能力(Portability)</H3>
<P>據報,近日來許多正面消息指出,若有某件東東到現在都還沒移植到Linux上去,那麼可以肯定的是,它一定一點價值也沒有.:-)</P>
<P>嗯!正經一點.一般而言,原始碼只需要做一些局部的修改(minor changes),就可以克服(get over)Linux
100%與POSIX相容的特質(compliance).如果你做了任何的修改,而將此部份傳回(passing
back)給原作者,會是很有建設性的舉動(worthwhile).這樣日後就只需要用到'make',就能得到一個可執行的檔案了.</P>
<H4><A name=25></A>4.3.1. BSD教派(BSDisms) (有 <CODE>bsd_ioctl</CODE>,
<CODE>daemon</CODE> 與 <CODE><sgtty.h></CODE>)</H4>
<P>編譯程式時,可以配合<CODE>-I/usr/include/bsd</CODE>與連結<CODE>-lbsd</CODE>的程式庫.(例如:在你的Makefile檔內,把<CODE>-I/usr/include/bsd</CODE>加到<CODE>CFLAGS</CODE>那一行;把<CODE>-lbsd</CODE>加到<CODE>LDFLAGS</CODE>那一行).如果你真的那麼想要BSD型態的信號行為(BSD
type signal
behavior),也<EM>不</EM>再需要加上<CODE>-D__USE_BSD_SIGNAL</CODE>了.那是因為當你用了<CODE>-I/usr/include/bsd</CODE>與含括了標頭檔<CODE><signal.h></CODE>之後,make就自動會把它加入了.</P>
<H4><A name=26></A>4.3.2. 失落的封印(`Missing' signals)(<CODE>SIGBUS</CODE>,
<CODE>SIGEMT</CODE>, <CODE>SIGIOT</CODE>, <CODE>SIGTRAP</CODE>,
<CODE>SIGSYS</CODE> etc) <A name=index.34></A><A name=index.35></A><A
name=index.36></A><A name=index.37></A><A name=index.38></A></H4>
<P>Linux與POSIX是完全相容的.不過,有些信號并不是POSIX定義的---ISO/IEC 9945-1:1990 (IEEE Std
1003.1-1990), paragraph B.3.3.1.1 sez:</P>
<P>
<BLOCKQUOTE>"在POSIX.1中省略了SIGBUS, SIGEMT, SIGIOT, SIGTRAP,
與SIGSYS信號,那是因為它們的行為(behavior)與實作方式是息息相關的(implementations
dependent),而且也無法進行適當的分門別類(adequately categorized).確認實作方式後(conforming
implementations),便可以生產出(deliver)這些信號,可以必須以文件說明(document)它們是在什麼樣的環境(circumstances)下生產出來的,以及指出與它們的發展相關的任何限制(any
restrictions concerning their delivery)". </BLOCKQUOTE>
<P></P>
<P>如欲修正此點,最簡單,也是最笨的(cheesy)方法就是以<CODE>SIGUNUSED</CODE>重新定義這些信號.而<EM>正確的</EM>方法應是以條件式的編譯<CODE>#ifdef</CODE>來處理這些問題才對:</P>
<P>
<BLOCKQUOTE><CODE><PRE>#ifdef SIGSYS
/* ... non-posix SIGSYS code here .... */
#endif
</PRE></CODE></BLOCKQUOTE>
<P></P>
<P>11/15/97譯</P>
<H4><A name=27></A>4.3.3. K & R <A name=index.39></A></H4>
<P>gcc是個與ANSI相容的編譯器;奇怪的是,目前大多數的程式碼都不符合ANSI所定的標準.如果你熱愛ANSI,喜歡用ANSI提供的標準來撰寫C程式,似乎除了在編譯器的旗號上加上<CODE>-traditional</CODE>之外,就沒有什麼其它的可以多談的了.There
is a certain amount of finer-grained control over which varieties of brain
damage to emulate;請自行查閱gcc info page.</P>
<P>要注意的是,盡管你用了<CODE>-traditional</CODE>來改變語言,它的效果也僅局限在gcc所能夠接受的□圍.例如,
<CODE>-traditional</CODE>會打開(turn
on)<CODE>-fwritable-strings</CODE>,使得字串常數(string constants)移至資料記憶體空間(data
space)內(從程式碼記憶體空間(text space),這地方是不能任意寫入的).這樣做會讓程式碼的記憶體空間無形中增加的.</P>
<H4><A name=28></A>4.3.4. 前置處理器(Preprocessor)的符號卯上函數原型宣告(prototypes)<A
name=index.40></A> <A name=index.41></A></H4>
<P>最常見的問題是,如眾所皆知,Linux中有許多常用的函數都定義成巨集(macros)存放在標頭檔(header
files)內,此時若有相似的函數原型宣告出現在程式碼內,前置處理器會拒絕進行語法分析(parse)的前置作業.常見的有<CODE>atoi()</CODE>與<CODE>atol()</CODE>.</P>
<H4><A name=29></A>4.3.5. <CODE>sprintf()</CODE><A name=index.42></A> </H4>
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -