?? 回復the linux gcc howto中譯版 (轉--縮水版)。.txt
字號:
作者:L772
email: L772@263.net
日期:8/28/2001 1:51:24 PM
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。
要注意的是,盡管你用了-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()會嘗試著在*適當的位置*重新寫入它的
引數。
修正的方法
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -