⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 移植(porting)与编译(compiling)程式.htm

📁 gcc的安装相关资料 详细介绍了gcc的安装步骤和方法
💻 HTM
📖 第 1 页 / 共 2 页
字号:
<P>在大部份的Unix系统上, <CODE>sprintf(string, fmt, 
...)</CODE>传回的是<CODE>string</CODE>的指标,然而,这方面Linux(遵循ANSI)传回的却是放入string内的字元数目.进行移植时,尤其是针对SunOS,需有警觉的心.</P>
<H4><A name=30></A>4.3.6. <CODE>fcntl</CODE> 与相关的函数; 
<CODE>FD_*</CODE>家族的定义到底摆在哪里?<A name=index.43></A> <A name=index.44></A><A 
name=index.45></A><A name=index.46></A><A name=index.47></A><A 
name=index.48></A><A name=index.49></A></H4>
<P>就在 <CODE>&lt;sys/time.h&gt;</CODE>里头. 
为了真正的原型宣告,当你用了<CODE>fcntl</CODE>,可能你也想含括标头档<CODE>&lt;unistd.h&gt;</CODE>进来.</P>
<P>一般而言,函数的manual page会在SYNOPSIS章节内列出需要的<CODE>标头档</CODE>.</P>
<H4><A name=31></A>4.3.7. <CODE>select()</CODE> 
的计时(time-out)---程式执行时会处於忙碌-等待的状态(busy-waiting).<A name=index.50></A> </H4>
<P>很久很久以前, <CODE>select()</CODE>的计时参数(time-out 
parameter)只有读的属性(read-only)而已.即使到了最近,manual pages仍然有下面这段的警告:</P>
<P>
<BLOCKQUOTE>select()照理讲应该是藉由适当的修正时间的数值,再传回自原始计时(original 
  time-out)开始後所剩馀的时间.未来的版本可能会使这项功能实现.因此,就目前而言,若假定在呼叫select()之後,计时指标(time-out 
  pointer)仍然不会让人给修正过,可是一种非常不明智的想法喔! </BLOCKQUOTE>
<P></P>
<P>未来就在我们的眼前了!至少,在这儿你绝对可以看到. 
函数<CODE>select()</CODE>传回的,是扣除等待尚未到达的资料所耗费的时间後,其剩馀的时间值.如果在计时结束时,都没有资料传送进来,计时引数(time-out 
argument)便会设为0;如果接著还有任何的select(),以同样的time-out structure来呼叫,那麽select()便会立刻结束.</P>
<P>若要修正这项问题,只要每次呼叫<CODE>select()</CODE>前,都把计时数值(time-out value)放到time-out 
structure内,就没有问题了.把下面的程式码, 
<BLOCKQUOTE><CODE><PRE>      struct timeval timeout;
      timeout.tv_sec = 1; timeout.tv_usec = 0;
      while (some_condition)
            select(n,readfds,writefds,exceptfds,&amp;timeout); 
</PRE></CODE></BLOCKQUOTE>改成, 
<BLOCKQUOTE><CODE><PRE>      struct timeval timeout;
      while (some_condition) {
            timeout.tv_sec = 1; timeout.tv_usec = 0;
            select(n,readfds,writefds,exceptfds,&amp;timeout);
      }
</PRE></CODE></BLOCKQUOTE>
<P></P>
<P>这个问题,在有些版本的Mosaic里是相当著名的,只消一次的等待,Mosaic就挂了.Mosaic的萤幕右上角,是不是有个圆圆的,会旋转的地球动画.那颗球转得愈快,就表示资料从网路上传送过来的速率愈慢!</P>
<H4><A name=32></A>4.3.8. 产生中断的系统呼叫(Interrupted system calls)<A 
name=index.51></A> <A name=index.52></A></H4>
<H5><A name=33></A>4.3.8.1. 徵兆(Symptom):</H5>
<P>当一支程式以Ctrl-Z中止(stop),然後再重新执行(restart)时--或者是其它可以产生Ctrl-C中断(interruption)信号的情况,如子程序(child 
process)终结(termination)等--系统就会抱怨说"interrupted system call"或是"write: unknown 
error",或者诸如此类的讯息.</P>
<H5><A name=34></A>4.3.8.2. 问题点:</H5>
<P>POSIX的系统检查信号的次数,比起一些旧版的Unix是要多那麽一点.如果是Linux,可能就会执行signal handlers了--</P>
<P>
<UL>
  <LI>非同步地(asynchronously)(计时器的滴答声) 
  <LI>系统呼叫的传回值(on return from any system call) 
  <LI>在下列系统呼叫的执行期间: <CODE>select()</CODE>, <CODE>pause()</CODE>, 
  <CODE>connect()</CODE>,<CODE>accept()</CODE>, <CODE>read()</CODE> on 
  terminals, sockets, pipes or files in <CODE>/proc</CODE>, <CODE>write()</CODE> 
  on terminals, sockets, pipes or the line printer, <CODE>open()</CODE> on 
  FIFOs, PTYs or serial lines,<CODE>ioctl()</CODE> on terminals, 
  <CODE>fcntl()</CODE> with command <CODE>F_SETLKW</CODE>, <CODE>wait4()</CODE>, 
  <CODE>syslog()</CODE>, any TCP or NFS operations. </LI></UL>
<P></P>
<P>就其它的作业系统而言,你需要的可能就是下面这些系统呼叫(system calls)了: <CODE>creat()</CODE>, 
<CODE>close()</CODE>, <CODE>getmsg()</CODE>, <CODE>putmsg()</CODE>, 
<CODE>msgrcv()</CODE>, <CODE>msgsnd()</CODE>, <CODE>recv()</CODE>, 
<CODE>send()</CODE>, <CODE>wait()</CODE>, <CODE>waitpid()</CODE>, 
<CODE>wait3()</CODE>, <CODE>tcdrain()</CODE>, <CODE>sigpause()</CODE>, 
<CODE>semop()</CODE> to this list.</P>
<P>在系统呼叫期间,若有一信号(那支程式本身应准备好handler因应了)产生,handler就会被呼叫.当handler将控制权转移回系统呼叫时,它会侦测出它已经产生中断,而且传回值会立刻设定成-1,<CODE>errno设定成EINTR</CODE>.程式并没有想到会发生这种事,所以就会bottles 
out了.</P>
<P>有两种修正的方法可以选择:</P>
<P>(1) 对每个你自行安装(install)的signal 
handler,都须在sigaction旗号加上<CODE>SA_RESTART</CODE>.例如,把下列的程式,</P>
<P>
<BLOCKQUOTE><CODE><PRE>  signal (sig_nr, my_signal_handler);
</PRE></CODE></BLOCKQUOTE>改成, 
<BLOCKQUOTE><CODE><PRE>  signal (sig_nr, my_signal_handler);
  { struct sigaction sa;
    sigaction (sig_nr, (struct sigaction *)0, &amp;sa);
#ifdef SA_RESTART
    sa.sa_flags |= SA_RESTART;
#endif
#ifdef SA_INTERRUPT
    sa.sa_flags &amp;= ~ SA_INTERRUPT;
#endif
    sigaction (sig_nr, &amp;sa, (struct sigaction *)0);
  }
</PRE></CODE></BLOCKQUOTE>
<P></P>
<P>要注意的是,当这部份的变更大量应用到系统呼叫之後,呼叫<CODE>read()</CODE>, 
<CODE>write()</CODE>,<CODE>ioctl()</CODE>, <CODE>select()</CODE>, 
<CODE>pause()</CODE> 与 <CODE>connect()</CODE>时,你仍然得自行检查(check 
for)<CODE>EINTR</CODE>.如下所示.</P>
<P>(2) 你自己得很明确地(explicitly)检查<CODE>EINTR</CODE>:</P>
<P>这里有两个针对<CODE>read()</CODE>与<CODE>ioctl()</CODE>的例子.</P>
<P>原始的程式片段,使用<CODE>read()</CODE>.</P>
<P>
<BLOCKQUOTE><CODE><PRE>int result;
while (len &gt; 0) { 
  result = read(fd,buffer,len);
  if (result &lt; 0) break;
  buffer += result; len -= result;
}
</PRE></CODE></BLOCKQUOTE>修改成, 
<BLOCKQUOTE><CODE><PRE>int result;
while (len &gt; 0) { 
  result = read(fd,buffer,len);
  if (result &lt; 0) { if (errno != EINTR) break; }
  else { buffer += result; len -= result; }
}
</PRE></CODE></BLOCKQUOTE>原始的程式片段,使用<CODE>ioctl()</CODE>.
<P></P>
<P>
<BLOCKQUOTE><CODE><PRE>int result;
result = ioctl(fd,cmd,addr);
</PRE></CODE></BLOCKQUOTE>修改成, 
<BLOCKQUOTE><CODE><PRE>int result;
do { result = ioctl(fd,cmd,addr); }
while ((result == -1) &amp;&amp; (errno == EINTR));
</PRE></CODE></BLOCKQUOTE>
<P></P>
<P>注意一点,有些版本的BSD Unix,其内定的行为(default behaviour)是重新执行系统呼叫.若要让系统呼叫中断,得使用 
<CODE>SV_INTERRUPT</CODE>或<CODE>SA_INTERRUPT</CODE>旗号.</P>
<H4><A name=35></A>4.3.9. 可以写入的字串(Writable strings)<A name=index.53></A> <A 
name=index.54></A><A name=index.55></A><A name=index.56></A></H4>
<P>gcc对其users总怀抱著乐观的想法(optimistic 
view),相信当他们打算让某个字串当作常数来用时---那它就真的只是字串常数而已.因此,这种字串常数会储存在程式码的记忆体区段内(in the code 
area of the 
program).这块区域可以page到磁碟机的image上,避免耗掉swap的记忆体空间,而且任何尝试写入的举动都会造成分页的错误(segmentation 
fault).这可是一种特色呢!</P>
<P>对老旧一点的程式而言, 这可能会产生一个问题.例如,呼叫<CODE>mktemp()</CODE>,传递引数(arguments)是字串常数. 
<CODE>mktemp()</CODE>会尝试著在*适当的位置(in place)*重新写入它的引数.</P>
<P>修正的方法不外乎(a)以<CODE>-fwritable-strings</CODE>编译,迫使gcc将此常数置放在资料记忆体空间(data 
space)内.或者(b)将侵犯地权的部份(offending parts)重新改写,配置一个不为常数的字串(non-constant 
string),在呼叫前,先以strcpy()将资料拷贝进去.</P>
<H4><A name=36></A>4.3.10. 为什麽呼叫<CODE>execl()</CODE>会失败?<A name=index.57></A> 
</H4>
<P>那是因为你呼叫的方式不对.<CODE>execl</CODE>的第一个引数是你想要执行的程式名.第二个与接续的引数会变成你所呼叫的程式的<CODE>argv</CODE>阵列(array).记住:传统上,<CODE>argv[0]</CODE>是只有当程式没有带著引数执行时,才会有设定值.所以罗,你应该这样写:</P>
<P>
<BLOCKQUOTE><CODE><PRE>execl("/bin/ls","ls",NULL);
</PRE></CODE></BLOCKQUOTE>而不是只有, 
<BLOCKQUOTE><CODE><PRE>execl("/bin/ls", NULL);
</PRE></CODE></BLOCKQUOTE>
<P></P>
<P>执行程式而不带任何引数(with no 
arguments),可解释成(construe)是一种邀请函(invitation),目的是把此程式的动态程式库独立(dynamic library 
dependencies)的特性印出来(print out).至少,a.out是这样的.就ELF而言,事情就不是这样了.</P>
<P>(如果你想得知此程式库的资讯,有一些更简单的介面可用;参考动态载入(dynamic 
loading)那一章节,或是<CODE>ldd</CODE>的manual page.)</P>
<P>11/16/97译</P>
<HR noShade>
<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> </BODY></HTML>

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -