作業系統

當前位置 /首頁/計算機/作業系統/列表

關於Linux系統命令中exit與exit的區別

注:exit()就是退出,傳入的引數是程式退出時的狀態碼,0表示正常退出,其他表示非正常退出,一般都用-1或者1,標準C裡有EXIT_SUCCESS和EXIT_FAILURE兩個巨集,用exit(EXIT_SUCCESS);可讀性比較好一點。

關於Linux系統命令中exit與exit的區別

作為系統呼叫而言,_exit和exit是一對孿生兄弟,它們究竟相似到什麼程度,我們可以從Linux的原始碼中找到答案:

#define__NR__exit__NR_exit/*摘自檔案include/asm-i386/unistd.h第334行*/

"__NR_"是在Linux的原始碼中為每個系統呼叫加上的字首,請注意第一個exit前有2條下劃線,第二個exit前只有1條下劃線。 這時隨便一個懂得C語言並且頭腦清醒的人都會說,_exit和exit沒有任何區別,但我們還要講一下這兩者之間的區別,這種區別主要體現在它們在函式庫中的定義。_exit在Linux函式庫中的原型是:

#includevoid_exit(intstatus);

和exit比較一下,exit()函式定義在stdlib.h中,而_exit()定義在unistd.h中,從名字上看,stdlib.h似乎比unistd.h高階一點,那麼,它們之間到底有什麼區別呢? _exit()函式的作用最為簡單:直接使程序停止執行,清除其使用的記憶體空間,並銷燬其在核心中的各種資料結構;exit()函式則在這些基礎上作了一些包裝,在執行退出之前加了若干道工序,也是因為這個原因,有些人認為exit已經不能算是純粹的系統呼叫。 exit()函式與_exit()函式最大的區別就在於exit()函式在呼叫exit系統呼叫之前要檢查檔案的開啟情況,把檔案緩衝區中的內容寫回檔案,就是"清理I/O緩衝"。

exit()在結束呼叫它的程序之前,要進行如下步驟:

1.呼叫atexit()註冊的函式(出口函式);按ATEXIT註冊時相反的順序呼叫所有由它註冊的函式,這使得我們可以指定在程式終止時執行自己的清理動作.例如,儲存程式狀態資訊於某個檔案,解開對共享資料庫上的鎖等.

nup();關閉所有開啟的流,這將導致寫所有被緩衝的輸出,刪除用TMPFILE函式建立的所有臨時檔案.

3.最後呼叫_exit()函式終止程序。

_exit做3件事(man): 1,Anyopenfiledescriptorsbelongingtotheprocessareclosed 2,anychildrenoftheprocessareinheritedbyprocess1,init 3,theprocess‘sparentissentaSIGCHLDsignal

exit執行完清理工作後就呼叫_exit來終止程序。

此外,另外一種解釋:

簡單的說,exit函式將終止呼叫程序。在退出程式之前,所有檔案關閉,緩衝輸出內容將重新整理定義,並呼叫所有已重新整理的“出口函式”(由atexit定義)。

_exit:該函式是由Posix定義的,不會執行exithandler和signalhandler,在UNIX系統中不會flush標準I/O流。

簡單的說,_exit終止呼叫程序,但不關閉檔案,不清除輸出快取,也不調用出口函式。

共同:

不管程序是如何終止的,核心都會關閉程序開啟的所有filedescriptors,釋放程序使用的memory!

更詳細的介紹:

Callingexit() Theexit()functioncausesnormalprogramtermination.

Theexit()functionperformsthefollowingfunctions:

unctionsregisteredbytheStandardCatexit()functionarecalledinthereverse yofthesefunctionscallsexit(),theresultsarenotportable. penoutputstreamsareflushed(datawrittenout)andthestreamsareclosed.

ilescreatedbytmpfile()ared.

_exit()functioniscalled. Calling_exit() The_exit()functionperformsoperatingsystem-specificprogramterminationfunctions. Theseinclude: penfiledescriptorsanddirectorystreamsareclosed.

eparentprocessisexecutingawait()orwaitpid(),theparentwakesupand statusismadeavailable.

eparentisnotexecutingawait()orwaitpid(),thestatusissavedforreturnto theparentonasubsequentwait()orwaitpid(). :the terminationofaparentdoesnotdirectlyterminateitschildren. eimplementationsupportstheSIGCHLDsignal,aSIGCHLDissenttotheparent. raljobcontrolsignalsaresent.

為何在一個fork的子程序分支中使用_exit函式而不使用exit函式? ‘exit()’與‘_exit()’有不少區別在使用‘fork()’,特別是‘vfork()’時變得很 突出。

‘exit()’與‘_exit()’的基本區別在於前一個呼叫實施與呼叫庫裡使用者狀態結構(user-modeconstructs)有關的清除工作(clean-up),而且呼叫使用者自定義的清除程式(自定義清除程式由atexit函式定義,可定義多次,並以倒序執行),相對應,_exit函式只為程序實施核心清除工作。 在由‘fork()’建立的`子程序分支裡,正常情況下使用‘exit()’是不正確的,這是因為使用它會導致標準輸入輸出(stdio:StandardInputOutput)的緩衝區被清空兩次,而且臨時檔案被出乎意料的刪除(臨時檔案由tmpfile函式建立在系統臨時目錄下,檔名由系統隨機生成)。在C++程式中情況會更糟,因為靜態目標(staticobjects)的解構函式(destructors)可以被錯誤地執行。(還有一些特殊情況,比如守護程式,它們的父程序需要呼叫‘_exit()’而不是子程序;適用於絕大多數情況的基本規則是,‘exit()’在每一次進入‘main’函式後只調用一次。) 在由‘vfork()’建立的子程序分支裡,‘exit()’的使用將更加危險,因為它將影響父程序的狀態。

#include; #includeintglob=6;/*externalvariableininitializeddata*/ int main(void) { intvar;/*automaticvariableonthestack*/ pid_tpid; var=88; printf("beforevforkn";/*wedon‘tflushstdio*/ if((pid=vfork())<0) printf("vforkerrorn"; elseif(pid==0){/*child*/ glob++;/*modifyparent‘svariables*/ var++; exit(0);/*childterminates*///子程序中最好還是用_exit(0)比較安全。 } /*parent*/ printf("pid=%d,glob=%d,var=%dn",getpid(),glob,var); exit(0); } 在Linux系統上執行,父程序printf的內容輸出:pid=29650,glob=7,var=89

子程序關閉的是自己的,雖然他們共享標準輸入、標準輸出、標準出錯等“開啟的檔案”,子程序exit時,也不過是遞減一個引用計數,不可能關閉父程序的,所以父程序還是有輸出的。

但在其它UNIX系統上,父程序可能沒有輸出,原因是子程序呼叫了exit,它重新整理關閉了所有標準I/O流,這包括標準輸出。雖然這是由子程序執行的,但卻是在父程序的地址空間中進行的,所以所有受到影響的標準I/OFILE物件都是在父程序中的。當父程序呼叫printf時,標準輸出已被關閉了,於是printf返回-1。

在Linux的標準函式庫中,有一套稱作"高階I/O"的函式,我們熟知的printf()、fopen()、fread()、fwrite()都在此列,它們也被稱作"緩衝I/O(bufferedI/O)",其特徵是對應每一個開啟的檔案,在記憶體中都有一片緩衝區,每次讀檔案時,會多讀出若干條記錄,這樣下次讀檔案時就可以直接從記憶體的緩衝區中讀取,每次寫檔案的時候,也僅僅是寫入記憶體中的緩衝區,等滿足了一定的條件(達到一定數量,或遇到特定字元,如換行符和檔案結束符EOF),再將緩衝區中的內容一次性寫入檔案,這樣就大大增加了檔案讀寫的速度,但也為我們程式設計帶來了一點點麻煩。如果有一些資料,我們認為已經寫入了檔案,實際上因為沒有滿足特定的條件,它們還只是儲存在緩衝區內,這時我們用_exit()函式直接將程序關閉,緩衝區中的資料就會丟失,反之,如果想保證資料的完整性,就一定要使用exit()函式。

Exit的函式宣告在stdlib.h標頭檔案中。

_exit的函式宣告在unistd.h標頭檔案當中。

下面的例項比較了這兩個函式的區別。printf函式就是使用緩衝I/O的方式,該函式在遇到“n”換行符時自動的從緩衝區中將記錄讀出。例項就是利用這個性質進行比較的。

exit.c原始碼

#include#includeintmain(void) { printf("Usingexit...n"); printf("Thisisthecontentinbuffer"); exit(0); }

輸出資訊:

Usingexit...

Thisisthecontentinbuffer

#include#includeintmain(void) { printf("Usingexit...n");//如果此處不加“n”的話,這條資訊有可能也不會顯示在終端上。 printf("Thisisthecontentinbuffer"); _exit(0); }

則只輸出:

Usingexit...

說明:在一個程序呼叫了exit之後,該程序並不會馬上完全消失,而是留下一個稱為殭屍程序(Zombie)的資料結構。殭屍程序是一種非常特殊的程序,它幾乎已經放棄了所有的記憶體空間,沒有任何可執行程式碼,也不能被排程,僅僅在程序列表中保留一個位置,記載該程序的退出狀態等資訊供其它程序收集,除此之外,殭屍程序不再佔有任何記憶體空間。

#include;

intmain() { printf("%c",‘c‘); _exit(0); }

TAG標籤:系統命令 LINUX exit #