Как правильно использовать ifstreams в c ++, с подпроцессом и избежать утечек? - PullRequest
0 голосов
/ 28 апреля 2019

Я пытаюсь создать симулятор пиццерии, чтобы узнать больше о подпроцессах и потоках.Я хочу избежать максимальных утечек памяти.

На данный момент, когда я создаю только одну кухню, ошибок нет, но когда я создаю 2 или более, я получаю некоторые утечки с ifstreams и ofstreams при уничтожении.

Существует главный ():

int main(int argc __attribute__((unused)), char const *argv[] __attribute__((unused)))
{
    Kitchen k(0, 2, 30);
    Kitchen k2(0, 2, 30);

    return 0;
}

Kitchen.hpp:

class Kitchen {
    private:

        bool                                _IsFull = false;
        int                                 _Pid = 0;

        int                                 _CookTime;
        int                                 _MaxCooks;
        long                                _RefreshDelay;
        double                              _MaxTime = 5;
        unsigned long int                   _QueueSize;

        std::ofstream                       _Opipe;
        std::ifstream                       _Ipipe;
        unsigned int                        _Door = 0;
        std::string                         _FallIn = "";
        std::string                         _FallOut = "";

        void CooksAwakening (void);
        void CleaningTime (void);
        void OpenPipe (void);
        void Quit (void);
        void Run (void);

    public:
        explicit Kitchen (int CookingTime, int MaxCooks, long RefreshDelay);
        virtual ~Kitchen ();

        bool IsFull () const { return _IsFull; }
        int GetPid () const { return _Pid; }
    };

И Kitchen.cpp:

int Knum = 0;

Kitchen::Kitchen(int CookingTime, int MaxCooks, long RefreshDelay)
: _CookTime(CookingTime), _MaxCooks(MaxCooks), _RefreshDelay(RefreshDelay), _QueueSize(_MaxCooks * 2)
{
    OpenPipe();
    _Pid = fork();
    if (_Pid == 0) {
        _Opipe.open(_FallIn.c_str(), std::ostream::out);
        _Ipipe.open(_FallOut.c_str(), std::istream::in);
        Run();
    } else {
        _Ipipe.open(_FallIn.c_str(), std::istream::in);   // Valgrind point this line
        _Opipe.open(_FallOut.c_str(), std::ostream::out); // Valgrind point this line too
    }
}

Kitchen::~Kitchen()
{
    if (_Pid == 0) {
    } else {
        _Opipe << "QUIT" << std::endl;
        _Ipipe.close();
        _Opipe.close();
        unlink(_FallIn.c_str());
        unlink(_FallOut.c_str());
    }
}

void Kitchen::Quit(void)
{
    CleaningTime();
    _Ipipe.close();
    _Opipe.close();
    exit(0);
}

void Kitchen::CleaningTime(void)
{
    while (!_Cooks.empty()) {
        _Cooks.pop_back();
    }
    while (!_PizzaQueue.empty()) {
        _PizzaQueue.pop_back();
    }
    _Cooks.shrink_to_fit();
    _PizzaQueue.shrink_to_fit();
}

void Kitchen::OpenPipe(void)
{
    std::cout << "Kit Open Pipes\t" << getpid() << '\n';
    std::ostringstream       oss1;
    oss1 << "/tmp/kint" << Knum;
    _FallIn = oss1.str();

    std::ostringstream      oss2;
    oss2 << "/tmp/kout" << Knum;
    _FallOut = oss2.str();

    _Door = Knum;
    ++Knum;

    if (mkfifo(_FallOut.c_str(), 0666) != 0) {
        perror ("mkfifo1");
        exit(84);
    }
    if (mkfifo(_FallIn.c_str(), 0666) != 0) {
        perror("mkfifo2");
        exit(84);
    }
}

void    Kitchen::Run(void)
{
    std::string cmd;

    while (_Ipipe >> cmd)
    {
        if (cmd == "QUIT") {
            Quit();
        }
    }
    Quit();
}

Это валгриндрезультат:

total heap usage: 36 allocs, 32 frees, 109,712 bytes allocated
==20890== 
==20890== 552 bytes in 1 blocks are still reachable in loss record 1 of 4
==20890==    at 0x483880B: malloc (vg_replace_malloc.c:309)
==20890==    by 0x4C1536E: __fopen_internal (in /usr/lib64/libc-2.28.so)
==20890==    by 0x4925AA3: std::__basic_file<char>::open(char const*, std::_Ios_Openmode, int) (in /usr/lib64/libstdc++.so.6.0.25)
==20890==    by 0x496789D: std::basic_filebuf<char, std::char_traits<char> >::open(char const*, std::_Ios_Openmode) (in /usr/lib64/libstdc++.so.6.0.25)
==20890==    by 0x4967A73: std::basic_ifstream<char, std::char_traits<char> >::open(char const*, std::_Ios_Openmode) (in /usr/lib64/libstdc++.so.6.0.25)
==20890==    by 0x4052F2: WorkSpace::Kitchen::Kitchen(int, int, long) (Kitchen.cpp:36)
==20890==    by 0x40250C: main (main.cpp:46)
==20890== 
==20890== 552 bytes in 1 blocks are still reachable in loss record 2 of 4
==20890==    at 0x483880B: malloc (vg_replace_malloc.c:309)
==20890==    by 0x4C1536E: __fopen_internal (in /usr/lib64/libc-2.28.so)
==20890==    by 0x4925AA3: std::__basic_file<char>::open(char const*, std::_Ios_Openmode, int) (in /usr/lib64/libstdc++.so.6.0.25)
==20890==    by 0x496789D: std::basic_filebuf<char, std::char_traits<char> >::open(char const*, std::_Ios_Openmode) (in /usr/lib64/libstdc++.so.6.0.25)
==20890==    by 0x4967AC3: std::basic_ofstream<char, std::char_traits<char> >::open(char const*, std::_Ios_Openmode) (in /usr/lib64/libstdc++.so.6.0.25)
==20890==    by 0x40531C: WorkSpace::Kitchen::Kitchen(int, int, long) (Kitchen.cpp:37)
==20890==    by 0x40250C: main (main.cpp:46)
==20890== 
==20890== 8,192 bytes in 1 blocks are still reachable in loss record 3 of 4
==20890==    at 0x4839593: operator new[](unsigned long) (vg_replace_malloc.c:433)
==20890==    by 0x496358F: std::basic_filebuf<char, std::char_traits<char> >::_M_allocate_internal_buffer() (in /usr/lib64/libstdc++.so.6.0.25)
==20890==    by 0x49678B5: std::basic_filebuf<char, std::char_traits<char> >::open(char const*, std::_Ios_Openmode) (in /usr/lib64/libstdc++.so.6.0.25)
==20890==    by 0x4967A73: std::basic_ifstream<char, std::char_traits<char> >::open(char const*, std::_Ios_Openmode) (in /usr/lib64/libstdc++.so.6.0.25)
==20890==    by 0x4052F2: WorkSpace::Kitchen::Kitchen(int, int, long) (Kitchen.cpp:36)
==20890==    by 0x40250C: main (main.cpp:46)
==20890== 
==20890== 8,192 bytes in 1 blocks are still reachable in loss record 4 of 4
==20890==    at 0x4839593: operator new[](unsigned long) (vg_replace_malloc.c:433)
==20890==    by 0x496358F: std::basic_filebuf<char, std::char_traits<char> >::_M_allocate_internal_buffer() (in /usr/lib64/libstdc++.so.6.0.25)
==20890==    by 0x49678B5: std::basic_filebuf<char, std::char_traits<char> >::open(char const*, std::_Ios_Openmode) (in /usr/lib64/libstdc++.so.6.0.25)
==20890==    by 0x4967AC3: std::basic_ofstream<char, std::char_traits<char> >::open(char const*, std::_Ios_Openmode) (in /usr/lib64/libstdc++.so.6.0.25)
==20890==    by 0x40531C: WorkSpace::Kitchen::Kitchen(int, int, long) (Kitchen.cpp:37)
==20890==    by 0x40250C: main (main.cpp:46)
==20890== 
==20890== LEAK SUMMARY:
==20890==    definitely lost: 0 bytes in 0 blocks
==20890==    indirectly lost: 0 bytes in 0 blocks
==20890==      possibly lost: 0 bytes in 0 blocks
==20890==    still reachable: 17,488 bytes in 4 blocks
==20890==         suppressed: 0 bytes in 0 block

Я компилирую с помощью gcc и следующих флагов: -Wall -Wextra -Weffc ++

И команда valgrind выглядит следующим образом: valgrind --leak-check = full --show-leak-types = all ./plazza

1 Ответ

0 голосов
/ 28 апреля 2019

Показанный код выполняет в дочернем процессе следующее:

void Kitchen::Quit(void)
{
    CleaningTime();
    _Ipipe.close();
    _Opipe.close();
    exit(0);
}

Даже если открытые потоки закрыты вручную, эти объекты все еще существуют в этой точке, и exit(0) немедленно завершает процесс.

Несмотря на фактическое закрытие файлов, объекты потока все еще существуют, и им все еще выделяется некоторая память для их внутренних потоковых буферов. Эта память освобождается только тогда, когда эти объекты должным образом уничтожены, чего не происходит через exit(0).

exit(0) - это стандартная функция библиотеки C, которая ничего не знает ни о каких объектах C ++. Он просто сбивает процесс с большой орбиты.

valgrind обнаруживает, что подпроцесс завершен без полного освобождения всей выделенной памяти, и сообщает об этом.

Чтобы «правильно использовать ifstreams в C ++ с подпроцессами и избежать утечек», подпроцессы должны завершаться так же, как основной процесс: при возврате из main(). Это, очевидно, уничтожит все объекты в автоматической и статической области в подпроцессе, прежде чем завершить его.

Это, очевидно, создает несколько трудных для решения проблем с точки зрения логической работы программы. Один из распространенных подходов грубой силы, который будет часто работать, - это генерировать исключение вместо exit() ing, которое попадает в main(). Конечно, это работает только с кодом, который структурирован так, чтобы правильно вести себя при возникновении исключений.

...