2010-03-01 38 views
3

我在Linux(Fedora)上使用代码魔法工具链构建了一个简单的C代码。这是针对ARM Cortex-A8目标的。该代码运行在运行嵌入式Linux的Cortex A8板上。如果独立运行,gdb下的Linux上的C代码运行方式不同吗?

当运行一段测试情况下,确实对于一些大尺寸(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. 我该如何解决这个问题?

感谢,

〜AD

+0

为什么你说你期望你的10MB'malloc'失败呢?系统上没有那么多的内存? – vladr 2010-03-01 03:20:10

+0

你提到的那个错误信息是什么? – 2010-03-01 03:32:19

+0

错过了OP中的错误消息,现在编辑添加相同。 – goldenmean 2010-03-01 04:22:15

回答

6

因此,对于这部分的质询,有一个万无一失的答案:

为什么会失败的malloc尚未返回NULL。这可能是可能的,或者我得到的错误信息的原因是其他吗?

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

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

为什么在gdb下运行时以及没有调试器时,这种行为会有所不同?
这可能(只是一个猜测)GDB有自己的 malloc,并以某种方式跟踪你的分配。在一个相关的问题上,我经常发现我的代码中的堆错误在调试器中通常是不可重现的。这是令人沮丧的,并让我挠我的脑袋,但它基本上是我几乎想到一个人必须忍受的东西...

我该如何解决这个问题?

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

不及格...我只是确保你没有超出预期的分配。

+2

对于像这样的嵌入式系统,运行严格控制的一组进程,关闭overcommit可能是正确的运行方式。如果你有一个合理的标准启动顺序,你可以在'/etc/sysctl.conf'中修改永久。 – caf 2010-03-01 04:33:54

2

按照定义在调试器下运行与运行独立不同。调试器可以隐藏许多错误。如果编译进行调试,则可以添加相当多的代码,类似于完全未优化的编译(例如,允许您单步执行或监视变量)。在编译版本时,可以删除调试选项并删除所需的代码,但有许多优化陷阱可以使用。我不知道你的帖子是谁在控制编译选项或它们是什么。

除非您计划交付要在调试器下运行的产品,否则您应该单独进行测试。理想情况下,在没有调试器的情况下也可以进行开发,从而使您不必再做任何事情。

这听起来像是你的代码中的一个错误,用新的眼睛慢慢重新读你的代码,就好像你是在向某人解释它,或者实际上可能是逐行解释给别人。可能有些东西是你无法看到的,因为你一直以相同的方式看太久。这是惊人的多少次,以及如何工作。

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

这是一个经过验证的系统,还是您正在开发新硬件?例如,尝试在没有启用任何缓存的情况下运行。在调试器中工作而不是单独使用,如果不是编译器错误可能是时序问题,单步冲洗管道,以不同方式混合高速缓存,给高速缓存和内存系统永恒以产生它不具有的结果实时。

简而言之,为什么在调试器下运行隐藏了在最终可交付的环境中测试之前无法找到的错误,我只涉及了一些问题。让它在调试器中工作而不是单独工作并不意外,它只是工具的工作方式。这可能是您的代码,硬件或基于迄今给出的描述的工具。

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

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

+0

@dwelch:感谢所有的指针。我已更新错误消息。如果它有助于进一步缩小,我在OP中搞砸了。 – goldenmean 2010-03-01 04:31:33