?? gcc-howto-4.html
字號(hào):
select()應(yīng)該是藉由修正時(shí)間的數(shù)值(如果有的話),再傳回自原始計(jì)時(shí)開始後所剩馀的時(shí)間。未來的版本可能會(huì)使這項(xiàng)功能實(shí)現(xiàn)。因此,就目前而言,若以為呼叫select()之後,計(jì)時(shí)指標(biāo)仍然不會(huì)被修正過,可是一種非常不明智的想法喔!
</BLOCKQUOTE>
<P>未來就在我們的眼前了!至少,在這兒你絕對(duì)可以看到。函數(shù)<CODE>select()</CODE>傳回的,是扣除等待尚未到達(dá)的資料所耗費(fèi)的時(shí)間後,其剩馀的時(shí)間數(shù)值。如果在計(jì)時(shí)結(jié)束時(shí),都沒有資料傳送進(jìn)來,計(jì)時(shí)引數(shù)便會(huì)設(shè)為0;如果接著還有任何的select(),以同樣的計(jì)時(shí)structure來呼叫,那麼select()便會(huì)立刻結(jié)束。
<P>若要修正這項(xiàng)問題,只要每次呼叫<CODE>select()</CODE>前,都把計(jì)時(shí)數(shù)值放到計(jì)時(shí) structure內(nèi),就沒有問題了。把下面的程式碼,
<BLOCKQUOTE><CODE>
<PRE>
struct timeval timeout;
timeout.tv_sec = 1; timeout.tv_usec = 0;
while (some_condition)
select(n,readfds,writefds,exceptfds,&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,&timeout);
}
</PRE>
</CODE></BLOCKQUOTE>
<P>這個(gè)問題,在有些版本的Mosaic里是相當(dāng)著名的,只要一次的等待,Mosaic就掛在那里了。Mosaic的螢?zāi)挥疑辖牵遣皇怯袀€(gè)圓圓的、會(huì)旋轉(zhuǎn)的地球動(dòng)畫。那顆球轉(zhuǎn)得愈快,就表示資料從網(wǎng)路上傳送過來的速率愈慢!
<P>
<H3><A NAME="index.52"></A> <A NAME="index.51"></A> 產(chǎn)生中斷的系統(tǒng)呼叫 </H3>
<H3>特徵:</H3>
<P>當(dāng)一支程式以Ctrl-Z中止、然後再重新執(zhí)行時(shí)□或者是其它可以產(chǎn)生Ctrl-C中斷信號(hào)的情況,如子程序的終結(jié)等□系統(tǒng)就會(huì)抱怨說"interrupted system call"或是"write: unknown error",或者諸如此類的訊息。
<P>
<H3>問題點(diǎn):</H3>
<P>POSIX的系統(tǒng)檢查信號(hào)的次數(shù),比起一些舊版的Unix是要多那麼一點(diǎn)。如果是Linux,可能就會(huì)執(zhí)行signal handlers了□
<P>
<UL>
<LI> 非同步地(計(jì)時(shí)器的滴答聲)</LI>
<LI> 系統(tǒng)呼叫的傳回值</LI>
<LI> 在下列系統(tǒng)呼叫的執(zhí)行期間∶
<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>就其它的作業(yè)系統(tǒng)而言,你需要的可能就是下面這些系統(tǒng)呼叫了:
<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>在系統(tǒng)呼叫期間,若有一信號(hào)(那支程式本身應(yīng)準(zhǔn)備好handler因應(yīng)了)產(chǎn)生,handler就會(huì)被呼叫。當(dāng)handler將控制權(quán)轉(zhuǎn)移回系統(tǒng)呼叫時(shí),它會(huì)偵測(cè)出它已經(jīng)產(chǎn)生中斷,而且傳回值會(huì)立刻設(shè)定成-1,而<CODE>errno設(shè)定成EINTR</CODE>。程式并沒有想到會(huì)發(fā)生這種事,所以就掛了。
<P>有兩種修正的方法可以選擇:
<P>(1) 對(duì)每個(gè)你自行安裝的signal handler,都須在sigaction的旗號(hào)加上<CODE>SA_RESTART</CODE>。例如,把下列的程式,
<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, &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);
}
</PRE>
</CODE></BLOCKQUOTE>
<P>要注意的是,當(dāng)這部份的變更大量應(yīng)用到系統(tǒng)呼叫之後,呼叫<CODE>read()</CODE>、<CODE>write()</CODE>、<CODE>ioctl()</CODE>、 <CODE>select()</CODE>、 <CODE>pause()</CODE> 與 <CODE>connect()</CODE>時(shí),你仍然得自行檢查<CODE>EINTR</CODE>。如下所示:
<P>(2) 你自己得很明確地檢查<CODE>EINTR</CODE>:
<P>這里有兩個(gè)針對(duì)<CODE>read()</CODE>與<CODE>ioctl()</CODE>的例子。
<P>
<P>原始的程式片段,使用<CODE>read()</CODE>:
<P>
<BLOCKQUOTE><CODE>
<PRE>
int result;
while (len > 0) {
result = read(fd,buffer,len);
if (result < 0) break;
buffer += result; len -= result;
}
</PRE>
</CODE></BLOCKQUOTE>
修改成,
<BLOCKQUOTE><CODE>
<PRE>
int result;
while (len > 0) {
result = read(fd,buffer,len);
if (result < 0) { if (errno != EINTR) break; }
else { buffer += result; len -= result; }
}
</PRE>
</CODE></BLOCKQUOTE>
原始的程式片段,使用<CODE>ioctl()</CODE>:
<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) && (errno == EINTR));
</PRE>
</CODE></BLOCKQUOTE>
<P>注意一點(diǎn),有些版本的BSD Unix,其內(nèi)定的行為是重新執(zhí)行系統(tǒng)呼叫。若要讓系統(tǒng)呼叫中斷,得使用 <CODE>SV_INTERRUPT</CODE>或<CODE>SA_INTERRUPT</CODE>旗號(hào)。
<P>
<P>
<H3><A NAME="index.56"></A> <A NAME="index.55"></A> <A NAME="index.54"></A> <A NAME="index.53"></A> 可以寫入的字串 </H3>
<P> gcc對(duì)其users總懷抱著樂觀的想法,相信當(dāng)他們打算讓某個(gè)字串當(dāng)作常數(shù)來用時(shí)---那它就真的只是字串常數(shù)而已。因此,這種字串常數(shù)會(huì)儲(chǔ)存在程式碼的記憶體區(qū)段內(nèi)。這塊區(qū)域可以page到磁碟機(jī)的image上,避免耗掉swap的記憶體空間,而且任何嘗試寫入的舉動(dòng)都會(huì)造成分頁(yè)的錯(cuò)誤(segmentation fault)。這可是一種特色呢!
<P>對(duì)老舊一點(diǎn)的程式而言,這可能會(huì)產(chǎn)生一個(gè)問題。例如,呼叫<CODE>mktemp()</CODE>,傳遞的引數(shù)(arguments)是字串常數(shù)。 <CODE>mktemp()</CODE>會(huì)嘗試著在*適當(dāng)?shù)奈恢?重新寫入它的引數(shù)。
<P>修正的方法不外乎(a)以<CODE>-fwritable-strings</CODE>編譯,迫使gcc將此常數(shù)置放在資料記憶體空間內(nèi);或者(b)將侵犯地權(quán)的部份重新改寫,配置一個(gè)不為常數(shù)的字串,在呼叫前,先以strcpy()將資料拷貝進(jìn)去。
<P>
<H3><A NAME="index.57"></A> 為什麼呼叫<CODE>execl()</CODE>會(huì)失敗? </H3>
<P>那是因?yàn)槟愫艚械姆绞讲粚?duì)。<CODE>execl</CODE>的第一個(gè)引數(shù)是你想要執(zhí)行的程式名.第二個(gè)與接續(xù)的引數(shù)會(huì)變成你所呼叫的程式的<CODE>argv</CODE>陣列。記住:傳統(tǒng)上,<CODE>argv[0]</CODE>是只有當(dāng)程式?jīng)]有帶著引數(shù)執(zhí)行時(shí),才會(huì)有設(shè)定值。所以羅,你應(yīng)該這樣寫:
<P>
<BLOCKQUOTE><CODE>
<PRE>
execl("/bin/ls","ls",NULL);
</PRE>
</CODE></BLOCKQUOTE>
而不是只有,
<BLOCKQUOTE><CODE>
<PRE>
execl("/bin/ls", NULL);
</PRE>
</CODE></BLOCKQUOTE>
<P>
<P>執(zhí)行程式而不帶任何引數(shù),可解釋成是一種邀請(qǐng)函,目的是把此程式的動(dòng)態(tài)程式庫(kù)獨(dú)立的特性印出來。至少,a.out是這樣的。就ELF而言。事情就不是這樣了.
<P>
<P>(如果你想得知此程式庫(kù)的資訊,有一些更簡(jiǎn)單的介面可用;參考動(dòng)態(tài)載入那一章節(jié),或是<CODE>ldd</CODE>的manual page。)
<P>11/16/97譯
6/2/98修正
<P>
<P>
<HR>
<A HREF="GCC-HOWTO-5.html">Next</A>
<A HREF="GCC-HOWTO-3.html">Previous</A>
<A HREF="GCC-HOWTO.html#toc4">Contents</A>
</BODY>
</HTML>
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號(hào)
Ctrl + =
減小字號(hào)
Ctrl + -