如果单独运行,在gdb下Linux上的C代码运行方式会有所不同?

时间:2010-03-01 03:17:57

标签: c linux embedded gdb

我使用code-sorcery工具链在Linux(Fedora)上构建了一个简单的C代码。这适用于ARM Cortex-A8目标。此代码在运行嵌入式Linux的Cortex A8主板上运行。

当我为某些大型(10MB)进行动态内存分配(malloc)的测试用例运行此代码时,它会在一段时间后崩溃,并给出错误消息,如下所示:

select 1 (init), adj 0, size 61, to kill
select 1030 (syslogd), adj 0, size 64, to kill
select 1032 (klogd), adj 0, size 74, to kill
select 1227 (bash), adj 0, size 378, to kill
select 1254 (ppp), adj 0, size 1069, to kill
select 1255 (TheoraDec_Corte), adj 0, size 1159, to kill
send sigkill to 1255 (TheoraDec_Corte), adj 0, size 1159
Program terminated with signal SIGKILL, Killed.

然后,当我使用为目标构建的gdb为相同的测试用例调试此代码时,发生此动态内存分配的点,代码无法分配该内存,malloc返回NULL。但是在正常的独立运行期间,我认为malloc应该无法分配,但奇怪的是它可能不会返回NULL,但它会崩溃并且操作系统会终止我的进程。

  1. 为什么在gdb和没有调试器的情况下运行时这种行为会有所不同?
  2. 为什么malloc失败但却没有返回NULL。这可能是可能的,或者我得到的错误消息的原因是否是其他?
  3. 我该如何解决这个问题?
  4. 感谢,

    -AD

2 个答案:

答案 0 :(得分:6)

因此,对于这部分问题,有一个肯定的答案:

为什么malloc失败但没有返回NULL。这可能是可能的,或者我得到错误消息的原因是否是其他?

在Linux中,默认情况下,用于分配内存的内核接口几乎不会彻底失败。相反,他们以这样的方式设置你的page table,在第一次访问你要求的内存时,CPU将生成page fault,此时内核处理这个并查找物理内存将用于该(虚拟)页面。因此,在内存不足的情况下,你可以向内核询问内存,它会“成功”,并且当你第一次尝试触摸它返回的内存时,这个就是分配实际上失败了,杀了你的过程。 (或许是其他一些不幸的受害者。有一些启发式方法,我对此并不十分熟悉。请参阅“oom-killer”。)

你的其他一些问题,答案对我来说不太清楚。

为什么在gdb和没有调试器的情况下运行时这种行为会有所不同?
可能(实际上只是猜测)GDB有自己的malloc,并且以某种方式跟踪您的分配。在某种程度上相关的观点,我实际上经常发现我的代码中的堆错误通常不能在调试器下重现。这令我感到沮丧,让我挠头,但这基本上是我认为必须忍受的东西......

我如何解决这个问题?

这是一个大锤解决方案(也就是说,它改变了所有进程的行为,而不仅仅是你自己的进程,并且让你的程序改变全局状态通常不是一个好主意但是,您可以将字符串2写入/proc/sys/vm/overcommit_memory。请参阅我从Google搜索中获得的this link

失败了......我只是确保你没有超出预期分配。

答案 1 :(得分:2)

根据定义,在调试器下运行与独立运行不同。调试器可以并且确实隐藏了许多错误。如果编译进行调试,则可以添加相当数量的代码,类似于完全未优化的编译(例如,允许您单步执行或查看变量)。在为发布进行编译的情况下,可以删除调试选项并删除所需的代码,您可以使用许多优化陷阱。我不知道你的帖子是谁控制编译选项或它们是什么。

除非您计划在调试器下运行要运行的产品,否则您应该独立进行测试。理想情况下,在没有调试器的情况下进行开发,可以使您不必再做两次。

这听起来像是你的代码中的一个错误,慢慢地用新的眼睛重新阅读你的代码,好像你是在向别人解释它,或者实际上是一行一行地解释它。那里可能有一些你看不到的东西,因为你长时间以同样的方式看待它。令人惊奇的是,它的次数和效果如何。

我也可能是编译器错误。执行诸如打印返回值之类的操作可能会导致编译器生成不同的代码。添加另一个变量并将结果保存到该变量可以使编译器执行不同的操作。尝试更改编译器选项,减少或删除任何优化选项,减少或删除调试器编译器选项等。

这是一个经过验证的系统还是您正在开发新硬件?尝试在没有启用任何缓存的情况下运行。在调试器中工作而不是独立工作,如果不是编译器错误可能是一个时间问题,单步执行刷新pipline,以不同方式混合缓存,给缓存和内存系统永恒提出一个它没有的结果实时。

简而言之,有一个很长的原因列表,为什么在调试器下运行会隐藏在最终可交付环境中进行测试之前无法找到的错误,我只谈了几个。让它在调试器中运行而不是独立运行并不意外,它只是工具的工作方式。根据您目前所提供的描述,可能是您的代码,硬件或工具。

消除它作为代码或工具的最快方法是反汇编部分并检查传递的值和返回值的处理方式。如果返回值已经优化,那么就有答案。

您是在为共享C库还是静态编译?也许为静态编译...