2012-02-22 7 views
9

に出るの機能を実行しない私は、私はいつでも私のプログラムが終了を実行する機能を持っていますなぜシグナルが出るのか、exit()コールなどのために終了するのでしょうか?ときexitどちらのシステムがその登録の逆順でatexitに登録された関数のスタックを維持し、各それらを呼び出しますはどのようにC++

#include <cstdlib> 

void exiting() { 
    std::cout << "Exiting"; 
} 

int main() { 
    std::atexit(exiting); 
} 

+0

すべての場合では可能ではありません。信号の中には、必要に応じてすぐにプロセスを停止させるものがあります。 (スタックオーバフローなど) –

+0

どのオペレーティングシステムですか? –

答えて

26

あなたはcstdlibヘッダー内の適切な名前std::atexit機能を使用することができます関数が呼び出されるか、プログラムはmainから返されます。この方法で少なくとも32個の関数を登録することができます。

+0

シグナルや 'abort' /' terminate'の呼び出しの後には呼び出されません。 (言った:**通常のプロセス終了時に呼び出される関数を登録する**) –

+1

@BillyONealなぜ「exit」関数が呼び出されるか、または「main」からプログラムが返されるとき –

+0

一言、私の間違い:) –

3

グローバルインスタンスを持つクラスのデストラクタに置くことができます。

class SomeGlobalStuff { 
    ~SomeGlobalStuff() { 
      foo(); 
    } 
    static SomeGlobalStuff instance; 
}; 
// putting this in a single compilation unit. 
SomeGlobalStuff SomeGlobalStuff::instance instance; 

しかし、他の方法と同様に、あなたはそれがまだ存在していることをgaranteeことができない場合は、任意のデータを使用できないことを覚えておく必要があります。グローバルオブジェクトの割り当て解除は任意の順序で行われるので、基本的にfoo()関数ではstd :: coutを使用することはできません。この点に関してatexit()は悪いです。なぜなら、グローバルオブジェクトの破壊の前または後に実行するかどうかは、コンパイラオプションとコンパイラオプションによって決まるからです。

とにかく、信号を正しく処理する必要があります。どの信号を扱うかを決める必要があります(SIGSEGVを扱いたくない可能性が高いでしょう)。信号処理をエスケープすることはできません。また、信号がいつでも(マスクされていない限り)プログラムを中断して、更新の途中でデータ構造が任意の状態になる可能性があることに注意してください。

+0

私は、 'cout'はどこでも有効である(最初に構築されると定義されています)が、現時点では見つからないという言語の標準があると思います。これはシグナル、 'abort'、' terminate'などのためには機能しません。 –

+0

@BillyONeal: 'abort'は信号を発生させます(私が言ったように、処理するかどうかを選択する必要があります)。' terminate'は完全に別の問題。また 'std :: cout'は常に存在するかもしれませんが、' abort() 'のアクションは' SIGABRT'を発生させるように定義されています。 fclose(stdout)は、std :: coutが機能しないようにします。 – BatchyX

+0

'SIGABRT'のようなものがあるシステムでのみ。 'fclose(stdout)'はCRTによって 'std :: cout'が機能しなくなることがあります。標準では、標準のコンソール入力/出力/エラーストリームとCの対応するファイルハンドルの関係(存在する場合)は定義されていません。私は、 'cout'が常に有効であると言うとき、'異なる翻訳単位で定義された非ローカル静的オブジェクトの初期化の順序は未定義です.'ルールの影響を受けないことを意味します。おそらく私はあなたの答えを誤解しました。 –

1

プロセスが終了した後で制御を取り戻す唯一の方法(UnixおよびUnixライクなオペレーティングシステム)は、wait(2)です。電源異常、カーネルパニック、または強制リブートのショート、これは動作するはずです:

#include <sys/types.h> 
#include <sys/wait.h> 
#include <iostream> 

int AtExit() { 
    pid_t pid = fork(); 
    if(pid < 0) return pid; 
    if(pid == 0) return pid; 
    pid = waitpid(pid, 0, 0); 
    return pid; 
} 

int main() { 
    if(AtExit()) { 
    std::cout << "Exiting\n"; 
    return 0; 
    } 

    std::cout << 7 << "\n"; 
} 
+1

あなたはこの問題をどこか別の場所に移しています:このプログラムを殺すとどうなりますか?書かれているように、制御端末の^ Cは親と子の両方にSIGINTを送るでしょう。 – BatchyX

+0

これは本当に創造的な答えです(コメント欄に^ Cが当てはまるという問題はありますが)。あなたが望むならこれで^ Cを無視することができますが、^ \を無視することはできません。 – IanPudney

1

私はLinuxユーザとして答えていますが、これはすべてのウィンドウに適用されるべきです。

私はこのような質問をしていましたので、以前の回答を要約して私の2セントを追加してください。

信号とabort()^C^Zは、exit()で終了する前に関数を呼び出すために「傍受される」可能性があります。シグナルSIGQUIT AKA ^\およびSIGKILLはキーストロークを持たず、傍受できません。次に、csignalヘッダーとC++ラムダの使用例を示します。

#include <iostream> 
#include <csignal> 
#include <cstdlib> 

using namespace std; 

int main() 
{ 
    //signal requires lam take an int parameter 
    //this parameter is equal to the signals value 
    auto lam = 
     [] (int i) { cout << "aborting" << endl; exit(0); }; 

    //^C 
    signal(SIGINT, lam); 
    //abort() 
    signal(SIGABRT, lam); 
    //sent by "kill" command 
    signal(SIGTERM, lam); 
    //^Z 
    signal(SIGTSTP, lam); 


    while(1) 
    { 
    } 

    return 0; 
} 

終了:私は上記の私の例でexit()を使用しているので、注意がここで注意する必要があります。実行されている関数が一度だけ実行する必要のあるクリーンアップ関数の場合は、おそらく静的変数has_runを使用できます。上記の例では、raise()は傍受できない信号です。しかし、これらはコアダンプが付いてくる傾向があります。あなたの選択、ここ。例は

#include <cstdlib> 
#include <iostream> 

using namespace std; 

int main() 
{ 
    //called with no parameters 
    auto lam = []() { cout << "at exit"; }; 

    atexit(lam); 

    return 0; 
} 

注意し、以下のC++ 11は、上記と同様の作用伴うat_quick_exitを有するquick_exitを追加しました。しかし、quick_exitではクリーンアップタスクは実行されません。対照的に、exitではオブジェクトデストラクタが呼び出され、Cストリームは閉じられ、自動ストレージ変数のみがクリーンアップされません。