exit() _exit() 和 return() 的區別

來源:互聯網
上載者:User

轉載於  http://www.360doc.com/content/12/0504/15/9400799_208611794.shtml

#include<stdlib.h>

void exit(int status);
不像fork那麼難理解,從exit的名字就能看出,這個系統調用是用來終止一個進程的。無論在程式中的什麼位置,只要執行到exit系統調用,進程就會停止剩下的所有操作,清除包括PCB在內的各種資料結構,並終止本進程的運行。請看下面的程式:
/* exit_test1.c */
#include<stdlib.h>
main()
{
    printf("this process will exit!\n");
    exit(0);
    printf("never be displayed!\n");
}
編譯後運行:
$gcc exit_test1.c -o exit_test1
$./exit_test1
this process will exit!
我們可以看到,程式並沒有列印後面的"never be displayed!\n",因為在此之前,在執行到exit(0)時,進程就已經終止了。
exit 系統調用帶有一個整數類型的參數status,我們可以利用這個參數傳遞進程結束時的狀態,比如說,該進程是正常結束的,還是出現某種意外而結束的,一般來說,0表示沒有意外的正常結束;其他的數值表示出現了錯誤,進程非正常結束。我們在實際編程時,可以用wait系統調用接收子進程的傳回值,從而針對不同的情況進行不同的處理。關於wait的詳細情況,我們將在以後的篇幅中進行介紹。
exit和_exit
作為系統調用而言,_exit和exit是一對孿生兄弟。
這時隨便一個懂得C語言並且頭腦清醒的人都會說,_exit和exit沒有任何區別,但我們還要講一下這兩者之間的區別,這種區別主要體現在它們在函數庫中
的定義。_exit在Linux函數庫中的原型是:
#include<unistd.h>
    void _exit(int status);
exit比較一下,exit()函數定義在 stdlib.h中,而_exit()定義在unistd.h中,從名字上看,stdlib.h似乎比unistd.h進階一點,那麼,它們之間到底有什麼區別呢?

     _exit()函數的作用最為簡單:直接使進程停止運行,清除其使用的記憶體空間,並銷毀其在核心中的各種資料結構;exit()函數則在這些基礎上作了一些封裝,在執行退出之前加了若干道工序,也是因為這個原因,有些人認為exit已經不能算是純粹的系統調用。
      exit()函數與_exit()函數最大的區別就在於exit()函數在調用exit系統調用之前要檢查檔案的開啟情況,把檔案緩衝區中的內容寫迴文件,就是“清理I/O緩衝”。
在Linux 的標準函數庫中,有一套稱作“進階I/O”的函數,我們熟知的printf()、fopen()、fread()、fwrite()都在此列,它們也被稱作“緩衝I/O(buffered I/O)”,其特徵是對應每一個開啟的檔案,在記憶體中都有一片緩衝區,每次讀檔案時,會多讀出若干條記錄,這樣下次讀檔案時就可以直接從記憶體的緩衝區中讀取,每次寫檔案的時候,也僅僅是寫入記憶體中的緩衝區,等滿足了一定的條件(達到一定數量,或遇到特定字元,如分行符號\n和檔案結束符EOF),再將緩衝區中的內容一次性寫入檔案,這樣就大大增加了檔案讀寫的速度,但也為我們編程帶來了一點點麻煩。如果有一些資料,我們認為已經寫入了檔案, 實際上因為沒有滿足特定的條件,它們還只是儲存在緩衝區內,這時我們用_exit()函數直接將進程關閉,緩衝區中的資料就會丟失,反之,如果想保證數
據的完整性,就一定要使用exit()函數。
請看以下常式
/* exit2.c */
#include<stdlib.h>
main()
{
    printf("output begin\n");
    printf("content in buffer");
    exit(0);
}
編譯並運行:
$gcc exit2.c -o exit2
$./exit2
output begin
content in buffer

/* _exit1.c */
#include<unistd.h>
main()
{
    printf("output begin\n");
    printf("content in buffer");  //注,這個地方一定不要加上\n斷行符號分行符號,否則的話,就會顯示了
    _exit(0);
}
編譯並運行:
$gcc _exit1.c -o _exit1
$./_exit1
output begin

return,exit(),_exit()的區別
1. return 只能返回當前函數,常用於擷取函數的傳回值,只有在main函數中return才起到退出程式的作用,而在子程式中只能返回上一級調用程式。

2. exit()和_exit()函數調用時會結束進程,他們的區別在於結束時兩者所做的清理工作不同。exit()會在退出程式之前關閉所有開啟的檔案,清空標準輸入輸出緩衝區,最後執行atexit()註冊的回呼函數(在main中使用return效果和exit()一樣)。而_exit()則不會關閉開啟的檔案,不會清空緩衝區,也不會執行atexit()註冊的回呼函數,這一切由核心接管執行處理。

3. 所以,fork()的子進程中,最好不要使用exit()來退出,因為子進程調用exit()產生的清理工作可能會影響到父進程
例如:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
void good()
{
printf("YES\n");
}
void main()
{
atexit(good);
printf("Hello\n");
exit(EXIT_SUCCESS);
}
輸出:
Hello
YES

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
void good()
{
printf("YES\n");
}
void main()
{
atexit(good);
printf("Hello\n");
_exit(EXIT_SUCCESS);
}
輸出:
Hello

Linux的源碼

#define __NR_exit                 1

#define __NR__exit __NR_exit /* 摘自檔案include/asm-i386/unistd.h*/ 
"__NR_"是在Linux的源碼中為每個系統調用加上的首碼,請注意第一個exit前有2條底線,第二個exit前只有1條底線。

Linux核心還提供了一些C語言函數庫,這些庫對系統調用進行了一些封裝和擴充,因為這些庫函數與系統調用的關係非常緊密,所以習慣上把這些函數也稱為系統-調用。#define這句只能說明核心裡的兩個系統調用_exit和exit相同,但其封裝後對應的C庫函數_exit()和exit()是不同的。

exit()函數定義在stdlib.h中,而_exit()定義在unistd.h中;

exit()函數的作用最為簡單:直接使進程停止運行,清除其使用的記憶體空間,並銷毀其在核心中的各種資料結構;exit()函數則在這些基礎上作了一些封裝-,在執行退出之前加了若干道工序,也是因為這個原因,有些人認為exit已經不能算是純粹的系統調用。

exit()函數與_exit()函數最大的區別就在於exit()函數在調用exit系統調用之前要檢查檔案的開啟情況,把檔案緩衝區中的內容寫迴文件,即所謂的"清理I/O緩衝"。

‘exit()’與‘_exit()’有不少區別在使用‘fork()’,特別是‘vfork()’時變得很 突出。 

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


  fork:子進程擁有父進程的資料區段、堆和棧的副本,父進程和子進程共用本文段。但現在很多實現卻並不是將父進程的資料區段、堆棧段進行完全拷貝,而是採用寫時複製(copy-on-write),核心將其標記為唯讀,(典型的頁式虛存)只有父進程或子進程對這些地區進行修改時核心才真正將那一頁進行拷貝,從物理上分離開。
  vfork:由於在vfork後經常是跟著一個exec執行一個新的程式不會在用到原來的地址空間,所以vfork的子進程在調用exec或exit之前是在父進程的空間裡啟動並執行,這樣對於頁式虛存效率很高。另外,vfork的子進程總是先與父進程執行,但是子進程不能依賴與父進程的執行否則產生死結。

exit(0):根據實現的不同而不同,一般是重新整理I/O緩衝區,關閉所有I/O標準流(APUE上如是說,但是我在linux下驗證的結果應該是沒有關閉),一般現在的I/O庫函數在關閉I/O流方面不自找麻煩了。

_exit(0):不重新整理I/O緩衝區

標準I/O庫:
標準I/O庫是帶緩衝的,如果標準輸出是串連到終端裝置,則它是行緩衝的,否則是全緩衝的。行緩衝在接收到一個分行符號才進行重新整理,而全緩衝在緩衝區滿或者程式在執行exit退出後在執行緩衝區重新整理

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.