2011-01-11 10 views
5

Tôi đang cố gắng để sinh ra một quy trình mới từ dự án C++ của tôi bằng cách sử dụng fork-exec. Tôi đang sử dụng fork-exec để tạo ra một ống hai chiều cho quá trình con. Nhưng tôi sợ tài nguyên của tôi trong quá trình chia rẽ sẽ không được giải phóng đúng cách, vì cuộc gọi exec sẽ hoàn toàn tiếp quản quá trình của tôi và sẽ không gọi bất kỳ kẻ hủy diệt nào.Phát hành tài nguyên C++ và fork-exec?

Tôi đã cố gắng phá vỡ điều này bằng cách ném một ngoại lệ và gọi execl từ một khối catch ở cuối của chính, nhưng giải pháp này không phá hủy bất kỳ đơn.

Có cách nào hợp lý để đạt được điều này một cách an toàn không? (Hy vọng tránh bất kỳ hacks atExit)

Ex: Các đầu ra đoạn mã sau:

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

Mặc dù quá trình chia hai còn có một bản sao của singleton mà cần phải được destructed trước khi tôi gọi 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

Bạn đang nói về tài nguyên nào? Chủ yếu tập tin mô tả tồn tại exec(), mà bạn có thể đánh dấu close-on-exec để hạt nhân đóng chúng cho bạn. http://pubs.opengroup.org/onlinepubs/009695399/functions/exec.html –

+1

Nhân tiện, nếu destructors được gọi trong đứa trẻ được chia đôi cũng như trong phụ huynh, nó sẽ kết thúc gọi các nhà xây dựng một lần (trong phụ huynh) và destructors hai lần (cả trong phụ huynh và đứa trẻ). –

+1

Tôi tin rằng tôi đang nhận được nguy hiểm gần với hành vi không xác định ở đây, nhưng lớp Tài nguyên đại diện cho một số lớp đơn tôi sử dụng để gói C-thư viện vào RAII-đối tượng. Và nếu ngã ba thực sự sao chép toàn bộ trạng thái quá trình, thì có lẽ tôi nên gọi các RAII-destructors trước khi tôi gọi exec(). Điều này tất nhiên sẽ trở nên điên rồ nếu các tài nguyên nằm ngoài chương trình (như kết nối cơ sở dữ liệu). Nhưng vì chúng là thư viện, tôi tin rằng chúng nên được phát hành trong cả phụ huynh và tiến trình con. [Nếu nó giúp, tôi hiện đang gói ncurses, nscapi và SDL thành singletons] – Phog

Trả lời

3

Có mâu thuẫn tuyệt vời mà bạn không cần để gọi bất kỳ destructors ở giữa forkexec. Vâng, fork tạo một bản sao của toàn bộ tiểu bang xử lý của bạn, bao gồm các đối tượng có destructors và exec xóa tất cả trạng thái đó. Nhưng nó thực sự quan trọng? Có thể một người quan sát từ bên ngoài chương trình của bạn - một quá trình khác không liên quan đang chạy trên cùng một máy tính - nói rằng những kẻ hủy diệt không được chạy trong đứa trẻ? Nếu không có cách nào để nói, không cần phải chạy chúng.

Ngay cả khi người quan sát bên ngoài có thể biết, có thể là đang hoạt động sai để chạy trình phá hủy ở trẻ em. Ví dụ thông thường cho điều này là: hãy tưởng tượng bạn đã viết một cái gì đó để stdout trước khi gọi fork, nhưng nó đã được đệm trong thư viện và do đó chưa thực sự được gửi đến hệ điều hành nào. Trong trường hợp đó, bạn không được gọi fclose hoặc fflush trên số stdout ở trẻ em hoặc đầu ra sẽ diễn ra hai lần! (Đây cũng là lý do tại sao bạn gần như chắc chắn nên gọi _exit thay vì exit nếu không exec không thành công.)

Có nói chung là có hai trường hợp bạn cần làm một số công việc dọn dẹp ở trẻ. Một là các bộ mô tả tệp (không nhầm lẫn các tệp này với các tệp Fdio hoặc các đối tượng iostream) không nên mở sau exec. Cách chính xác để giải quyết những điều này là đặt cờ FD_CLOEXEC vào chúng càng sớm càng tốt sau khi chúng được mở mở (một số hệ điều hành cho phép bạn thực hiện điều này trong chính số này) và/hoặc lặp lại từ 3 đến một số lớn gọi số close (khôngfclose) ở trẻ em. (FreeBSD có closefrom, nhưng theo như tôi biết, không ai khác làm, đó là một sự xấu hổ bởi vì nó thực sự khá tiện dụng.)

Trường hợp còn lại là hệ thống khóa toàn cầu, mà - đây là một gai và kém tiêu chuẩn hóa khu vực - có thể gió được tổ chức bởi cả cha lẫn mẹ và đứa trẻ, và sau đó được kế thừa trên exec vào một quá trình không có ý tưởng nó giữ một khóa. Đây là những gì pthread_atfork là nghĩa vụ phải được cho, nhưng tôi đã đọc rằng trong thực tế nó không hoạt động đáng tin cậy. Lời khuyên duy nhất tôi có thể cung cấp là "không giữ bất kỳ khóa nào khi bạn gọi fork", xin lỗi.

+0

Cảm ơn! Điều này đã dập tắt tất cả những hiểu lầm mà tôi có. (... Đặc biệt là vì nó cho phép tôi bỏ qua các nguồn lực chưa được phát hành trong quá trình con với một lương tâm tốt =) – Phog

+0

Giải thích tốt; dưới Linux có thể mô phỏng cận cảnh(), nhưng trong mọi trường hợp, nó là một hack rất xấu và tôi sẽ không khuyên bạn nên nó. – MarkR