簡體   English   中英

_Exit如何在C ++程序中運行?

[英]How will _Exit behave in a C++ program?

C99提供了_Exit功能,“立即”退出,雖然 可能會關閉文件描述符。 Unix / POSIX通過強制關閉所有fd而不刷新來擴展此行為(並提供同義詞_exit )。

當從C ++程序調用時,這些函數會調用static對象的析構函數嗎? C ++標准是否對_Exit做出任何保證?

(受這個問題的啟發;我突然想知道在C ++中典型的fork - exec - _exit成語會發生什么。)

首先,任何形式的程序退出都不會自動調用堆對象的析構函數(隱含在ISO / IEC 14882:1998(E)12.4.10中)。

調用exit()不會為具有自動持續時間的對象調用析構函數,因為它不會通過其封閉范圍返回(3.6.1.4)。 但是, 按照構造的相反順序調用靜態對象的析構函數(18.3.8)。

調用abort()不會為任何類型的對象調用任何析構函數,也不會調用atexit()注冊的函數(18.3.3)。 我在這里的C ++標准副本有點過時,並沒有直接提到_exit_Exit ,但我想,如果存在,它們的行為應該相同 - 也就是說,不要調用任何析構函數。 特別是,在C99標准中, _Exit()跳過atexit處理程序(實現定義是否刷新流緩沖區,關閉打開的流,還是刪除臨時文件)。

進一步注意,可以通過捕獲信號SIGABRT來取消abort() (ISO / IEC 9899:1999(E)7.20.4.1.2 - 我這里只有C99但我希望它在C ++引用的版本中是相同的)。 _Exit()不能。

更實際的是,在abort()_exit()大多數unix實現中, abort()引發SIGABRT_exit()只是調用操作系統調用來立即終止進程。 這意味着主要的區別是:

  • 您可以為_exit()指定退出代碼
  • abort()可能被信號處理程序捕獲
  • 根據系統配置,操作系統和ulimits, abort()可能會導致核心轉儲或類似情況

fork()/exec()模式中, _exit()可能更可取,以避免核心轉儲的可能性。

它在標准C ++中根本不存在,因此無法保證。

列入的C ++ 0x。 這指定(§18.5):

函數_Exit(int status)在本國際標准中有其他行為:

- 程序終止而不執行自動,線程或靜態存儲持續時間對象的析構函數,並且不調用傳遞給atexit()的函數(3.6.3)。

跟進:

ISO於2011年8月12日批准了C ++ 0x。

從技術上講, _Exit不是由C ++標准定義的,因此您甚至無法從100%可移植的C ++程序中調用它。 C ++ 03標准通過引用並入C89標准(又名C90或ANSI C),而_Exit僅在較新的C99標准中定義。 我不確定即將推出的C ++ 0x標准包含哪個版本的C,但我猜它是基於C99的。

但無論如何,這里是相關語言標准的相關條款:

_Exit不保證關閉文件描述符。 從C99§7.20.4.4/ 2(強調我的):

_Exit函數會導致正常的程序終止,並將控制權返回給主機環境。 沒有通過注冊的功能atexit由注冊的函數或信號處理signal函數被調用。 返回到主機環境的狀態的確定方式與exit功能(7.20.4.3)相同。 是否刷新具有未寫入緩沖數據的開放流,關閉打開流,或者刪除臨時文件是實現定義的。

回想一下, 實現定義意味着實現(即編譯器工具鏈和運行時環境)可以選擇做任何想做的事情,但它必須記錄它的作用

來自C ++03§3.6.3/ 1:

用於靜態存儲持續時間的初始化對象的析構函數(12.4)(在塊作用域或命名空間作用域中聲明)作為從main返回並由於調用exit (18.3)的結果而被調用。 這些對象以其構造函數完成或動態初始化完成的相反順序銷毀。 如果對象是靜態初始化的,則對象的破壞順序與對象動態初始化的順序相同。 對於數組或類類型的對象,在銷毀子對象構造期間初始化靜態存儲持續時間的任何本地對象之前,將銷毀該對象的所有子對象。

§3.6.3/ 4:

調用函數

void abort();

<cstdlib>聲明的終止程序,不會為自動或靜態存儲持續時間的對象執行析構函數,也不會調用傳遞給atexit()的函數。

實際上,在大多數實現中,全局對象析構函數是通過atexit實現的,所以你會看到_Exit不會為全局對象調用析構函數,雖然這種行為不能得到保證(因為_Exit和C ++不能保證都存在於同一種語言)。

請注意,雖然C ++沒有指定_Exit而C99讓它實現定義是否刷新緩沖區,但POSIX 要求它不刷新緩沖區(因為這會破壞_exit / _Exit的主要用法,即在fork之后處理execve失敗)。 由於POSIX沒有與C ++標准保持一致或者在任何事情上遵守它們,我認為未來版本的C ++標准不太可能試圖改變這一點。 它可能要么未指定_Exit要么指定它是實現定義的。

C ++ 0x定義了一個名為std :: quick_exit的新函數,它終止進程而不調用任何析構函數。 剛剛檢查過,g ++ - 4.4.5已經提供了它。

有一個有趣的分析, 在這里與並發性和對象關系的破壞。 據我所知,析構函數不會被調用。 目前的標准中沒有任何關於它的內容。

靜態析構函數的調用是根據atexit定義的。 _exit(或_Exit)定義為不運行atexit處理程序。 因此,任何實現都不應調用靜態析構函數。

調用exit()時甚至不調用自動析構函數。

因此,C ++的_Exit語義的任何理智定義都不會運行析構函數。

我在Mac OS上用gcc進行了快速測試,我的析構函數沒有被調用。

struct A
{
    ~A()
    {
        puts("A::~A");
    }
};

A globalA;

int main()
{
    A localA;
    _exit(0); // or _Exit(0)
    return 0;
}

另一方面, exit(0) 調用 globalA的析構函數。

fork()exec()_exit()都是由POSIX定義的,它們將C99的_Exit()預定多年。 使用fork / exec / _exit程序不能移植到支持C ++的每個系統。

特別是關於_exit() ,它是一個操作系統調用(在POSIX下)將關閉文件並直接終止進程(但不一定很快)。 這將繞過用於調用析構函數的任何C ++機制。

即使C ++ 0x提供了_Exit()或類似的東西,我懷疑是否有太多理由將它與fork結合使用。 它可能只是為其他環境中的“快速退出”提供了更廣泛的可移植性。 如果您使用的是POSIX API,則_exit()已涵蓋該功能。

程序終止在C ++ 2003部分[3.6.3]中解決。 它表示當main()返回和調用exit()時,靜態對象被隱式地破壞。 它還說當調用abort()時不會破壞這些對象。 _exit()未在C ++ 2003標准中得到解決,但POSIX文檔中描述了它旨在繞過特定於語言的清理的事實。 通過陳述的內容和C ++標准中未說明的內容進一步證實了這種效果。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM