当前位置:首页 > GCC使用介绍
下载-D__linux -Asystem(unix) -Asystem(posix) -Acpu(i386)-Amachine(i386) -D__i486__ -
附录A计GCC 使用介绍计计399假若你正在编写的程序代码会用到一些L inux独有的特性,那么把那些无法移植的程序代码,以条件式编译的前置命令封括起来。如下所示∶
#ifdef __linux__/* ... funky stuff ... */#endif /* linux */
用_ _linux__就可以完成任务;看仔细一点,不是l inux。尽管l inux也有定义,毕竟,这个不是P OSIX的标准。A.6.2 调用编译程序
在命令行上执行g cc时,只要在它的后面加上- On的选项,就能让g cc编译出最优化的计算机编码。这里的n是一个可以省略的小整数,不同版本的g cc,n的意义与其作用都不一样,不过,典型的范围是从0变化到2,再升级到3。
g cc在其内部会将这些数字转换成一系列的- f与- m的选项。执行g cc时带上标志- v与- Q,你就能很清楚的看出每一种等级的- O对应到哪些选项。好比说,就- O2来讲,我的g cc会显示:
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
要是你用的最优化编码等级高于你的编译器所能支持的等级(例如- O6),那么它的效果就跟你用你的编译器所能提供的最高等级的效果是一样的。A.6.3 和特定的微处理器相关
有一些- m的标志十分有用,但是却无法根据各种等级的- O来使用。这之中最重要的是-m 386和- m486这两种。它们用来告诉g cc该把正在编译的程序代码视作专为3 86或是4 86计算机所写的代码。不论是用哪一种- m来编译程序代码,都可以在彼此的计算机上执行。但- m486编译出来的代码会比较大,不过拿来在3 86的计算机上运行也不会比较慢就是了。
目前尚无- mpentium或是- m586的标志。L inux建议我们可以用-m486 -malign-loops=2-malign-jumps=2 -malign-functions=2来得到最佳的4 86程序代码。A.6.4 Internal compiler error: cc1 got fatal signal 11
Signal 11是指S IGSEGV,或者“segmentation violation”。通常这是说g cc对自己所用的指标感到困惑,而且还试图把信息写入不属于它的内存里。所以,这可能是一个g cc的错误。然而,大体而言,g cc是一个经过严密测试且可靠度良好的软件。它也使用了大量复杂的信息结构与惊人数量的指标。假如你无法再一次重复这个错误(当你重新开始编译时,错误的信息并没有一直出现在同一个地方)那几乎可以确定是你的硬件( CPU、内存、主机板或是高速缓存)本身有问题。千万不要因为你的电脑可以通过开机程序的测试、或者Wi ndows可以运行得好、或者其他什么程序可以运行得好,就回过头来说这是g cc的一个错误;你所做的这些开机测试动作,通常没有什么实际上的价值。
400计计第六篇X Window 系统的内部结构和使用下载A.6.5 移植能力
1. BSD的用户(有b sd_ioctl、守护进程和< sgtty. h>)
你可以使用- I / u s r / i n c l u d e / b s d来编译程序,同时使用- l b s d来链接程序。例如:在你的M akefile文件内,把- I/usr/include/bsd加到C FLAGS那一行;把- lbsd加到L DFLAGS那一行。如果你确实需要B SD类型的信号,也不需要再加上- D__USE_BSD_SIGNAL了。那是因为当你用了- I/usr/include/bsd,并且包括了头文件< sig?nal.h>之后,建立时就会自动加入这些信号。2. 丢失的信号( SIGBUS、S IGEMT、S IGIOT、S IGTRAP、SIGSYS 等)L inux与P OSIX是完全相容的。不过,有些信号并不是P OSIX中定义的。
在P OSIX.1中省略了S IGBUS、S IGEMT、S IGIOT、S IGTRAP与S IGSYS信号,那是因为它们的行为与实际运行的方式息息相关,而且也无法进行适当的分类。确认实际运行方式后,便可以发送这些信号,可是必须以文件形式说明它们是在什么样的环境下发送出来的,以及指出任何与它们的发展相关的限制。
想要修正这个问题,最简单也是最笨的方法就是用S IGUNUSED重新定义这些信号。正确的方法应该是以条件式的编译# ifdef的形式来处理这些问题:
#ifdef SIGSYS
/* ... non-posix SIGSYS code here .... */# endif
3. K & R代码
g cc是一个与A NSI相容的编译器;奇怪的是,目前大多数的程序代码都不符合A NSI所定的标准。如果你喜欢用A NSI提供的标准来编写C程序,似乎除了加上- traditional的标志之外,就没有其他什么可以多谈的了
要注意的是,尽管你用了- traditional来改变语言的特性,它的效果也仅局限于g cc所能够接受的范围。例如, -traditional会打开- fwritable-strings,使得字符串常数移至数据内存空间内(从程序代码内存空间移出来,这个地方是不能任意写入的)。这样做会让程序代码的内存空间无形中增加了。
4. 预处理符号和函数原型的冲突
最常见的问题是L inux中有许多常用的函数都定义成宏存放在头文件内。此时若有相似的函数原型出现在程序代码内,预处理程序会拒绝进行语法分析的预处理。常见的函数有a toi()与a tol()。
5. sprintf()函数
在大部分的U nix系统上,sprintf(string, fmt,...)传回的是s tring的指针,然而,L inux(遵循A NSI)传回的却是放入s tring内的字符数目。
6. select()函数—程序执行时会处于忙碌/等待的状态
很久以前,s elect()的计时参数还只是只读的。即使到了最近,操作联机帮助中仍然有下面这段的警告:
s elect()应该是根据修正时间的数值(如果有的话),再传回自原始计时开始后所剩余的时间。未来的版本可能会使这项功能实现。因此,就目前而言,若以为调用s elect()之后,计时指针仍然不会被修正过,是不正确的。
函数s elect()传回的是扣除等待尚未到达的信息所耗费的时间后,其剩余的时间数值。如果在计时结束时,都没有信息传送进来,计时参数便会设为0;如果接着还有s elect()函数,以同样的计时数据结构来调用,那么s elect()便会立刻结束。
下载附录A计GCC 使用介绍计计401若要修正这项问题,只要每次调用s elect()前,都把计时数值放到计时数据结构内,这样就没有问题了。把下面的程序代码,
struct timeval timeout;
timeout.tv_sec = 1; timeout.tv_usec = 0;while (some_condition)
s elect(n,readfds,writefds,exceptfds,&timeout);
改成,
struct timeval timeout;while (some_condition) {
timeout.tv_sec = 1; timeout.tv_usec = 0;
s elect(n,readfds,writefds,exceptfds,&timeout);}
这个问题,在有些版本的M osaic里是相当著名的,只要有一次的等待,M osaic就停止在那里了。M osaic的屏幕右上角,有个圆圆的、会旋转的地球动画。那个球转得愈快,就表示信息从网络上传送过来的速率愈慢!
7. 产生中断的系统调用
当一个程序以C trl-Z中止、然后再重新执行时,或者有其他可以产生C trl-C中断信号的情况,如子程序的终结等,系统就会显示“interrupted system call”或者“write: unknown error”,或者诸如此类的信息。
比起一些旧版的Unix ,P OSIX的系统检查信号的次数是多一点。而L inux可能会执行s ignal处理程序。
8. 系统调用的返回值
在下列系统调用的执行期间∶
s elect()、p ause()、c onnect()、a ccept()、r ead()(终端上)、套接字、管道或文件(/ proc中)、w rite()(终端上)、套接字、管道或行式打印机、o pen()(F IFO上)、P TY或串行线路、i octl()(终端上)、f cntl()(具有命令F _SETLKW)、w ait4()、s yslog()、任何T CP或N FS操作。
就其他的操作系统而言,你需要的可能就是下面这些系统调用了:c r e a t ( )、c l o s e ( )、g e t m s g ( )、p u t m s g ( )、m s g r c v ( )、m s g s n d ( )、r e c v ( )、s e n d ( )、w a i t ( )、w a i t p i d ( )、w a i t 3 ( )、t cdrain()、s igpause()、semop() 。
在系统调用期间,若有一信号产生,处理程序就会被调用。当处理程序将控制权转移回系统调用时,它会侦测出已经产生中断,而且返回值会立刻设置成- 1,而e rrno设置成E INTR。程序并没有想到会发生这种事,所以就停止了。
有两种修正的方法可以选择:
1) 对每个你自行安装的信号处理程序,都须在s igaction的标志加上S A_RESTA RT。例如,把下列的程序,
signal (sig_nr, my_signal_handler);
改成:
signal (sig_nr, my_signal_handler);{ struct sigaction sa;
sigaction (sig_nr, (struct sigaction *)0, &sa);#ifdef SA_RESTA RT
sa.sa_flags |= SA_RESTA RT;# endif
#ifdef SA_INTERRUPT
402计计第六篇X Window 系统的内部结构和使用下载sa.sa_flags &= ~ SA_INTERRUPT;# endif
sigaction (sig_nr, &sa, (struct sigaction *)0);}
要注意的是,当这部分的更改大量应用到系统调用之后,调用r ead()、w rite()、i octl()、s elect()、pause() 与c onnect()时,你仍然得自行检查E INTR。如下所示:
2) 你自己明确地检查E INTR:
这里有两个针对r ead()与i octl()的例子。原始的程序使用r ead():
int result;
while (len > 0) {
result = read(fd,buff er, len);if (result < 0) break;
b uffer += result; len -= result;}
修改成,
int result;
while (len > 0) {
result = read(fd,buff er, len);
if (result < 0) { if (errno != EINTR) break; }else { buffer += result; len -= result; }}
原始的程序使用i octl():
int result;
result = ioctl(fd,cmd,addr);
修改成,
int result;
do { result = ioctl(fd,cmd,addr); }
while ((result == -1) && (errno == EINTR));
注意一点,有些版本的BSD Unix,其内定的行为是重新执行系统调用。若要让系统调用中断,得使用S V_INTER?RUPT或S A_INTERRUPT标志。
9. 可以写入的字符串
g cc认为当用户打算让某个字符串当作常数来使用时,它就只是字符串常数而已。因此,这种字符串常数会保存在程序代码的内存区段内。这块区域可以交换到磁盘的i mage文件上,避免耗掉交换区占用的内存空间,而且任何尝试写入的举动都会造成分页的错误。
对旧一点的程序而言,这可能会产生一个问题。例如,调用m ktemp(),传递的参数是字符串常数。m ktemp()会试图在“适当的位置”重新写入它的参数。
修正的方法不外乎如下:
? 以- fwritable-strings编译,迫使g cc将此常数置放在数据内存空间内。
? 将侵犯地址的部分重新改写,配置一个不为常数的字符串,在调用前,先以s trcpy()将拷贝进去。10. 为什么调用e xecl()会失败?
那是因为调用的方式不对。e xecl的第一个参数是想要执行的程序名。第二个与后续的参数会变成所调用的程序的a rg v数组。记住:传统上,a rg v[0]是只有当程序没有带着参数执行时才会有的设置值。所以,你应该这样写:
共分享92篇相关文档