如何在 C++ 中正确使用 ifstreams 和子进程并避免泄漏?
How to correctly use ifstreams in c++, with subprocess and avoid leaks?
我正在尝试创建一个比萨店模拟以了解有关子进程和线程的更多信息。我想最大程度地避免内存泄漏。
现在,当我只创建一个厨房时,没有错误,但是当我创建 2 个或更多时,我在销毁时遇到了 ifstreams 和 ofstreams 的一些泄漏。
有main():
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();
}
这是 valgrind 结果:
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 和这些标志编译:
-墙
-Wextra
-Weffc++
valgrind命令如下:
valgrind --leak-check=full --show-leak-kinds=all ./plazza
显示的代码在子进程中执行以下操作:
void Kitchen::Quit(void)
{
CleaningTime();
_Ipipe.close();
_Opipe.close();
exit(0);
}
即使打开的流被手动关闭,这些对象此时仍然存在,并且exit(0)
立即终止进程。
尽管实际文件被关闭,流对象仍然存在,并且仍然有一些内存分配给它们,用于它们的内部流缓冲区。只有当这些对象被正确销毁时,内存才会被释放,这不会通过 exit(0)
.
发生
exit(0)
是一个标准的 C 库函数,它对任何 C++ 对象一无所知。它只是从高轨道上用核武器摧毁了这个过程。
valgrind
检测到子进程在没有完全释放它分配的所有内存的情况下终止,并报告。
至 "correctly use ifstreams in C++ with subprocesses and avoid leaks",子进程必须以与主进程相同的方式终止:从 main()
返回。这显然会在终止子进程之前销毁子进程中自动和静态范围内的所有对象。
就程序的逻辑工作方式而言,这显然提出了几个需要解决的难题。一种常见的、经常有效的蛮力方法是抛出异常而不是 exit()
ing,它会在 main()
中被捕获。当然,这只适用于在抛出异常时结构正确的代码。
我正在尝试创建一个比萨店模拟以了解有关子进程和线程的更多信息。我想最大程度地避免内存泄漏。
现在,当我只创建一个厨房时,没有错误,但是当我创建 2 个或更多时,我在销毁时遇到了 ifstreams 和 ofstreams 的一些泄漏。
有main():
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();
}
这是 valgrind 结果:
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 和这些标志编译: -墙 -Wextra -Weffc++
valgrind命令如下: valgrind --leak-check=full --show-leak-kinds=all ./plazza
显示的代码在子进程中执行以下操作:
void Kitchen::Quit(void)
{
CleaningTime();
_Ipipe.close();
_Opipe.close();
exit(0);
}
即使打开的流被手动关闭,这些对象此时仍然存在,并且exit(0)
立即终止进程。
尽管实际文件被关闭,流对象仍然存在,并且仍然有一些内存分配给它们,用于它们的内部流缓冲区。只有当这些对象被正确销毁时,内存才会被释放,这不会通过 exit(0)
.
exit(0)
是一个标准的 C 库函数,它对任何 C++ 对象一无所知。它只是从高轨道上用核武器摧毁了这个过程。
valgrind
检测到子进程在没有完全释放它分配的所有内存的情况下终止,并报告。
至 "correctly use ifstreams in C++ with subprocesses and avoid leaks",子进程必须以与主进程相同的方式终止:从 main()
返回。这显然会在终止子进程之前销毁子进程中自动和静态范围内的所有对象。
就程序的逻辑工作方式而言,这显然提出了几个需要解决的难题。一种常见的、经常有效的蛮力方法是抛出异常而不是 exit()
ing,它会在 main()
中被捕获。当然,这只适用于在抛出异常时结构正确的代码。