?? unix
字號:
itimers的函數(shù),使用“man
setitimer”確認(rèn)你的系統(tǒng)支持),你可以用它們自己攛一<BR>
個‘usleep()’。(參見BSD源程序的‘usleep()’以便知道怎樣做)<BR><BR>
*
如果你有POSIX實時(realtime)支持,那會有一個‘nanosleep()’函數(shù)。<BR><BR>眾觀以上方法,‘select()’可能是移植性最好的(直截了當(dāng)說,它經(jīng)常比<BR>‘usleep()’或基于itimer的方法更有效)。但是,在睡眠中捕獲信號的做法會有<BR>所不同;基于不同應(yīng)用,這可以成為或不成為一個問題。<BR><BR>無論你選擇哪條路,意識到你將受到系統(tǒng)計時器分辨率的限制是很重要的(一<BR>些系統(tǒng)允許設(shè)置非常短的時間間隔,而其他的系統(tǒng)有一個分辨率,比如說10毫<BR>秒,而且總是將所有設(shè)置時間取整到那個值)。而且,關(guān)于‘sleep()’,你設(shè)置<BR>的延遲只是最小值(譯者注:實際延遲的最小值);經(jīng)過這段時間的延遲,會有<BR>一個中間時間間隔直到你的進(jìn)程重新被調(diào)度到。<BR><BR>1.4 我怎樣得到一個更細(xì)分時間單位的alarm函數(shù)版本?<BR>==================================================<BR><BR>當(dāng)今Unix系統(tǒng)傾向于使用‘setitimer()’函數(shù)實現(xiàn)鬧鐘,它比簡單的‘a(chǎn)larm()’函<BR>數(shù)具有更高的分辨率和更多的選擇項。一個使用者一般需要首先假設(shè)‘a(chǎn)larm()’<BR>和‘setitimer(ITIMER_REAL)’可能是相同的底層計時器,而且假設(shè)同時使用兩<BR>種方法會造成混亂。<BR><BR>Itimers可被用于實現(xiàn)一次性或重復(fù)信號;而且一般有3種不同的計時器可以用:<BR><BR>`ITIMER_REAL'<BR> 計數(shù)真實(掛鐘)時間,然后發(fā)送‘SIGALRM’信號<BR><BR>`ITIMER_VIRTUAL'<BR> 計數(shù)進(jìn)程虛擬(用戶中央處理器)時間,然后發(fā)送‘SIGVTALRM’信號<BR><BR>`ITIMER_PROF'<BR>
計數(shù)用戶和系統(tǒng)中央處理器時間,然后發(fā)送‘SIGPROF’信號;它供解釋器<BR>
用來進(jìn)行梗概處理(profiling)<BR><BR>然而itimers不是許多標(biāo)準(zhǔn)的一部份,盡管它自從4.2BSD就被提供。POSIX實時標(biāo)<BR>準(zhǔn)的擴(kuò)充定義了類似但不同的函數(shù)。<BR><BR>1.5
父子進(jìn)程如何通信?<BR>======================<BR><BR>一對父子進(jìn)程可以通過正常的進(jìn)程間通信的辦法(管道,套接字,消息隊列,共<BR>享內(nèi)存)進(jìn)行通信,但也可以通過利用它們作為父子進(jìn)程的相互關(guān)系而具有的一<BR>些特殊方法。<BR><BR>一個最顯然的方法是父進(jìn)程可以得到子進(jìn)程的退出狀態(tài)。<BR><BR>因為子進(jìn)程從它的父進(jìn)程繼承文件描述符,所以父進(jìn)程可以打開一個管道的兩端,<BR>然后fork,然后父進(jìn)程關(guān)閉管道這一端,子進(jìn)程關(guān)閉管道另一端。這正是你從你的<BR>進(jìn)程調(diào)用‘popen()’函數(shù)運(yùn)行另一個程序所發(fā)生的情況,也就是說你可以向<BR>‘popen()’返回的文件描述符進(jìn)行寫操作而子進(jìn)程將其當(dāng)作自己的標(biāo)準(zhǔn)輸入,或<BR>者你可以讀取這個文件描述符來看子進(jìn)程向標(biāo)準(zhǔn)輸出寫了什么。(‘popen()’函數(shù)<BR>的mode參數(shù)定義你的意圖(譯者注:mode=“r”為讀,mode=“w”為寫);如果你<BR>想讀寫都做,那么你可以并不困難地用管道自己做到)<BR><BR>而且,子進(jìn)程繼承由父進(jìn)程用mmap函數(shù)映射的匿名共享內(nèi)存段(或者通過映射特<BR>殊文件‘/dev/zero’);這些共享內(nèi)存段不能從無關(guān)的進(jìn)程訪問。<BR><BR>1.6
我怎樣去除僵死進(jìn)程?<BR>========================<BR><BR>1.6.1
何為僵死進(jìn)程?<BR>--------------------<BR><BR>當(dāng)一個程序創(chuàng)建的子進(jìn)程比父進(jìn)程提前結(jié)束,內(nèi)核仍然保存一些它的信息以便父<BR>進(jìn)程會需要它
-
比如,父進(jìn)程可能需要檢查子進(jìn)程的退出狀態(tài)。為了得到這些信<BR>息,父進(jìn)程調(diào)用‘wait()’;當(dāng)這個調(diào)用發(fā)生,內(nèi)核可以丟棄這些信息。<BR><BR>在子進(jìn)程終止后到父進(jìn)程調(diào)用‘wait()’前的時間里,子進(jìn)程被稱為‘僵死進(jìn)程’<BR>(‘zombie’)。(如果你用‘ps’,這個子進(jìn)程會有一個‘Z’出現(xiàn)在它的狀態(tài)區(qū)<BR>里指出這點(diǎn)。)即使它沒有在執(zhí)行,它仍然占據(jù)進(jìn)程表里一個位置。(它不消耗其<BR>它資源,但是有些工具程序會顯示錯誤的數(shù)字,比如中央處理器的使用;這是<BR>因為為節(jié)約空間進(jìn)程表的某些部份與會計數(shù)據(jù)(accounting
info)是共用(overlaid)的。)<BR><BR>這并不好,因為進(jìn)程表對于進(jìn)程數(shù)有固定的上限,系統(tǒng)會用光它們。即使系統(tǒng)沒<BR>有用光
,每一個用戶可以同時執(zhí)行的進(jìn)程數(shù)有限制,它總是小于系統(tǒng)的限制。<BR>順便說一下,這也正是你需要總是
檢查‘fork()’是否失敗的一個原因。<BR><BR>如果父進(jìn)程未調(diào)用wait函數(shù)而終止,子進(jìn)程將被‘init’進(jìn)程收管,它將控制子進(jìn)<BR>程退出后必須的清除工作。(‘init’是一個特殊的系統(tǒng)程序,進(jìn)程號為1
- 它實際<BR>上是系統(tǒng)啟動后運(yùn)行的第一個程序),<BR><BR>1.6.2
我怎樣避免它們的出現(xiàn)?<BR>----------------------------<BR><BR>你需要卻認(rèn)父進(jìn)程為每個子進(jìn)程的終止調(diào)用‘wait()’(或者‘waitpid()’,<BR>‘wait3()’,等等);
或者,在某些系統(tǒng)上,你可以指令系統(tǒng)你對子進(jìn)程的退出狀<BR>態(tài)沒有興趣。(譯者注:在SysV系統(tǒng)上,可以調(diào)用signal函數(shù),設(shè)置SIGCLD信號為<BR>SIG_IGN,系統(tǒng)將不產(chǎn)生僵死進(jìn)程,
詳細(xì)說明參見<<高級編程>>10.7節(jié))<BR><BR>另一種方法是*兩次*‘fork()’,而且使緊跟的子進(jìn)程直接退出,這樣造成孫子進(jìn)<BR>程變成孤兒進(jìn)程(orphaned),從而init進(jìn)程將負(fù)責(zé)清除它。欲獲得做這個的程序,參<BR>看范例章節(jié)的函數(shù)‘fork2()’。<BR><BR>為了忽略子進(jìn)程狀態(tài),你需要做下面的步驟(查詢你的系統(tǒng)手冊頁以知道這是否正<BR>常工作):<BR><BR>
struct sigaction
sa;<BR>
sa.sa_handler =
SIG_IGN;<BR> #ifdef
SA_NOCLDWAIT<BR>
sa.sa_flags =
SA_NOCLDWAIT;<BR>
#else<BR>
sa.sa_flags = 0;<BR>
#endif<BR>
sigemptyset(&sa.sa_mask);<BR>
sigaction(SIGCHLD, &sa,
NULL);<BR><BR>如果這是成功的,那么‘wait()’函數(shù)集將不再正常工作;如果它們中任何一個被<BR>調(diào)用,它們將等待直到*所有*子進(jìn)程已經(jīng)退出,然后返回失敗,并且<BR>‘errno==ECHILD’。<BR><BR>另一個技巧是捕獲SIGCHLD信號,然后使信號處理程序調(diào)用‘waitpid()’或<BR>‘wait3()’。參見范例章節(jié)的完整程序。<BR><BR>1.7
我怎樣使我的程序作為守護(hù)程序運(yùn)行?<BR>======================================<BR><BR>一個“守護(hù)程序”進(jìn)程通常被定義為一個后臺進(jìn)程,而且它不屬于任何一個終端<BR>會話,(terminal
session)。許多系統(tǒng)服務(wù)由守護(hù)程序?qū)嵤蝗缇W(wǎng)絡(luò)服務(wù),打印等。<BR><BR>簡單地在后臺啟動一個程序并非足夠是這些長時間運(yùn)行的程序;那種方法沒有正<BR>確地將進(jìn)程從啟動它的終端脫離(detach)。而且,啟動守護(hù)程序的普遍接受的的方<BR>法是簡單地手工執(zhí)行或從rc腳本程序執(zhí)行(譯者注:rc:runcom);并希望這個守護(hù)<BR>程序?qū)⑵?自身*安置到后臺。<BR><BR>這里是成為守護(hù)程序的步驟:<BR><BR> 1.
調(diào)用‘fork()’以便父進(jìn)程可以退出,這樣就將控制權(quán)歸還給運(yùn)行你程序的<BR>
命令行或shell程序。需要這一步以便保證新進(jìn)程不是一個進(jìn)程組頭領(lǐng)進(jìn)程(process<BR>
group
leader)。下一步,‘setsid()’,會因為你是進(jìn)程組頭領(lǐng)進(jìn)程而失敗。<BR><BR> 2.
調(diào)用‘setsid()’
以便成為一個進(jìn)程組和會話組的頭領(lǐng)進(jìn)程。由于一個控制終端<BR>
與一個會話相關(guān)聯(lián),而且這個新會話還沒有獲得一個控制終端,我們的進(jìn)程沒<BR>
有控制終端,這對于守護(hù)程序來說是一件好事。<BR><BR> 3.
再次調(diào)用‘fork()’所以父進(jìn)程(會話組頭領(lǐng)進(jìn)程)可以退出。這意味著我們,一<BR>
個非會話組頭領(lǐng)進(jìn)程永遠(yuǎn)不能重新獲得控制終端。<BR><BR> 4.
調(diào)用‘chdir("/")’確認(rèn)我們的進(jìn)程不保持任何目錄于使用狀態(tài)。不做這個會導(dǎo)<BR>
致系統(tǒng)管理員不能卸裝(umount)一個文件系統(tǒng),因為它是我們的當(dāng)前工作目錄。<BR><BR>
[類似的,我們可以改變當(dāng)前目錄至對于守護(hù)程序運(yùn)行重要的文件所在目錄]<BR><BR> 5.
調(diào)用‘umask(0)’以便我們擁有對于我們寫的任何東西的完全控制。我們不知<BR>
道我們繼承了什么樣的umask。<BR><BR>
[這一步是可選的](譯者注:這里指步驟5,因為守護(hù)程序不一定需要寫文件)<BR><BR> 6.
調(diào)用‘close()’關(guān)閉文件描述符0,1和2。這樣我們釋放了從父進(jìn)程繼承的標(biāo)<BR>
準(zhǔn)輸入,標(biāo)準(zhǔn)輸出,和標(biāo)準(zhǔn)錯誤輸出。我們沒辦法知道這些文描述符符可能<BR>
已經(jīng)被重定向去哪里。注意到許多守護(hù)程序使用‘sysconf()’來確認(rèn)<BR>
‘_SC_OPEN_MAX’的限制。‘_SC_OPEN_MAX’告訴你每個進(jìn)程能夠打<BR>
開的最多文件數(shù)。然后使用一個循環(huán),守護(hù)程序可以關(guān)閉所有可能的文件描<BR>
述符。你必須決定你需要做這個或不做。如果你認(rèn)為有可能有打開的文件描<BR>
述符,你需要關(guān)閉它們,因為系統(tǒng)有一個同時打開文件數(shù)的限制。<BR><BR> 7.
為標(biāo)準(zhǔn)輸入,標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)錯誤輸出建立新的文件描述符。即使你不打算<BR>
使用它們,打開著它們不失為一個好主意。準(zhǔn)確操作這些描述符是基于各自<BR>
愛好;比如說,如果你有一個日志文件,你可能希望把它作為標(biāo)準(zhǔn)輸出和標(biāo)<BR>
準(zhǔn)錯誤輸出打開,而把‘/dev/null’作為標(biāo)準(zhǔn)輸入打開;作為替代方法,你可<BR>
以將‘/dev/console’作為標(biāo)準(zhǔn)錯誤輸出和/或標(biāo)準(zhǔn)輸出打開,而‘/dev/null’作<BR>
為標(biāo)準(zhǔn)輸入,或者任何其它對你的守護(hù)程序有意義的結(jié)合方法。(譯者注:一<BR>
般使用dup2函數(shù)原子化關(guān)閉和復(fù)制文件描述符,參見<<高級編程>>3.12節(jié))<BR><BR>如果你的守護(hù)程序是被‘inetd’啟動的,幾乎所有這些步驟都不需要(或不建議<BR>采用)。在那種情況下,標(biāo)準(zhǔn)輸入,標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)錯誤輸出都為你指定為網(wǎng)絡(luò)<BR>連接,而且‘fork()’的調(diào)用和會話的操縱不應(yīng)做(以免使‘inetd’造成混亂)。只<BR>有‘chdir()’和‘umask()’這兩步保持有用。<BR><BR>1.8 我怎樣象ps程序一樣審視系統(tǒng)的進(jìn)程?<BR>=======================================<BR><BR>你真的不該想做這個。<BR><BR>到目前為止,移植性最好的是調(diào)用‘popen(pscmd,"r")’并處理它的輸出。(pscmd<BR>應(yīng)當(dāng)是類似SysV系統(tǒng)上的‘“ps
-ef”’,BSD系統(tǒng)有很多可能的顯示選項:選<BR>擇一個。)<BR><BR>在范例章節(jié)有這個問題的兩個完整解決方法;一個適用于SunOS
4,它需要root權(quán)<BR>限執(zhí)行并使用‘kvm_*’例程從內(nèi)核數(shù)據(jù)結(jié)果讀取信息;另一種適用于SVR4系統(tǒng)<BR>(包括Sun
OS
5),它使用‘/proc’文件系統(tǒng)。<BR><BR>在具有SVR4.2風(fēng)格‘/proc’的系統(tǒng)上更簡單;只要對于每一個感興趣的進(jìn)程號從<BR>文件‘/proc/進(jìn)程號/psinfo’讀取一個psinfo_t結(jié)構(gòu)。但是,這種可能是最清晰的方<BR>法也許又是最不得到很好支持的方法。(在FreeBSD的‘/proc’上,你從<BR>‘/proc/進(jìn)程號/status’讀取一個半未提供文檔說明(semi-undocumented)的可打印字<BR>符串;Linux有一些與其類似的東西)<BR><BR>1.9 給定一個進(jìn)程號,我怎樣知道它是個正在運(yùn)行的程序?<BR>=====================================================<BR><BR>使用‘kill()’函數(shù),而已0作為信號代碼(signal
number)。<BR><BR>從這個函數(shù)返回有四種可能的結(jié)果:<BR><BR>
*
‘kill()’返回0<BR><BR>
-
這意味著一個給定此進(jìn)程號的進(jìn)程退出,系統(tǒng)允許你向它發(fā)送信號。該進(jìn)<BR>
程是否可以是僵死進(jìn)程與不同系統(tǒng)有關(guān)。<BR><BR> *
‘kill()’返回-1,‘errno ==
ESRCH’<BR><BR>
-
要么不存在給定進(jìn)程號的進(jìn)程,要么增強(qiáng)的安全機(jī)制導(dǎo)致系統(tǒng)否認(rèn)它的存<BR>
在。(在一些系統(tǒng)上,這個進(jìn)程有可能是僵死進(jìn)程。)<BR><BR> *
‘kill()’返回-1,‘errno ==
EPERM’<BR><BR>
-
系統(tǒng)不允許你殺死(kill)這個特定進(jìn)程。這意味著要么進(jìn)程存在(它又可能是<BR>
僵死進(jìn)程),要么嚴(yán)格的增強(qiáng)安全機(jī)制起作用(比如你的進(jìn)程不允許發(fā)送信號<BR>
給*任何人*)。<BR><BR> *
‘kill()’返回-1,伴以其它‘errno’值<BR><BR>
-
你有麻煩了!<BR><BR>用的最多的技巧是認(rèn)為調(diào)用“成功”或伴以‘EPERM’的“失敗”意味著進(jìn)程存<BR>在,而其它錯誤意味著它不存在。<BR><BR>如果你特別為提供‘/proc’文件系統(tǒng)的系統(tǒng)(或所有類似系統(tǒng))寫程序,一個替換<BR>方法存在:檢查‘proc/進(jìn)程號’是否存在是可行的。<BR><BR>1.10 system函數(shù),pclose函數(shù),waitpid函數(shù)
的返回值是什么?<BR>==========================================================<BR><BR>
‘system()’,‘pclose()’或者‘waitpid()’的返回值不象是我進(jìn)程的退出值(exit<BR> value)(譯者注:退出值指調(diào)用exit()
或_exit()時給的參數(shù))...
或者退出值左移了8<BR>
位...這是怎么搞的?<BR><BR>手冊頁是對的,你也是對的!
如果查閱手冊頁的‘waitpid()’你會發(fā)現(xiàn)進(jìn)程的返回<BR>值被編碼了。正常情況下,進(jìn)程的返回值在高16位,而余下的位用來作其它事。<BR>如果你希望可移植,你就不能憑借這個,而建議是你該使用提供的宏。這些宏總<BR>是在‘wait()’或‘wstat’的文檔中說明了。<BR><BR>為了不同目的定義的宏(在‘<sys/wait.h>’)包括(stat是‘waitpid()’返回的值):<BR><BR>`WIFEXITED(stat)'<BR>
如果子進(jìn)程正常退出則返回非0<BR><BR>`WEXITSTATUS(stat)'<BR>
子進(jìn)程返回的退出碼<BR><BR>`WIFSIGNALED(stat)'<BR>
如果子進(jìn)程由與信號而
終止則返回非0<BR><BR>`WTERMSIG(stat)'<BR>
終止子進(jìn)程的信號代碼<BR><BR>`WIFSTOPPED(stat)'<BR>
如果子進(jìn)程暫停(stopped)則返回非0<BR><BR>`WSTOPSIG(stat)'<BR>
使子進(jìn)程暫停的信號代碼<BR><BR>`WIFCONTINUED(stat)'<BR>
如果狀態(tài)是表示子進(jìn)程繼續(xù)執(zhí)行則返回非0<BR><BR>`WCOREDUMP(stat)'<BR>
如果‘WIFSIGNALED(stat)’為非0,而如果這個進(jìn)程產(chǎn)生一個內(nèi)存映射文件<BR>
(core dump)則返回非0<BR><BR>1.11
我怎樣找出一個進(jìn)程的存儲器使用情況?<BR>=========================================<BR><BR>如果提供的話,參看‘getrusage()’手冊頁<BR><BR>1.12
為什么進(jìn)程的大小不縮減?<BR>=============================<BR><BR>當(dāng)你使用‘free()’函數(shù)釋放內(nèi)存給堆時,幾乎所有的系統(tǒng)都*不*減少你程序的<BR>對內(nèi)存的使用。被‘free()’釋放的內(nèi)存仍然屬于進(jìn)程地址空間的一部份,并將<BR>被將來的‘malloc()’請求所重復(fù)使用。<BR><BR>如果你真的需要釋放內(nèi)存給系統(tǒng),參看使用‘mmap()’分配私有匿名內(nèi)存映射<BR>(private
anonymous
mappings)。當(dāng)這些內(nèi)存映射被取消映射時,內(nèi)存真的將其釋放給<BR>系統(tǒng)。某些‘malloc()’的實現(xiàn)方法(比如在GNU
C庫中)在允許時自動使用‘mmap()’<BR>實施大容量分配;這些內(nèi)存塊(blocks)隨著‘free()’被釋放回系統(tǒng)。<BR><BR>當(dāng)然,如果你的程序的大小增加而你認(rèn)為它不應(yīng)該這樣,你可能有一個‘內(nèi)存泄<BR>露’(‘memory
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -