2011-01-11 13 views
5

C++プロジェクトからfork-execを使用して新しいプロセスを生成しようとしています。子プロセスへの双方向パイプを作成するためにfork-execを使用しています。しかし、エグゼクティブコールがプロセスを完全に引き継いでデストラクタを呼び出さないため、フォークされたプロセスのリソースが適切に解放されないことは恐れられます。C++リソースとfork-execを解放しますか?

私は例外をスローし、execlをmainの最後のcatchブロックから呼び出すことでこれを回避しようとしましたが、この解決法はシングルトンを破壊しません。

これを安全に達成するための賢明な方法はありますか?

例(うまくいけば任意のATEXITハックの回避):次のコード出力:

フォークプロセスはまた、私はEXECLを呼び出す前に破壊する必要がシングルトンのコピーを持っているにもかかわらず
We are the child, gogo! 
Parent proc, do nothing 
Destroying object 

#include <iostream> 
#include <unistd.h> 

using namespace std; 

class Resources 
{ 
public: 
    ~Resources() { cout<<"Destroying object\n"; } 
}; 

Resources& getRes() 
{ 
    static Resources r1; 
    return r1; 
} 

void makeChild(const string &command) 
{ 
    int pid = fork(); 
    switch(pid) 
    { 
    case -1: 
     cout<<"Big error! Wtf!\n"; 
     return; 
    case 0: 
     cout<<"Parent proc, do nothing\n"; 
     return; 
    } 
    cout<<"We are the child, gogo!\n"; 
    throw command; 
} 

int main(int argc, char* argv[]) 
{ 
    try 
    { 
     Resources& ref = getRes(); 
     makeChild("child"); 
    } 
    catch(const string &command) 
    { 
     execl(command.c_str(), ""); 
    } 
    return 0; 
} 
+1

あなたはどのリソースについて話していますか?ほとんどのファイル記述子はexec()に残っています。exec()はclose-on-execをマークして、カーネルがそれらを閉じることができます。 http://pubs.opengroup.org/onlinepubs/009695399/functions/exec.html –

+1

ちなみに、デストラクタが分岐した子や親の中で呼び出された場合、デストラクタは一度(親)とデストラクタを2回(親と子の両方で)実行します。 –

+1

私はここでは未定義の挙動に危険なほど近いと思っていますが、ResourcesクラスはCライブラリをRAIIオブジェクトにラッピングするために使用するいくつかのシングルトンクラスを表しています。そして、forkが実際にプロセスの状態全体をコピーするならば、私はexec()を呼び出す前におそらくRAIIデストラクタを呼び出すべきです。もちろん、リソースがプログラムの外部(データベース接続のようなもの)であった場合、これは狂気になります。しかし、彼らは図書館なので、私は彼らが親プロセスと子プロセスの両方でリリースされるべきだと考えています。 [役立つなら、現在はncurses、nscapi、SDLをシングルトンにラップしています] – Phog

答えて

3

あなたはforkexecの間のいずれかのデストラクタを呼び出すない必要を行う優れたオッズがあります。ええ、forkは、デストラクタを持つオブジェクトを含むプロセス全体の状態のコピーを作成し、execはその状態をすべて消します。しかし、それは実際に重要ですか?同じコンピュータ上で実行されている他の無関係のプロセスであるあなたのプログラムの外からのオブザーバーは、デストラクタがその子に実行されていないことを伝えることができますか?伝える方法がない場合は、実行する必要はありません。

外部の観察者が知ることができたとしても、子供の中でデストラクタを実行するには、が間違ってと間違っている可能性があります。通常の例は次のとおりです。forkを呼び出す前にstdoutに何かを書きましたが、ライブラリにバッファリングされているため、実際にはオペレーティングシステムにはまだ配信されていません。その場合、になる必要はありません。子供のfcloseまたはfflushstdoutに電話をかけてください。そうしないと、出力が2回発生します。 (execが失敗した場合は、ほぼ確実に代わりexit_exitを呼び出す必要があります理由でもある。)

すべてのことを言って、あなたが子供にいくつかのクリーンアップ作業を行う必要があります2つの一般的な場合があります。 1つはファイル記述子(はstdio FILEまたはiostreamオブジェクトと混同しないでください)は、execの後にはオープンしないでください。これらを処理する正しい方法は、になった後で、できるだけ早くFD_CLOEXECフラグを設定することです(open自体でこれを実行できるOSもありますが、これは普遍的ではありません)。子供の中で何度か電話をかけてclosefclose)です。 (FreeBSDにはclosefromがありますが、私が知っている限り他の誰もしていませんが、これは本当に便利だから残念です)

もう1つのケースはシステム全体のスレッドロックです。これはぎこちない標準化されていません領域-になり、親と子の両者によって保持され、execを介してロックを保持しているとは考えられないプロセスに継承されます。これはpthread_atforkが対象としているものですが、実際には確実に動作しません。私が提供できる唯一のアドバイスは、あなたがforkと呼ぶときに「ロックを保持しないでください。

+0

ありがとう!これは、私が持っていたすべての誤解を打ち消しました。 (...特に良心的な子プロセスの未公開リソースを無視できるので特にそうです)=== – Phog

+0

良い説明; Linuxではclosefrom()をエミュレートすることは可能ですが、いずれにしても非常に醜いハックですので、お勧めしません。 – MarkR

関連する問題