【发布时间】:2023-01-04 23:27:51
【问题描述】:
我正在使用我自己的使用 librpm library 的代码来模拟 rpm -qa。我这样做是为了对一个更大的程序进行初步实验,该程序将出于安全目的分析已安装的软件。
现在,我只打开 RPM DB 并在不读取任何内容的情况下将其关闭。
当我比较我代码的 valgrind 输出和 rpm -qa 的 valgrind 输出时,结果如下:
$ valgrind ./泄漏 ==8201== Memcheck,内存错误检测器 ==8201== 版权所有 (C) 2002-2017,以及 Julian Seward 等人的 GNU GPL'd。 ==8201== 使用 Valgrind-3.18.1 和 LibVEX;使用 -h 重新运行以获得版权信息 ==8201== 命令:./leaky ==8201== ==8201== ==8201== 堆摘要: ==8201== 在退出时使用:2,352 个块中的 104,700 字节 ==8201== 堆总使用量:10,430 次分配,8,078 次释放,分配了 2,292,650 字节 ==8201== ==8201== 泄漏摘要: ==8201== 绝对丢失:0 个块中的 0 个字节 ==8201== 间接丢失:0 个块中的 0 个字节 ==8201== 可能丢失:325 个块中的 25,740 个字节 ==8201== 仍可访问:2,027 个块中的 78,960 字节 ==8201== 抑制:0 个块中的 0 个字节 ==8201== 重新运行 --leak-check=full 以查看泄漏内存的详细信息 ==8201== ==8201== 对于检测到的和抑制的错误列表,重新运行:-s ==8201== 错误摘要:0 个上下文中的 0 个错误(抑制:0 个中的 0 个)
$ valgrind rpm -qa > /dev/null ==8101== Memcheck,内存错误检测器 ==8101== 版权所有 (C) 2002-2017,以及 Julian Seward 等人的 GNU GPL'd。 ==8101== 使用 Valgrind-3.18.1 和 LibVEX;使用 -h 重新运行以获得版权信息 ==8101== 命令:rpm -qa ==8101== ==8101== ==8101== 堆摘要: ==8101== 在退出时使用:2 个块中的 287 个字节 ==8101== 总堆使用量:170,103 allocs,170,101 frees,120,309,981 bytes allocated ==8101== ==8101== 泄漏摘要: ==8101== 绝对丢失:0 个块中的 0 个字节 ==8101== 间接丢失:0 个块中的 0 个字节 ==8101== 可能丢失:0 个块中的 0 个字节 ==8101== 仍然可达:2 个块中的 287 个字节 ==8101== 抑制:0 个块中的 0 个字节 ==8101== 重新运行 --leak-check=full 以查看泄漏内存的详细信息 ==8101== ==8101== 对于检测到的和抑制的错误列表,重新运行:-s ==8101== 错误摘要:0 个上下文中的 0 个错误(抑制:0 个中的 0 个)
如您所见,我的程序可能丢失了 25,740 个字节,而 rpm -qa 丢失了 0 个字节。
这是我的代码:
#include <rpm/rpmdb.h>
#include <rpm/rpmlib.h>
#include <rpm/rpmts.h>
bool openDb(rpmts & ts, rpmdbMatchIterator & mi);
void closeDb(rpmts & ts, rpmdbMatchIterator & mi);
int main()
{
rpmts ts;
rpmdbMatchIterator mi;
if (!openDb(ts, mi)) {
return 1;
}
closeDb(ts, mi);
return 0;
}
bool openDb(rpmts & ts, rpmdbMatchIterator & mi)
{
{
static volatile bool s_bHereBefore = false;
if (!s_bHereBefore) {
s_bHereBefore = true;
rpmReadConfigFiles(NULL, NULL);
}
}
mi = NULL;
ts = rpmtsCreate();
if (!ts) {
printf("RPM open failed\n");
} else {
mi = rpmtsInitIterator(ts, (rpmTag)RPMDBI_PACKAGES, NULL, 0);
if (!mi) {
printf("RPM iterator failed\n");
rpmtsFree(ts);
}
}
return mi != NULL;
}
void closeDb(rpmts & ts, rpmdbMatchIterator & mi)
{
mi = rpmdbFreeIterator(mi);
if (ts) {
rpmtsFree(ts);
}
}
我用g++ -Wall -Wextra -Wunused -Og -g try_to_fix_mem_leak.cpp -lrpm -o leaky编译。
我仔细检查了我的程序,但我无法通过手动检查发现任何内存泄漏。
当我运行 valgrind --leak-check=full ./leaky 并在输出中搜索 try_to_fix_mem_leak.cpp 时,所有的命中都针对第 27 行,即 rpmReadConfigFiles(NULL, NULL); 行(从技术上讲,第 13 行也有命中,但这只是因为那是openDb 在main 中进行调用)。 (请参阅下面的 pastebin 链接。)但我不知道这一行如何导致任何内存泄漏。 The function's documentation for my version of librpm (4.16.1) 没有提到需要释放任何内存。
如何在不泄漏内存的情况下正确打开和关闭 RPM DB?或者,换句话说,我如何打开和关闭 RPM 数据库,而最坏情况下泄漏的字节数与 rpm -qa 一样多?
编辑
pastebin link,完整输出为valgrind --leak-check=full ./leaky。
【问题讨论】:
-
您可以将其他选项传递给 valgrind,这些选项将准确报告泄漏内存的分配位置,并为您指明正确的方向。查看 valgrind 的文档以获取更多信息。
-
@SamVarshavchik,我尝试将 valgrind 与
--leak-check=full一起使用,但我看不出它指向的行是如何泄漏的。请查看我更新的问题,特别是有关--leak-check=full和rpmReadConfigFiles的段落。 -
如果您只是在程序退出时泄漏内存,那么这很可能并不重要 - 除非这意味着重要的析构函数没有运行,这些析构函数没有运行。一旦可执行文件退出,内核就会清理全部它是内存(包括你泄露的内容)。实际上;通过避免不必要的对象破坏和内存释放,故意泄漏内存可能是允许应用程序快速关闭的有效策略。
-
@JesperJuhl,释放内存后我在我的程序中做的最后一件事是退出(参见我的
main函数的最后两行),所以如果我的程序退出时有任何未释放的内存,那么对我来说这意味着我我正在泄漏内存。 -
@ShaneBishop 您只能在程序运行时泄漏内存。内存泄漏是一个问题,因为内存使用会随着时间的推移不断增加。一旦您的程序退出,内核就会清除它曾经分配的所有内容(即使您没有)。内存泄漏不会在多个应用程序启动/终止时持续存在。
标签: c++ linux memory-leaks rpm