2011-01-11 39 views
5

我想從我的C++項目中使用fork-exec產生一個新進程。我正在使用fork-exec來爲子進程創建一個雙向管道。但是我擔心分叉進程中的資源不會被正確釋放,因爲exec-call將完全接管我的進程並且不會調用任何析構函數。釋放C++資源和fork-exec?

我試圖通過在主月底從catch塊拋出異常並調用EXECL規避這一點,但這種方法不破壞任何單身。

是否有任何明智的方法來實現這一安全? (希望避免任何atexit對黑客)

實施例:下面的代碼輸出:

We are the child, gogo! 
Parent proc, do nothing 
Destroying object 

即使兩岔過程還具有需要之前我請EXECL被破壞的單的副本。

#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()中,您可以標記close-on-exec,以便內核爲您關閉它們。 http://pubs.opengroup.org/onlinepubs/009695399/functions/exec.html – 2011-01-11 20:48:38

+1

順便說一下,如果析構函數在分叉的子代以及父代中被調用,它最終會調用構造函數一次(在父代中)和析構函數兩次(都在父母和孩子中)。 – 2011-01-11 20:51:23

回答

3

有,你不需要調用析構函數任何在forkexec之間的良好機會。是的,fork會複製整個進程狀態,包括具有析構函數的對象,並刪除所有狀態。但它確實很重要嗎?一個觀察者可以從程序外面觀察 - 另一個在同一臺計算機上運行的不相關進程 - 是否告訴瞭解析器沒有運行在孩子身上?如果沒有辦法說明,就沒有必要運行它們。

即使外部觀察者可以告訴,它可能是積極錯誤運行在子中的析構函數。通常的例子是:假設你在致電fork之前寫了一些東西給stdout,但它被緩衝在庫中,所以還沒有真正傳送到操作系統。在這種情況下,您一定不能致電fclosefflushstdout在子中,否則輸出會發生兩次! (這也是爲什麼你幾乎可以肯定應該調用_exit而不是exit如果exec失敗。)

說了這麼多,還有,你可能需要做一些清理工作,在兒童中的兩種情況。一種是文件描述符(請勿將與stdio FILE或iostream對象混淆),在exec之後不應該打開它們。處理這些正確的方法是,以儘快之後他們打開(某些操作系統允許你這樣做在open本身,但這不是普遍的)和/或環3設置FD_CLOEXEC標誌對他們一些大號呼叫close不是fclose)在孩子。 (FreeBSD有closefrom,但據我所知,沒有人會這樣做,這是一個恥辱,因爲它真的很方便。)

另一種情況是系統全局線程鎖,它是一個棘手的標準區域 - 可能最終由父母和孩子共同擁有,然後通過exec繼承,這個過程不知道它擁有鎖定。這是pthread_atfork應該是,但我已經讀過,實際上它不能可靠地工作。我可以提供的唯一建議是「當你打電話給fork時不要拿着任何鎖,」對不起。