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

📄 036.htm

📁 LINUX的操作系统分析文件和使用文件
💻 HTM
📖 第 1 页 / 共 2 页
字号:
#ifdef SIGSYS <br>/* ... non-posix SIGSYS code here .... */ <br>#endif <br><br><br>11/15/97译 <br><br>4.3.3. K &amp; R <br>gcc是个与ANSI相容的编译器;奇怪的是,目前大多数的程式码都不符合ANSI所定的标准.如果你热爱ANSI,喜欢用ANSI提供的标准来撰写C程式,似乎除了在编译器的旗号上加上-traditional之外,就没有什麽其它的可以多谈的了.There is a certain amount of finer-grained control over which varieties of brain damage to emulate;请自行查阅gcc info page. <br><br>要注意的是,尽管你用了-traditional来改变语言,它的效果也仅局限在gcc所能够接受的□围.例如, -traditional会打开(turn on)-fwritable-strings,使得字串常数(string constants)移至资料记忆体空间(data space)内(从程式码记忆体空间(text space),这地方是不能任意写入的).这样做会让程式码的记忆体空间无形中增加的. <br><br>4.3.4. 前置处理器(Preprocessor)的符号卯上函数原型宣告(prototypes) <br>最常见的问题是,如众所皆知,Linux中有许多常用的函数都定义成巨集(macros)存放在标头档(header files)内,此时若有相似的函数原型宣告出现在程式码内,前置处理器会拒绝进行语法分析(parse)的前置作业.常见的有atoi()与atol(). <br><br>4.3.5. sprintf() <br>在大部份的Unix系统上, sprintf(string, fmt, ...)传回的是string的指标,然而,这方面Linux(遵循ANSI)传回的却是放入string内的字元数目.进行移植时,尤其是针对SunOS,需有警觉的心. <br><br>4.3.6. fcntl 与相关的函数; FD_*家族的定义到底摆在哪里? <br>就在 &lt;sys/time.h&gt;里头. 为了真正的原型宣告,当你用了fcntl,可能你也想含括标头档&lt;unistd.h&gt;进来. <br><br>一般而言,函数的manual page会在SYNOPSIS章节内列出需要的标头档. <br><br>4.3.7. select() 的计时(time-out)---程式执行时会处於忙碌-等待的状态(busy-waiting). <br>很久很久以前, select()的计时参数(time-out parameter)只有读的属性(read-only)而已.即使到了最近,manual pages仍然有下面这段的警告: <br><br><br>select()照理讲应该是藉由适当的修正时间的数值,再传回自原始计时(original time-out)开始後所剩馀的时间.未来的版本可能会使这项功能实现.因此,就目前而言,若假定在呼叫select()之後,计时指标(time-out pointer)仍然不会让人给修正过,可是一种非常不明智的想法喔! <br><br>未来就在我们的眼前了!至少,在这儿你绝对可以看到. 函数select()传回的,是扣除等待尚未到达的资料所耗费的时间後,其剩馀的时间值.如果在计时结束时,都没有资料传送进来,计时引数(time-out argument)便会设为0;如果接著还有任何的select(),以同样的time-out structure来呼叫,那麽select()便会立刻结束. <br><br>若要修正这项问题,只要每次呼叫select()前,都把计时数值(time-out value)放到time-out structure内,就没有问题了.把下面的程式码, <br><br>struct timeval timeout; <br>timeout.tv_sec = 1; timeout.tv_usec = 0; <br>while (some_condition) <br>select(n,readfds,writefds,exceptfds,&amp;timeout); <br><br>改成, <br>struct timeval timeout; <br>while (some_condition) { <br>timeout.tv_sec = 1; timeout.tv_usec = 0; <br>select(n,readfds,writefds,exceptfds,&amp;timeout); <br>} <br><br><br>这个问题,在有些版本的Mosaic里是相当著名的,只消一次的等待,Mosaic就挂了.Mosaic的萤幕右上角,是不是有个圆圆的,会旋转的地球动画.那颗球转得愈快,就表示资料从网路上传送过来的速率愈慢! <br><br>4.3.8. 产生中断的系统呼叫(Interrupted system calls) <br>4.3.8.1. 徵兆(Symptom): <br>当一支程式以Ctrl-Z中止(stop),然後再重新执行(restart)时--或者是其它可以产生Ctrl-C中断(interruption)信号的情况,如子程序(child process)终结(termination)等--系统就会抱怨说&quot;interrupted system call&quot;或是&quot;write: unknown error&quot;,或者诸如此类的讯息. <br><br>4.3.8.2. 问题点: <br>POSIX的系统检查信号的次数,比起一些旧版的Unix是要多那麽一点.如果是Linux,可能就会执行signal handlers了-- <br><br><br>非同步地(asynchronously)(计时器的滴答声) <br>系统呼叫的传回值(on return from any system call) <br>在下列系统呼叫的执行期间: 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. <br><br>就其它的作业系统而言,你需要的可能就是下面这些系统呼叫(system calls)了: creat(), close(), getmsg(), putmsg(), msgrcv(), msgsnd(), recv(), send(), wait(), waitpid(), wait3(), tcdrain(), sigpause(), semop() to this list. <br><br>在系统呼叫期间,若有一信号(那支程式本身应准备好handler因应了)产生,handler就会被呼叫.当handler将控制权转移回系统呼叫时,它会侦测出它已经产生中断,而且传回值会立刻设定成-1,errno设定成EINTR.程式并没有想到会发生这种事,所以就会bottles out了. <br><br>有两种修正的方法可以选择: <br><br>(1) 对每个你自行安装(install)的signal handler,都须在sigaction旗号加上SA_RESTART.例如,把下列的程式, <br><br><br>signal (sig_nr, my_signal_handler); <br><br>改成, <br>signal (sig_nr, my_signal_handler); <br>{ struct sigaction sa; <br>sigaction (sig_nr, (struct sigaction *)0, &amp;sa); <br>#ifdef SA_RESTART <br>sa.sa_flags |= SA_RESTART; <br>#endif <br>#ifdef SA_INTERRUPT <br>sa.sa_flags &amp;= ~ SA_INTERRUPT; <br>#endif <br>sigaction (sig_nr, &amp;sa, (struct sigaction *)0); <br>} <br><br><br>要注意的是,当这部份的变更大量应用到系统呼叫之後,呼叫read(), write(),ioctl(), select(), pause() 与 connect()时,你仍然得自行检查(check for)EINTR.如下所示. <br><br>(2) 你自己得很明确地(explicitly)检查EINTR: <br><br>这里有两个针对read()与ioctl()的例子. <br><br>原始的程式片段,使用read(). <br><br><br>int result; <br>while (len &gt; 0) { <br>result = read(fd,buffer,len); <br>if (result &lt; 0) break; <br>buffer += result; len -= result; <br>} <br><br>修改成, <br>int result; <br>while (len &gt; 0) { <br>result = read(fd,buffer,len); <br>if (result &lt; 0) { if (errno != EINTR) break; } <br>else { buffer += result; len -= result; } <br>} <br><br>原始的程式片段,使用ioctl(). <br><br><br>int result; <br>result = ioctl(fd,cmd,addr); <br><br>修改成, <br>int result; <br>do { result = ioctl(fd,cmd,addr); } <br>while ((result == -1) &amp;&amp; (errno == EINTR)); <br><br><br>注意一点,有些版本的BSD Unix,其内定的行为(default behaviour)是重新执行系统呼叫.若要让系统呼叫中断,得使用 SV_INTERRUPT或SA_INTERRUPT旗号. <br><br>4.3.9. 可以写入的字串(Writable strings) <br>gcc对其users总怀抱著乐观的想法(optimistic view),相信当他们打算让某个字串当作常数来用时---那它就真的只是字串常数而已.因此,这种字串常数会储存在程式码的记忆体区段内(in the code area of the program).这块区域可以page到磁碟机的image上,避免耗掉swap的记忆体空间,而且任何尝试写入的举动都会造成分页的错误(segmentation fault).这可是一种特色呢! <br><br>对老旧一点的程式而言, 这可能会产生一个问题.例如,呼叫mktemp(),传递引数(arguments)是字串常数. mktemp()会尝试著在*适当的位置(in place)*重新写入它的引数. <br><br>修正的方法不外乎(a)以-fwritable-strings编译,迫使gcc将此常数置放在资料记忆体空间(data space)内.或者(b)将侵犯地权的部份(offending parts)重新改写,配置一个不为常数的字串(non-constant string),在呼叫前,先以strcpy()将资料拷贝进去. <br><br>4.3.10. 为什麽呼叫execl()会失败? <br>那是因为你呼叫的方式不对.execl的第一个引数是你想要执行的程式名.第二个与接续的引数会变成你所呼叫的程式的argv阵列(array).记住:传统上,argv[0]是只有当程式没有带著引数执行时,才会有设定值.所以罗,你应该这样写: <br><br><br>execl(&quot;/bin/ls&quot;,&quot;ls&quot;,NULL); <br><br>而不是只有, <br>execl(&quot;/bin/ls&quot;, NULL); <br><br><br>执行程式而不带任何引数(with no arguments),可解释成(construe)是一种邀请函(invitation),目的是把此程式的动态程式库独立(dynamic library dependencies)的特性印出来(print out).至少,a.out是这样的.就ELF而言,事情就不是这样了. <br><br>(如果你想得知此程式库的资讯,有一些更简单的介面可用;参考动态载入(dynamic loading)那一章节,或是ldd的manual page.) <br><br>11/16/97译 <br></p><hr SIZE="0"><p align="center"><a href="linux.htm">返回</a></p><p><br></p></body></html>

⌨️ 快捷键说明

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