【文章內(nèi)容簡介】
程結(jié)束時內(nèi)核就寫一個會計記錄。典型的會計記錄是 3 2字節(jié)長的二進(jìn)制數(shù)據(jù),包括命令名、所使用的 C P U時間總量、用戶 I D和組 I D、起動時間等。 ?記帳記錄所需的各個數(shù)據(jù)都由內(nèi)核保存在進(jìn)程表中,并在一個新進(jìn)程被創(chuàng)建時置初值 (例如 fork之后在子進(jìn)程中 )。進(jìn)程終止時寫一個會計記錄。這就意味著在記帳文件中記錄的順序?qū)?yīng)于進(jìn)程終止的順序,而不是它們起動的順序。為了確定起動順序,需要讀全部記帳文件,并按起動日歷時間進(jìn)行排序。這不是一種很完善的方法,因?yàn)槿諝v時間的單位是秒,在一個給定的秒中可能起動了多個進(jìn)程。 ?記帳記錄對應(yīng)于進(jìn)程而不是程序。在 f o r k之后,內(nèi)核為子進(jìn)程初始化一個記錄,而不是在一個新程序被執(zhí)行時。雖然 e x e c并不創(chuàng)建一個新的記帳記錄,但相應(yīng)記錄中的命令名改變了, A F O R K標(biāo)志則被清除。這意味著,如果一個進(jìn)程順序執(zhí)行了三個程序 (A exec B,B exec C,最后 C exit),但只寫一個會計記錄。在該記錄中的命令名對應(yīng)于程序 C,但 C P U時間是程序 A、 B、 C之和。 include int acct(const char *filename)。 進(jìn)程執(zhí)行時間 用戶進(jìn)程可以通過調(diào)用 t i m e s函數(shù)獲得它自己及終止子進(jìn)程的時間值 。 此函數(shù)填寫由 buf指向的 tms結(jié)構(gòu),該結(jié)構(gòu)定義如下: struct tms { clock_t tms_utime。 /* user time */ clock_t tms_stime。 /* system time */ clock_t tms_cutime。 /* user time of children */ clock_t tms_cstime。 /* system time of children*/ }。 ?結(jié)構(gòu)中兩個針對子進(jìn)程的字段包含了此進(jìn)程已等待到的各子進(jìn)程的值 ?由此函數(shù)返回的 clock_t值都用 _SC_CLK_TCK (由 sysconf函數(shù)返回的每秒時鐘滴答數(shù))變換成秒數(shù)。 include sys/ clock_t times(struct tms *buf)。 小結(jié) 對在 LINUX環(huán)境中的高級程序設(shè)計而言,完整地了解 LINUX的進(jìn)程控制非常重要。 其中必須熟練掌握的只有幾個 —— fork、 exec族、 _exit、wait和 waitpid。通過對這些接口的理解,我們應(yīng)該了解LINUX下新 進(jìn)程是怎么產(chǎn)生的?不同的程序又是如何執(zhí)行的?以及進(jìn)程控制的一些基本內(nèi)容。 對各種不同的用戶 I D和組 I D (實(shí)際,有效和保存的 )的理解和編寫安全的設(shè)置 用戶 I D程序是至關(guān)重要的。 本章的最后,我們還介紹了 LINUX下的幾個輔助系統(tǒng)調(diào)用。 第六章 LINUX下的信號 信號的本質(zhì) ?信號是在軟件層次上是對中斷機(jī)制的一種模擬,在原理上,一個進(jìn)程收到一個信號與處理器收到一個中斷請求可以說是一樣的。 ?信號是異步的,一個進(jìn)程不必通過任何操作來等待信號的到達(dá),事實(shí)上,進(jìn)程也不知道信號到底什么時候到達(dá)。 ?信號是進(jìn)程間通信機(jī)制中唯一的異步通信機(jī)制,可以看作是異步通知,通知接收信號的進(jìn)程有哪些事情發(fā)生了。 ?信號機(jī)制經(jīng)過 POSIX實(shí)時擴(kuò)展后,功能更加強(qiáng)大,除了基本通知功能外,還可以傳遞附加信息。 信號的來源 信號事件的發(fā)生有兩個來源: ?硬件來源 ?硬件中斷觸發(fā),如鍵盤 ?其它硬件故障,如 IO BUS error ?硬件觸發(fā)的程序異常 ?軟件來源 ?程序或命令控制的信號,如: kill, raise等 ?系統(tǒng)鬧鐘 ?程序調(diào)試 ?程序異常,如:段違例、非法運(yùn)算等操作 ?… 信號的種類 可以從兩個不同的分類角度對信號進(jìn)行分類: 1) 可靠性方面:可靠信號與不可靠信號; 2) 與時間的關(guān)系上:實(shí)時信號與非實(shí)時信號。 不可靠信號 Linux信號機(jī)制基本上是從 Unix系統(tǒng)中繼承過來的。早期Unix系統(tǒng)中的信號機(jī)制比較簡單和原始,后來在實(shí)踐中暴露出一些問題,因此,把那些建立在早期機(jī)制上的信號叫做“ 不可靠信號 ” ,信號值小于 SIGRTMIN(Red hat LINUX中,SIGRTMIN=32, SIGRTMAX=63)的信號都是不可靠信號。這就是 “ 不可靠信號 ” 的來源。它的主要問題是: ?進(jìn)程每次處理信號后,就將對信號的響應(yīng)設(shè)置為默認(rèn)動作。在某些情況下,將導(dǎo)致對信號的錯誤處理;因此,用戶如果不希望這樣的操作,那么就要在信號處理函數(shù)結(jié)尾再一次調(diào)用 signal(),重新安裝該信號。 ?信號可能丟失。早期 unix下的不可靠信號主要指的是進(jìn)程可能對信號做出錯誤的反應(yīng)以及信號可能丟失。這種情況主要出現(xiàn)在:當(dāng)信號處理函數(shù)執(zhí)行過程中到來的所有相同信號,都被合并為一個信號。 不可靠信號示例 說明: 這段代碼段的一個問題是:在信號發(fā)生之后到信號處理程序中調(diào)用signal函數(shù)之間有一個時間窗口 。 在此段時間中 , 可能發(fā)生另一次中斷信號 。 第二個信號會造成執(zhí)行默認(rèn)動作 , 而對中斷信號則是終止該進(jìn)程 。這種類型的程序段在大多數(shù)情況下會正常工作 , 使得我們認(rèn)為它們正確 ,而實(shí)際上卻并不是如此 。 Linux支持不可靠信號,但是對不可靠信號機(jī)制做了改進(jìn):在調(diào)用完信號處理函數(shù)后,不必重新調(diào)用該信號的安裝函數(shù)(信號安裝函數(shù)是在可靠機(jī)制上的實(shí)現(xiàn))。因此, Linux下的不可靠信號問題主要指的是信號可能丟失。 可靠信號 ?隨著時間的發(fā)展,實(shí)踐證明了有必要對信號的原始機(jī)制加以改進(jìn)和擴(kuò)充。所以,后來出現(xiàn)的各種 Unix版本分別在這方面進(jìn)行了研究,力圖實(shí)現(xiàn) “ 可靠信號 ” 。由于原來定義的信號已有許多應(yīng)用,不好再做改動,最終只好又新增加了一些信號,并在一開始就把它們定義為可靠信號,這些信號支持排隊,不會丟失。 ?信號的發(fā)送和安裝也出現(xiàn)了新版本:信號發(fā)送函數(shù) sigqueue()及信號安裝函數(shù) sigaction()。 POSIX對可靠信號機(jī)制做了標(biāo)準(zhǔn)化。但是, POSIX只對可靠信號機(jī)制應(yīng)具有的功能以及信號機(jī)制的對外接口做了標(biāo)準(zhǔn)化,對信號機(jī)制的實(shí)現(xiàn)沒有作具體的規(guī)定。 ?信號值位于 SIGRTMIN和 SIGRTMAX之間的信號都是可靠信號,可靠信號克服了信號可能丟失的問題。 Linux在支持新版本的信號安裝函數(shù) sigation()以及信號發(fā)送函數(shù) sigqueue()的同時,仍然支持早期的 signal()信號安裝函數(shù)和 kill()函數(shù)。 一點(diǎn)說明 ?可靠信號是指后來添加的新信號(信號值位于 SIGRTMIN及 SIGRTMAX之間);不可靠信號是信號值小于SIGRTMIN的信號。信號的可靠與不可靠只與信號值有關(guān),與信號的發(fā)送及安裝函數(shù)無關(guān)。 ?目前 linux中的 signal()是通過 sigation()函數(shù)實(shí)現(xiàn)的,因此,即使通過 signal()安裝的信號,在信號處理函數(shù)的結(jié)尾也不必再調(diào)用一次信號安裝函數(shù)。同時,由 signal()安裝的實(shí)時信號支持排隊,同樣不會丟失。 ?對于目前 linux的兩個信號安裝函數(shù) :signal()及 sigaction()來說,它們都不能把 SIGRTMIN以前的信號變成可靠信號(都不支持排隊,仍有可能丟失,仍然是不可靠信號),而且對SIGRTMIN以后的信號都支持排隊。 這兩個函數(shù)的最大區(qū)別在于, 經(jīng)過 sigaction安裝的信號都能傳遞信息 給信號處理函數(shù)(對所有信號這一點(diǎn)都成立),而經(jīng)過 signal安裝的信號卻不能向信號處理函數(shù)傳遞信息 ,對于信號發(fā)送函數(shù)來說也是一樣的。 實(shí)時信號和非實(shí)時信號 ?LINUX中信號的編號為 063,將來可能進(jìn)一步增加,這需要得到內(nèi)核的支持。 ?前 32種信號已經(jīng)有了預(yù)定義值,每個信號有了確定的用途及含義,并且每種信號都有各自的缺省動作。 ?后 32個信號 (SIGRTMIN=31, SIGRTMAX=63)表示實(shí)時信號,等同于前面闡述的可靠信號。這保證了發(fā)送的多個實(shí)時信號都被接收。實(shí)時信號是 POSIX標(biāo)準(zhǔn)的一部分,可用于應(yīng)用進(jìn)程。 ?非實(shí)時信號都不支持排隊,都是不可靠信號;實(shí)時信號都支持排隊,都是可靠信號。 進(jìn)程對信號的響應(yīng) 進(jìn)程可以通過三種方式來響應(yīng)一個信號: 1) 忽略信號 (SIG_DFL),即對信號不做任何處理,其中,有兩個信號不能忽略: SIGKILL及 SIGSTOP; 2) 捕捉信號 (CATCH)。定義信號處理函數(shù),當(dāng)信號發(fā)生時,執(zhí)行相應(yīng)的處理函數(shù); 3) 執(zhí)行缺省操作 (SIG_IGN), Linux對每種信號都規(guī)定了默認(rèn)操作,詳細(xì)情況請參考下表。注意,進(jìn)程對實(shí)時信號的缺省反應(yīng)是進(jìn)程終止。 Linux究竟采用上述三種方式的哪一個來響應(yīng)信號,取決于傳遞給相應(yīng) API函數(shù)的參數(shù)。 關(guān)于上表的說明 在系統(tǒng)默認(rèn)動作列 , “ 終止 w / c o r e”表示在進(jìn)程當(dāng)前工作目錄的產(chǎn)生了 c o r e文件 , 該文件中記錄了該進(jìn)程非正常退出時的存儲圖像 , 大多數(shù) U N I X調(diào)試程序都使用 c o r e文件以檢查進(jìn)程在終止時的狀態(tài) 。 在下列條件下不產(chǎn)生 c o r e文件: ( a )進(jìn)程是設(shè)置 用戶 ID, 而且當(dāng)前用戶并非程序文件的所有者 ( b )進(jìn)程是設(shè)置 組 ID, 而且當(dāng)前用戶并非該程序文件的組所有者 ( c )用戶沒有寫當(dāng)前工作目錄的許可權(quán) ( d )文件太大 。 core文件的許可權(quán) (假定該文件在此之前并不存在 )通常是用戶讀/寫,組讀和其他讀。 LINUX信號列表 (按序號排列 ) ?SIGHUP:如果終端界面檢測到一個連接斷開,則將此信號送給與該終端相關(guān)的控制進(jìn)程(對話期首進(jìn)程)。 ?SIGINT:當(dāng)用戶按中斷鍵(一般采用 DELETE或 CtrlC)時,終端驅(qū)動程序產(chǎn)生此信號并送至前臺進(jìn)程組中的每一個進(jìn)程。當(dāng)一個進(jìn)程在運(yùn)行時失控,特別是它正在屏幕上產(chǎn) 生大量不需要的輸出時,常用此信號終止它。 ?SIGQUIT:當(dāng)用戶在終端上按退出鍵(一般采用 Ctrl\)時,產(chǎn)生此信號,并送至前臺進(jìn)程組中的所有進(jìn)程。此信號不僅終止前臺進(jìn)程組(如 SIGINT所做的那樣),同時產(chǎn)生一個 core文件 ?SIGILL:此信號指示進(jìn)程已執(zhí)行一條非法硬件指令 ?SIGTRAP:指示一個實(shí)現(xiàn)定義的硬件故障,一般用于跟蹤調(diào)試自陷 ?SIGABRT:調(diào)用 abort函數(shù)時產(chǎn)生此信號。進(jìn)程異常終止 ?SIGIOT: IOT自陷,指示一個實(shí)現(xiàn)定義的硬件故障 ?SIGBUS:總線錯,指示一個實(shí)現(xiàn)定義的硬件故障 ?SIGFPE:浮點(diǎn)相關(guān)異常 ?SIGKILL:這是兩個不能被捕捉或忽略信號中的一個。它向系統(tǒng)管理員提供了一種可以殺死任一進(jìn)程的可靠方法。 ?SIGUSR1:用戶自定義信號 LINUX信號列表 (續(xù) ) ?SIGSEGV:存儲訪問相關(guān)的異常,一般為非法存儲空間讀寫引起 ?SIGUSR2:用戶自定義信號 ?SIGPIPE:如果在讀進(jìn)程已終止時寫管道,則產(chǎn)生此信號。當(dāng)套接口的一端已經(jīng)終止時,若進(jìn)程寫該套接口也產(chǎn)生此信號 ?SIGALRM: 用 alarm函數(shù)設(shè)置的時間到達(dá)時產(chǎn)生此信號。若由 s e t i t i m e r ( 2 )函數(shù)設(shè)置的間隔時間已經(jīng)過時,那么也產(chǎn)生此信號 ?SIGTERM:終端信號, 是由 kill(1)命令 發(fā)送的系統(tǒng)默認(rèn)終止信號 ?SIGSTKFLT:棧故障相關(guān)信號 ?SIGCHLD:在一個進(jìn)程終止或停止 時, SIGCHLD信號被送給其父進(jìn)程。按系統(tǒng)默認(rèn),將忽略此信號。如果父進(jìn)程希望了解其子進(jìn)程的這種狀態(tài)改變,則應(yīng)捕捉此信號。信號捕捉函數(shù)中通常要調(diào)用 wait函數(shù)以取得子進(jìn)程 ID和其終止?fàn)顟B(tài)。 ?SIGCONT:跟蹤調(diào)試時的繼續(xù)信號 ?SIGSTOP: 是一個作業(yè)控制信號,它停止一個進(jìn)程, SIGSTOP不能被捕捉或忽略 ? SIGTSTP:交互停止信號,當(dāng)用戶在終端上按掛起鍵(一般采用 CtrlZ)時,終端驅(qū)動程序產(chǎn)生此信號。 LINUX信號列表 (續(xù) ) ?SIGTTIN:當(dāng)一個后臺進(jìn)程組進(jìn)程試圖讀其控制終端時,終端驅(qū)動程序產(chǎn)生此信號。在下列例外情形下不產(chǎn)生此信號,此時讀操作返回出錯, errno設(shè)置為 EIO: (a)讀進(jìn)程忽略或阻塞此信號,或 (b)讀進(jìn)程所屬的進(jìn)程組是孤兒進(jìn)程組。 ?SIGTTOU:當(dāng)一個后臺進(jìn)程組進(jìn)程試圖寫其控制終端時產(chǎn)生此信號。與上面所述的 SIGTTIN信號不同,一個進(jìn)程可以選擇為允許后臺進(jìn)程寫控制終端。如果不允許后臺進(jìn)程寫,則與 SIGTTIN相似也有兩種特殊情況: ( a )寫進(jìn)程忽略或阻塞此信 不幸的是,術(shù)語停止 ( s t o p )有不同的意義。在討論作業(yè)控制和信號時我們需提及停止和繼續(xù)作業(yè)。但是終端驅(qū)動程序一直用術(shù)語停止表示用 C t r l S和 C t r l Q字符停止和起動終端輸出。因此,終端驅(qū)動程序?qū)a(chǎn)生交互停止信號和字符稱之為掛起字符而非停止字符。號,或 ( b