The Linux GCC HOWTO中译版

 
 
 
 
 
 
The <wbr>Linux <wbr>GCC <wbr>HOWTO中译版

[保留] The Linux GCC HOWTO中译版


 
 

作者: Daniel Barlow 
译者: 陈建勋(Frank J.S. Chen) 

v1.17, 28 February 1996 
_________________________________________________________________ 

本文阐述安装GNU C编译器和程式馆的方法,同时概观地说明程式的编译、连结、 
执行、除错的过程以及可能面临的诸多问题。写作的参考资料泰半来自於Mitch 
DSouza先生所收集的GCC-FAQ;而另一个来源是ELF-HOWTO。此份HOWTO可以说已 
代替了GCC-FAQ,而且即将要永久替代ELF-HOWTO了。此乃GCC-HOWTO第一份公开发 
行的版本(不须理会版本序号;那是RCS的杰作),有任何指正与建议的,本人都 
很欢迎。 
_________________________________________________________________ 

1. 行远必自迩! 

1.1 译者的话 
1.2 动与静 
1.3 作者的私语 
1.4 印刷与排版 

2. 东东在哪儿? 

2.1 GCC-HOWTO在哪儿? 
2.2 GCC相关的资料又在哪儿? 
2.3 GCC 
2.4 C程式馆与标头档 
2.5 有关联的工具 (as, ld, ar, strings etc) 

3. GCC的安装与GCC的设定 

3.1 GCC的版本 
3.2 东东装好後都到哪儿去了? 
3.3 标头档ㄋㄟ?标头档ㄋㄟ? 
3.4 建立交叉编译器(Building cross compilers) 

4. 移植程式与编译程式 

4.1 gcc自行定义的符号 
4.2 线上求助说明 
4.3 移植能力 

5. 除错与监管 

5.1 预防重於治疗(lint) 
5.2 除错 
5.3 监管 

6. 连结 

6.1 共享程式库 vs静态程式库 
6.2 终极审判(‘sin() 在哪个程式库里?’) 
6.3 X档案? 
6.4 建立你自己的程式库 

7. 动态载入 

7.1 基本概念 
7.2 错误讯息 
7.3 控制动态载入器的运作 
7.4 以动态载入撰写程式 

8. 与发展人士联络 

8.1 Bug报表 
8.2 协助发展 

9. 结语 

9.1 名人榜 
9.2 翻译 
9.3 欢迎任何的回馈 
9.4 合法的行迳规定 

10. 索引 
_________________________________________________________________ 

1. 行远必自迩! 

1.1 译者的话 

这份译文为Linux document projects(LDP)中文翻译计画系列之一。目前之 
网址为 [1]http://www.linux.org.tw/CLDP/,欢迎各位网友踊跃投入此一计 
画。 
我并没有完全按照原文逐字翻译。为了力求译文通畅可读,我会稍稍的重组 
一部份的文字,加油添醋,或是精简原文;这样做的话,可以弥补中英文间 
语法结构的差异性,且语气可以贯通无碍。 
一些关键字与专业词汇等,会附加上原文单字。 
遇有转译困难,唯原文常见的字汇如bugs、shadow password、padding 
、image之类的,则保留原文不变。若阁下对这些字汇有适当译辞的,请不吝 
指教。 
内文中若遇有"[译者注:**]"之标记,则为本人额外之注解。 
对这篇译文有任何建议与疑问的,请email至frank63@ms5.hinet.net。 
WWW Home Page: [2]http://linux.ntcic.edu.tw/~jsfrank/。 
此中译文件之翻译权已取得英籍之原作者Daniel Barlow 先生之同意;另, 
陈建勋先生保有此份中译版文件所有的权利,你可以任意的拷贝,以各种媒 
体散布这份中译文件,唯此节补充说明需原封不动附上,且不可任意更动译 
文。 
v0.1版的译文相当粗糙,连文句的语法结构都嫌太过於松散,v0.2版针对中 
文的用字习惯来修正,并将上一版译的不妥当的地方修正过来,例如either 
这个字,英国人常把either当名词用,指两者中任意一个;这份HOWTO就充份 
反应出这个用字习性,跟美语有基本上的差异。 
文中有几句话没有译出来,一方面是看不懂,另方面是直译也译不出来,所 
以只好保留原文了,要是阁下有新的领悟的,无论如何请告诉我。 
v0.1版翻译起始日期为:11/7/97;截止日期为:11/19/97。 
v0.2版修正起始日期:5/13/98;截止日期为:6/3/98。 

1.2 动与静 

目前Linux的发展正波涛汹涌的进行著。简单一点讲,Linux有两种执行档的格式 
可用,取决於你的系统是怎麽整合起来的;你的Linux应该是其中一种吧!阅读这 
份文件,可以帮助你□清执行档的类别。 

要如何区别呢?执行公用程式‘file’(例如,file /bin/bash)就对了。 
就ELF的程式而言,萤幕上显示出来的讯息会含有ELF的字眼;如果说是a.out的, 
讯息内会箝有 Linux/i386的字样。 

ELF与a.out格式的差异之处会在後续的章节中讨论(很广泛喔!)。ELF是比较新 
的格式,一般而言,接受的程度较佳。 

1.3 作者的私语 

版权说明与合法的行迳规定,就摆在这份文件的尾端。除此之外,我还有一些不 
得不提醒你的话要讲。就算你□著没事干,也不要在Usenet上丢一些呆瓜问的问 
题;还有,不要老以为自己C语言的功力深厚,专门发表一些不是bugs的bugs出来 
丢人现眼,这不就等於告诉别人你不学无术,在关老爷面前耍大刀了吗?所以说 
自以为是的英雄主义是得不偿失的。 

1.4 印刷与排版 

如果你现在读的是Postscript、dvi或是html格式,那麽你所看到的字型变化就会 
比只读纯文字格式的人多一些。特别是档案名称、命令、命令的输出与摘录出来 
的原始码等,统统都是打字机的字型。这样做的话,对於某些需要强调的‘变数 
’还有那些没有固定结果的□例而言,就可以达到强调的效果了。 

读这份文件的同时,你也会得到一套蛮有用的索引。假若是dvi、 postscript之 
类的版本,索引的数字就是章节(section)的编号;如果是HTML的话,这些数字会 
按顺序排列,你可以用滑鼠左键去连结相对的索引内容;如果你看的是纯文字版 
本的话, 数字就只是数字,没别的含意;建议你赶快升级为妙哩! 

我用的shell是Bourne shell(不是C shell),举的例子自然是Bourne shell的 
语法。如果你用的是C shell的话,设定环境变数的语法会像下面这样: 

setenv FOO bar 

要是用Bourne shell的话,我会这样子写: 

FOO=bar; export FOO 

如果提示符号显示的是井字符号#而不是钱字符号 $的话,很有可能这个命令是只 
适用於root而已。当然啦,要是你试了这些□例,结果弄得你的系统发生灾变, 
我可是一点责任也不会负的喔!祝好运!:-) 

11/8/97译. 5/13/98修订 

2. 东东在哪儿? 

2.1 GCC-HOWTO在哪儿? 

这份文件是Linux HOWTO系列之一,换句话说,你可以在所有存放Linux HOWTO文 
件的网站上面找到她的芳踪,例如 
[3]http://sunsite.unc.edu/pub/linux/docs/HOWTO/。HTML的版本(可能会是较 
新的版本)可以从 
[4]http://ftp.linux.org.uk/~barlow/howto/gcc-howto.html上面抓下来。 

2.2 GCC相关的资料又在哪儿? 

标准的gcc说明文件是随附在发行的原始码(source distribution)内(往下看就 
有了!),里头有textinfo与.info两种档案。要是你的网路连接速率够快,或是 
有一片cdrom,不然,有高度的耐心也成,你可以自己把它untar,然後再把相对 
应的位元一一拷贝到/usr/info的目录底下。假如你的条件与上述的不符,不妨到 
[5]tsx-11站上去参观参观。不过,我想,没有必要老是惦记著最新的版本吧! 

libc的文件说明有两种来源。一种是GNU libc,以.info的格式储存,除了stdio 
之外,其馀Linux libc的说明都相当的详尽精确。另一种可以在Linux的archive 
[6]manpages上找到系统呼叫(system call)(第2节)与libc函数(function) 
(第3节)的文件说明。 

2.3 GCC 

解答有二: 

(a)你可以在 [7]ftp://tsx-11.mit.edu:/pub/linux/packages/GCC/的网站上找 
到 正式的Linux GCC发行系统(distribution),而且是已经编译好的可执行档。 
当我在写这份文件时,2.7.2(gcc-2.7.2.bin.tar.gz)是最新的版本。 

(b)自由软体基金会(Free Software Foundation)所发布的GCC最新原始码可以 
从网站 [8]GNU archives上取得。没有必要非得与上述的版本一致才行,不过这 
个版本的确是目前最新的。Linux GCC的维护网友(maintainers)让你可以很轻松 
的自行编译这个最新的版本。configure命令稿(script)会帮你自动设定好所有该 
做的事情。建议你有空不妨到 [9]tsx-11看看,说不定会有修正的版本是你会想 
要用的。 

如果想要编写出一些有用的软体(不是我罗唆,还是有不少没啥用途的软体在网 
路上四处流窜。),下面这一小节所谈的也是你需要的: 

2.4 C程式馆与标头档 

该选哪一套程式馆是取决於(i)你的系统是ELF的或是a.out的;(ii)你希望你的系 
统变成哪一种?如果你是从libc 4升级到libc 5,那麽给你一个良心的建议,先 
去看看ELF-HOWTO再说。你一定会问,在ELF文件的哪儿呢?嘿!嘿!不偏不倚, 
就差不多跟这份文件相同的位置。网站 [10]tsx-11上面可以找到你想要的。 

libc-5.2.18.bin.tar.gz 
--- ELF共享程式馆(ELF shared library images),静态程式馆 
(static libraries)与标头档(针对C语言与数学程式馆)。 

libc-5.2.18.tar.gz 
---libc-5.2.18.bin.tar.gz的原始码。这个档案你也需要,因为.bin.套 
件(package)含有必需的标头档。如果此时你正犹豫不决,不晓得是老身 
亲自下海,动手编译C程式库比较好;还是直接用人家编译好的二进位 
档(binaries)就可以了。有这种困扰的人,来,看我的嘴形:用人家编译 
好的二进位档不就解决了嘛!只有在你想要NYS或是shadow password的情 
况下,才需要自己的手来推动摇篮。 

libc-4.7.5.bin.tar.gz 
--- 这个档案是a.out的共享程式库(shared library images)与静态程式 
库,用途是为了与前述的libc 5套件共存共荣而设计的,不过除非你想要 
继续使用a.out的程式或是继续发展a.out的程式,不然的话,是不需要它 
的。 

2.5 有关联的工具 (as, ld, ar, strings etc) 

到目前为止,与之前所谈的都一样,从网站 [11]tsx-11上,就可以找到这些工具 
程式。目前的版本是binutils-2.6.0.2.bin.tar.gz。 

需要注意的是binutils只适用於ELF,而且目前libc的版本也都是属於ELF的;当 
然啦,习惯a.out的人如果有个ELF的libc与a.out的libc联合起来一起使用,这对 
他们来讲是再好不过的美事了。不可否认的,C程式馆的发展正以坚决的脚步迈 
向ELF的格式,除非你真的有很好的理由,需要a.out的东东,不然啊,大家都会 
鼓励你勇於突破,趁早加入锐不可挡的大潮流。 

11/9/97译 

3. GCC的安装与GCC的设定 

3.1 GCC的版本 

在shell的提示符号下键入gcc -v,萤幕上就会显示出你目前正在使用的GCC的版 
本。同时这也是一个相当可靠的方法,可以确定你现在所用的是ELF或是a.out。 
在我的系统上,执行gcc -v的结果是: 

gcc -v 
Reading specs from /usr/lib/gcc-lib/i486-box-linux/2.7.2/specs 
gcc version 2.7.2 

上面的讯息指出了几件重要的事情: 
i486 这是指明你现在正在用的gcc是为了486的微处理器而写的---你的电脑 
可能是386或是586。这3种微处理器的晶片所编译而成的程式码,彼此间是可 
以相容使用的。差别之处是486的程式码在某些地方有加上padding的功能, 
所以可以在486上面跑得比较快。这对386的机器而言,执行程式的效能并不 
会有什麽不良的影响,只不过真的会让程式码变得稍稍的大了一些。 
box 这可以说一点也不重要;不过也可能另有所指(像是slackware或者 
是debian),或者根本什麽也不是(所以罗!完整的目录名称是i486-linux 
)。假如你是实务派的佼佼者,亲自动手建立属於自己的gcc,那麽你可以在 
建立的过程中设定这一项,以装点门面。就像我做的一样:-)。 
linux 其实这是指linuxelf或是linuxaout。这一项会令人引起不必要的困惑 
,究竟是指哪一种会根据你所用的版本而异。 
linux 意指ELF若版本序号是2.7.0.(或是更新的版本);否则的话, 
就是a.out的了。 
linuxaout 意指a.out的格式。当linux的定义从a.out更换到ELF时 
,linuxaout就会顺水推舟,摇身一变,成了一个目标物。因此,你不 
会看到任何版本新於2.7.0.的gcc有linuxaout的。 
linuxelf 已经过时了。通常这是指2.6.3版的gcc,而且这个版本也可 
以用来产生ELF的可执行档。要注意的是,gcc 2.6.3版在产生ELF程式 
码时会有bugs,所以如果你目前用的恰好是这个版本,建议你赶快升级 
。 
2.7.2 版本的序号。 

所以,总结起来,我有2.7.2版的gcc,可以产生ELF格式的程式码。就这麽简单, 
惊讶吧!eh? 

3.2 东东装好後都到哪儿去了? 

如果安装gcc时没有仔细的看著萤幕,或者你是从一个完整的发行系统里把gcc单 
独抓出来安装的话,那麽也许你会想知道到底这些东东装好後是住在整个档案系 
统的那些地方。几个重点如下: 

/usr/lib/gcc-lib/target/version/ (与子目录)大部份的编译器就是住在 
这个地方的。在这儿有可执行的程式,实际在做编译的工作;另外,还有一 
些特定版本的程式库与标头档等也会储存在此。 
/usr/bin/gcc 指的是编译器的驱动程式---也就是你实际在命令列(command 
line)上执行的程式。这个目录可供各种版本的gcc使用,只要你用不同的编 
译器目录(如上所述)来安装就可以了。要知道内定的版本是那一个, 
在shell提示符号下打gcc -v。要是想强迫执行某个版本,就换打gcc -V 
version。例如: 

gcc -v 
Reading specs from /usr/lib/gcc-lib/i486-box-linux/2.7.2/specs 
gcc version 2.7.2 
gcc -V 2.6.3 -v 
Reading specs from /usr/lib/gcc-lib/i486-box-linux/2.6.3/specs 
gcc driver version 2.7.2 executing gcc version 2.6.3 

/usr/target/(bin|lib|include)/ 如果你装了数种的目标物件,例如a.out 
与elf,或是某一种的交叉编译器(cross-compiler)等等,那些属於非主流目 
标物件(non-native target(s))的程式库,binutils(as、ld等等)工具 
与标头档等都可以在这儿找到。即使你只安装了一种gcc,还是可以在这儿找 
到这些原本就是替它们准备的东东。如果不是在这儿,那麽就应该是 
在/usr/(bin|lib|include)了。 
/lib/,/usr/lib 与其它的目录等都是主流系统(native-system)的程式馆 
目录。许多的应用程式都会用到/lib/cpp,因此你也需要它---作法上,不是 
从/usr/lib/gcc-lib/target/version/ 目录里拷贝,就是弄个符号连结 
(symlink)指向那儿。 [译者注:所谓的native,是指目前你的系统是 
以a.out或elf的格式为主,或者内定的gcc是哪一种版本等等。native的意思 
是‘本土的’、‘本国的’与‘天生的’……等等;当你拿到一片CD-ROM重 
头至尾将Linux安装完成,让Linux出生,成为你个人特色浓厚的作业平台後 
,如果再加装一些不一样的目标物件,自然就有‘本土’与‘外省’( 无关 
政治),‘本国’与‘外国’、‘天生’与‘人为’等等的区别,同时也含 
有内定(default)的意思在。假若再附加上你个人的价值观判断和喜好,我 
想用主流(native)与非主流(non-native)来翻译应该还算恰当。] 

3.3 标头档ㄋㄟ?标头档ㄋㄟ? 

假如把你自行安装在/usr/local/include目录底下的标头档排除在外的话 
,Linux还有另外3种主要的标头档: 

/usr/include/与其子目录底下的标头档,大部份都是由H.J.Lu发展的libc套 
件(libc binary package)所提供的。我会只说‘大部份’的原因,是因为你 
可能有其它来源的标头档(像是curses与dbm程式库等等)摆在这儿;尤其是 
,如果你现在用的是最新的libc发行系统的话(新版本不像旧版那样,已经 
不再支援curses或dbm了。),那东东之多是人人为之咋舌的! [译者 
注:libc binary package意指以二进位形式(machine code)储存之套件,并 
非原始码(text),若要以中文全称译出,则成‘libc二进位档套件’,似 
有聱牙之嫌,故略去binary,以libc套件通称。] 
在核心原始码的发行系统内(kernel source distribution) 
,/usr/include/linux 与 /usr/include/asm (里头有这些档案 
: 与 )应该有符号连结(symbolic links)可以连 
结至目录linux/include/linux 与 linux/include/asm。如果你有鸿鹄之志 
的话,安装这些东东後,就不应该只是拿来编译核心(kernel)而已。 把原 
始码解压缩(unpacking)後,可能你也会发现,需要在核心的目录 
(kernel directory)底下做make config的动作。很多的档案都会依 
赖的帮忙,可是这个档案却有可能因版本不同而不存在 
。若干核心版本里,asm就只是它自己的一个符号连结,仅仅是在make 
config时才建立出来而已。 [译者注:原文提及autoconf.h时是‘Many 
files depend on ,which otherwise may not exist,* 
’。此处之otherwise之词性应为形容词(adj),指‘另一情况’、‘另一种 
’、‘不同的’之意,将原文形容词子句拆开来应为:(i) Many files 
depend on (ii) of other 
condition may not exist. 与下一句互相比对,此处应同指在不同版本之情 
况下。] 所以,当你在目录/usr/src/linux底下,解开核心的程式码时,就 
照著下面指示的做吧! 

cd /usr/src/linux 
su 
make config 
(回答接下来的问题。通常回答得正不正确并不重要,除非你打算继续□造核心。) 
cd /usr/include 
ln -s ../src/linux/include/linux 
ln -s ../src/linux/include/asm 

诸如、、、 与之类 
的档案,会随著不同的编译器版本而异,属於你‘个人的’档案,可以在 
/usr/lib/gcc-lib/i486-box-linux/2.7.2/include/与其它相类似(相同) 
的目录名称的地方找到。 11/11/97译 5/14/98修正 

3.4 建立交叉编译器(Building cross compilers) 

将Linux当作标的作业平台(target platform) 

假设你已经拿到gcc的原始码,通常你只要依循INSTALL档的指示便可完成一切的 
设定。 make後面再接configure --target=i486-linux --host=XXX on 
platform XXX,就能帮你变把戏了。要注意的是,你会需要Linux还有核心的标头 
档;同时也需要建立交叉组译器(cross assembler)与交叉连结器(cross 
linker),来源是 [12]ftp://tsx-11.mit.edu/pub/linux/packages/GCC/。 

Linux当成原始作业平台(source platform)而MSDOS作为标的作业平台 

Ugh。很明显的,这个大概需要用到套件“emx”或是延伸套件“go”。请自行去 
[13]ftp://sunsite.unc.edu/pub/Linux/devel/msdos看看。我并没有测试过这些 
个东西,所以没有办法保证什麽。 

4. 移植程式与编译程式 

4.1 gcc自行定义的符号 

只要执行gcc时,附加 -v这个参数,就能找出你所用的这版gcc,自动帮你定义了 
什麽符号。例如,我的机器看起来会像这样: 

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__ 

假若你正在写的程式码会用到一些Linux独有的特性,那麽把那些无法移植的程式 
码,以条件式编译的前置命令封括起来,可是个不错的主意呢!如下所示∶ 

#ifdef __linux__ 
 
#endif  

用__linux__就可以达成目的;看仔细一点,不是linux喔。尽管linux也有定义, 
毕竟,这个仍然不是POSIX的标准。 

4.2 线上求助说明 

gcc编译器参数的说明文件是gcc info page(在Emacs内,按下C-h i,然後选 
‘gcc’的选项)。要是弄不出来,不是卖你CD-ROM的人没把这个东东压给你,不 
然就是你现在用的是旧版的。遇到这种情况,最好的方法是移动尊臀到archive 
[14]ftp://prep.ai.mit.edu/pub/gnu或是它的mirrors站台,去把gcc的原始档案 
抓回家,重新烹饪一番。 

gcc manual page(gcc.1) 可以说是已经过时了,要是你吃饱了撑著没事干硬是 
想看,它就会告诉你说别无聊了。 

旗正飘飘 

在命令列上执行gcc时,只要在它的屁股後面加上-On的选项,就能让gcc乖乖的替 
你生出最佳编码的机器码。这里的n是一个可有可无的小整数,不同版本的gcc 
,n的意义与其正确的功效都不一样,不过,典型的□围是从0(不要鸡婆,我不 
要最佳编码。)变化到2(最佳编码要多一点。),再升级到3(最佳编码要再多 
一点,多一点)。 

gcc在其内部会将这些数字转译成一系列的-f与-m的选项。执行gcc时带上旗号-v 
与-Q,你就能很清楚的看出每一种等级的-O是对应到那些选项。好比说,就-O2来 
讲,我的gcc告诉会我说: 

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 

要是你用的最佳编码等级高於你的编译器所能支援的(e.g. -O6),那麽它的效 
果就跟你用你的编译器所能提供的最高等级的效果是一样的。说实在的,发行出 
去的gcc程式码,用在编译时竟是如此处理这等问题,真的不是什麽好的构想。日 
後若是有更进步的最佳编码方法具体整合到新的版本里,而你(或是你的users) 
还是试著这样做的话,可能就会发现gcc会中断你的程式了。 

从gcc 2.7.0升级到2.7.2的users应该注意一点,使用-O2时会有一个bug。更糟糕 
的是,强度折减参数(strength reduction)居然没有用!要是你喜欢重新编 
译gcc的话,是有那麽一个修正的版本可以更正这项错误;不然的话,一定要确定 
每次编译时都有加上-fno-strength-reduce喔! 

11/12/97译 

有个性的微处理器 

有一些-m的旗号十分有用处,但是却无法藉由各种等级的-O打开来使用。这之中 
最重要的有是-m386和-m486这两种,用来告诉gcc该把正在编译的程式码视作专 
为386或是486机器所写的。不论是用哪一种-m来编译程式码,都可以在彼此的机 
器上执行,-m486编译出来的码会比较大,不过拿来在386的机器上跑也不会比较 
慢就是了。 

目前尚无-mpentium或是-m586的旗号。Linus建议我们可以用-m486 
-malign-loops=2 -malign-jumps=2 -malign-functions=2来得到最佳编码的486 
程式码,这样做正好就可以避免alignment(Pentium并不需要)有过大的gaps发 
生。Michael Meissner说: 

我的第六感告诉我,-mno-strength-reduce(嘿!要晓得我可不是在谈强度折 
减参数的bug呀,那已经是另外一个争论的战场了。)一样也可以在x86的机器 
上产生较快的程式码,这是因为x86的机器对暂存器有著不可磨灭的□渴在, 
而且GCCs method of grouping registers into spill registers vs. 
other registers doesnt help either。传统上,强度折减的结果会使得编 
译器去利用加法暂存器以加法运算来取代乘法运算。事实上,我在怀 
疑-fcaller-saves可能也只是个漏洞也说不定。 

而我的第七感则再度的告诉我说,-fomit-frame-pointer可能会也可能不会有 
任何的赚头。从这点来看,就是意谓著有另一个暂存器可以用来处理记忆体分 
配的问题。另方面,若纯粹从x86的机器在转换它的指令集成为机器码的方法 
上来看,便意谓著堆叠所用到的记忆体空间要比frame所用到的还要来得多; 
换句话说,Icache对程式码而言并没有实质上的帮助,若是阁下用 
了-fomit-frame-pointer的话,同时也是告诉编译器在每次呼叫函数之後,就 
必须修正堆叠的指标;然而,就frame来讲,若呼叫的次数不多的话,则允许 
堆叠暂时堆积起来。 

有关这方面主题的最後一段话仍是来自於Linus: 

要注意的是,如果你想要得到最佳状况的执行效能,可千万别相信我的话。无 
论如何,一定要进行测试。gcc编译器还有许多的参数可用,其中可能就有一 
种最特别的组合,可以给你最佳编码的结果。 

11/14/97译 5/15/98修正 

Internal compiler error: cc1 got fatal signal 11 

Signal 11是指 SIGSEGV,或者 ‘segmentation violation’。通常这是指 
说gcc对自己所用的指标感到困惑,而且还尝试著把资料写入不属於它的记忆体里 
。所以,这可能是一个gcc的bug。 然而,大体而言,gcc是一支经过严密测试且 
可靠度良好的软体佳作。它也用了大量复杂的资料结构与惊人的指标数量。简言 
之,若是要评选本世纪最挑惕与最一丝不□的RAM测试程式,gcc绝对可以一摘后 
冠。假如你无法重新复制这只bug---当你重新开始编译时,错误的讯息并没有一 
直出现在同一个地方---那几乎可以确定,是你的硬体本身有问题(CPU,记忆体,主 
机板或是快取记忆体).千万不要因为你的电脑可以通过开机程序的测试、或 
是Windows可以跑得很顺、或者其它什麽的,就回过头来大肆宣传说这是gcc的一 
个bug;你所做的这些测试动作,通常没有什麽实际上的价值,这是很合理的结论 
。另外,也不要因为编译核心时,总是停留在‘make zImage’的阶段,就要大骂 
这是gcc的bug---当然它会停在那儿啊!做‘make zImage’时,需要编译的档案 
可能就超过200档案;我们正在研拟一个替代的方案。 

如果你可以重覆产生这个bug,而且(最好是这样啦!)可以写一个短小的程式来 
展示这只bug的话,你就可以把它做成bug报告,然後email给FSF,或者 
是linux-gcc通信论坛。你可以去参考gcc的说明文件,看看有什麽详细的资讯,是 
他们所需要的。 

4.3 移植能力 

据报,近日来许多正面的消息指出,若是有某件东东到现在都还没移植到Linux上 
去,那麽可以肯定的是,它一定一点价值也没有。:-) 

嗯!正经一点。一般而言,原始码只需要做一些局部的修改,就可以克服Linux 
100%与POSIX相容的特质。如果你做了任何的修改,而将此部份传回给原作者,会 
是很有建设性的举动。这样日後就只需要用到‘make’,就能得到一个可执行的 
档案了。 

BSD教徒 (有 bsd_ioctl、daemon 与 

编译程式时,可以配合-I/usr/include/bsd与连结-lbsd的程式库。(例如:在你 
的Makefile档内,把-I/usr/include/bsd加到CFLAGS那一行;把-lbsd加 
到LDFLAGS那一行)。如果你真的那麽想要BSD型态的信号行为,也不需要再加 
上-D__USE_BSD_SIGNAL了。那是因为当你用了-I/usr/include/bsd与含括了标头 
档之後,make时就会自动加入了。 

失落的封印(SIGBUS, SIGEMT, SIGIOT, SIGTRAP, SIGSYS etc) 

Linux与POSIX是完全相容的。不过,有些信号并不是POSIX定义的---ISO/IEC 
9945-1:1990 (IEEE Std 1003.1-1990), paragraph B.3.3.1.1 sez: 

“在POSIX.1中省略了SIGBUS、SIGEMT、SIGIOT、SIGTRAP与SIGSYS信号,那是 
因为它们的行为与实作的方式息息相关,而且也无法进行适当的分类。确认实 
作方式後,便可以发送这些信号,可是必须以文件说明它们是在什麽样的环境 
底下发送出来的,以及指出任何与它们的发展相关的限制。” 

想要修正这个问题,最简单也是最笨的方法就是用SIGUNUSED重新定义这些信号。 
正确的方法应该是以条件式的编译#ifdef来处理这些问题才对: 

#ifdef SIGSYS 
 
#endif 

11/15/97译 5/22/98修正 



gcc是一个与ANSI相容的编译器;奇怪的是,目前大多数的程式码都不符合ANSI所 
定的标准。如果你热爱ANSI,喜欢用ANSI提供的标准来撰写C程式,似乎除了加 
上-traditional的旗号之外,就没有其它什麽可以多谈的了。There is 
certain amount of finer-grained control over which varieties of brain 
damage to emulate;请自行查阅gcc info page。 

要注意的是,尽管你用了-traditional来改变语言的特性,它的效果也仅局限 
於gcc所能够接受的□围。例如, -traditional会打开-fwritable-strings,使得 
字串常数移至资料记忆体空间内(从程式码记忆体空间移出来,这个地方是不能任 
意写入的)。这样做会让程式码的记忆体空间无形中增加的。 

前置处理器的符号卯上函数原型宣告 

最常见的问题是,如众所皆知,Linux中有许多常用的函数都定义成巨集存放在标 
头档内,此时若有相似的函数原型宣告出现在程式码内,前置处理器会拒绝进行 
语法分析的前置作业。常见的有atoi()与atol()。 

sprintf() 

在大部份的Unix系统上,sprintf(string, fmt, ...)传回的是string的指标,然 
而,这方面Linux(遵循ANSI)传回的却是放入string内的字元数目.进行移植时 
,尤其是针对SunOS,需有警觉的心。 

fcntl 与相关的函数;FD_*家族的定义到底摆在哪里? 

就在里头。 为了真正的原型宣告,当你用了fcntl,可能你也想含 
括标头档进来。 

一般而言,函数的manual page会在SYNOPSIS章节内列出需要的标头档。 

select()的计时---程式执行时会处於忙碌-等待的状态 

很久很久以前,,select()的计时参数只有唯读的性而已。即使到了最近 
,manual pages仍然有下面这段的警告: 

select()应该是藉由修正时间的数值(如果有的话),再传回自原始计时开始 
後所剩馀的时间。未来的版本可能会使这项功能实现。因此,就目前而言,若 
以为呼叫select()之後,计时指标仍然不会被修正过,可是一种非常不明智的 
想法喔! 

未来就在我们的眼前了!至少,在这儿你绝对可以看到。函数select()传回的, 
是扣除等待尚未到达的资料所耗费的时间後,其剩馀的时间数值。如果在计时结 
束时,都没有资料传送进来,计时引数便会设为0;如果接著还有任何 
的select(),以同样的计时structure来呼叫,那麽select()便会立刻结束。 

若要修正这项问题,只要每次呼叫select()前,都把计时数值放到计时 
structure内,就没有问题了。把下面的程式码, 

struct timeval timeout; 
timeout.tv_sec 1; timeout.tv_usec 0; 
while (some_condition) 
select(n,readfds,writefds,exceptfds,&timeout); 

改成, 

struct timeval timeout; 
while (some_condition) 
timeout.tv_sec 1; timeout.tv_usec 0; 
select(n,readfds,writefds,exceptfds,&timeout); 


这个问题,在有些版本的Mosaic里是相当著名的,只要一次的等待,Mosaic就挂 
在那里了。Mosaic的萤幕右上角,是不是有个圆圆的、会旋转的地球动画。那颗 
球转得愈快,就表示资料从网路上传送过来的速率愈慢! 

产生中断的系统呼叫 

特徵: 

当一支程式以Ctrl-Z中止、然後再重新执行时□或者是其它可以产生Ctrl-C中断 
信号的情况,如子程序的终结等□系统就会抱怨说"interrupted system call"或 
是"write: unknown error",或者诸如此类的讯息。 

问题点: 

POSIX的系统检查信号的次数,比起一些旧版的Unix是要多那麽一点。如果 
是Linux,可能就会执行signal handlers了□ 

非同步地(计时器的滴答声) 
系统呼叫的传回值 
在下列系统呼叫的执行期间∶ select(), pause(), connect(),accept(), 
read() on terminals, sockets, pipes or files in /proc, write() on 
terminals, sockets, pipes or the line printer, open() on FIFOs, 
PTYs or serial lines,ioctl() on terminals, fcntl() with command 
F_SETLKW, wait4(), syslog(), any TCP or NFS operations. 

就其它的作业系统而言,你需要的可能就是下面这些系统呼叫了: creat(), 
close(), getmsg(), putmsg(), msgrcv(), msgsnd(), recv(), send(), 
wait(), waitpid(), wait3(), tcdrain(), sigpause(), semop() to this 
list. 

在系统呼叫期间,若有一信号(那支程式本身应准备好handler因应了)产生 
,handler就会被呼叫。当handler将控制权转移回系统呼叫时,它会侦测出它已 
经产生中断,而且传回值会立刻设定成-1,而errno设定成EINTR。程式并没有想 
到会发生这种事,所以就挂了。 

有两种修正的方法可以选择: 

(1) 对每个你自行安装的signal handler,都须在sigaction的旗号加 
上SA_RESTART。例如,把下列的程式, 

signal (sig_nr, my_signal_handler); 

改成, 

signal (sig_nr, my_signal_handler); 
struct sigaction sa; 
sigaction (sig_nr, (struct sigaction *)0, &sa); 
#ifdef SA_RESTART 
sa.sa_flags |= SA_RESTART; 
#endif 
#ifdef SA_INTERRUPT 
sa.sa_flags &= SA_INTERRUPT; 
#endif 
sigaction (sig_nr, &sa, (struct sigaction *)0); 


要注意的是,当这部份的变更大量应用到系统呼叫之後,呼叫read()、write() 
、ioctl()、 select()、 pause() 与 connect()时,你仍然得自行检查EINTR。 
如下所示: 

(2) 你自己得很明确地检查EINTR: 

这里有两个针对read()与ioctl()的例子。 

原始的程式片段,使用read(): 

int result; 
while (len >; 0) 
result read(fd,buffer,len); 
if (result 0) break; 
buffer += result; len -= result; 


修改成, 

int result; 
while (len >; 0) 
result read(fd,buffer,len); 
if (result 0) if (errno != EINTR) break; 
else buffer += result; len -= result; 


原始的程式片段,使用ioctl(): 

int result; 
result ioctl(fd,cmd,addr); 

修改成, 

int result; 
do result ioctl(fd,cmd,addr); 
while ((result == -1) && (errno == EINTR)); 

注意一点,有些版本的BSD Unix,其内定的行为是重新执行系统呼叫。若要让系 
统呼叫中断,得使用 SV_INTERRUPT或SA_INTERRUPT旗号。 

可以写入的字串 

gcc对其users总怀抱著乐观的想法,相信当他们打算让某个字串当作常数来用 
时---那它就真的只是字串常数而已。因此,这种字串常数会储存在程式码的记忆 
体区段内。这块区域可以page到磁碟机的image上,避免耗掉swap的记忆体空间, 
而且任何尝试写入的举动都会造成分页的错误(segmentation fault)。这可是一 
种特色呢! 

对老旧一点的程式而言,这可能会产生一个问题。例如,呼叫mktemp(),传递的 
引数(arguments)是字串常数。 mktemp()会尝试著在*适当的位置*重新写入它的 
引数。
The <wbr>Linux <wbr>GCC <wbr>HOWTO中译版

原文链接:http://linux.chinaunix.net/bbs/viewthread.php?tid=232017
转载请注明作者名及原文出处


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值