3

在编写代码时,我经常检查是否发生错误。一个例子是:

char *x = malloc( some_bytes ); 
if( x == NULL ){
    fprintf( stderr, "Malloc failed.\n" ); 
    exit(EXIT_FAILURE); 
}

我过去也用过strerror( errno )

我只写过小型桌面应用程序exit(),如果出现错误,程序是否编辑并不重要。

但是,现在我正在为嵌入式系统 (Arduino) 编写 C 代码,并且我不希望系统在出现错误时退出。我希望它进入一个特定的状态/功能,它可以关闭系统、发送错误报告和安全地空闲。

我可以简单地调用一个error_handler()函数,但我可能在堆栈中很深并且内存非常低,导致error_handler()无法操作。

相反,我希望执行能够有效地折叠堆栈,释放大量内存并开始整理掉电和错误报告。如果系统不能安全关闭电源,则存在严重的火灾风险。

是否有在低内存嵌入式系统中实现安全错误处理的标准方法?

编辑 1:我将限制我malloc()在嵌入式系统中的使用。在这种特殊情况下,如果文件格式不正确,则在读取文件时会发生错误。

4

5 回答 5

1

如果您的系统有看门狗,您可以使用:

char *x = malloc( some_bytes ); 
assert(x != NULL);

的实现assert()可能是这样的:

#define assert (condition) \
    if (!(condition)) while(true)

万一发生故障,看门狗将触发,系统将进行重置。系统重启时会检查复位原因,如果复位原因是“看门狗复位”,系统会进入安全状态。

更新

在进入while循环之前,assertcold 还会输出错误信息,打印堆栈跟踪或将一些数据保存在非易失性内存中。

于 2015-09-12T09:43:48.283 回答
1

是否有在低内存嵌入式系统中实现安全错误处理的标准方法?

是的,有一种行业事实上的处理方式。这一切都很简单:

  • 对于程序中的每个模块,您都需要有一个结果类型,例如自定义枚举,它描述了该模块中的函数可能出错的所有可能的事情。
  • 您正确记录每个函数,说明出错时将返回哪些代码以及成功时将返回哪些代码。
  • 您将所有错误处理留给调用者。
  • 如果调用者是另一个模块,它也会将错误传递给它自己的调用者。可能在适用的情况下将错误重命名为更合适的名称。
  • 错误处理机制位于调用堆栈底部的 main() 中。

这与经典状态机一起工作得很好。一个典型的主要是:

void main (void)
{
  for(;;)
  {
    serve_watchdog();

    result = state_machine();

    if(result != good)
    {
      error_handler(result);
    }
  }
}

您不应该在裸机或 RTOS 微控制器应用程序中使用 malloc,与其说是出于安全原因,不如说是因为使用它没有任何意义。编程时应用常识。

于 2015-09-14T06:40:16.813 回答
1

也许你正在等待神圣和神圣的setjmp/longjmp,那个来拯救所有渴望记忆的罪恶堆栈的人?

#include <setjmp.h>

jmp_buf jumpToMeOnAnError;
void someUpperFunctionOnTheStack() {
    if(setjmp(jumpToMeOnAnError) != 0) {
        // Error handling code goes here

        // Return, abort(), while(1) {}, or whatever here...
    }

    // Do routinary stuff
}

void someLowerFunctionOnTheStack() {
    if(theWorldIsOver)
       longjmp(jumpToMeOnAnError, -1);
}

编辑:不喜欢在嵌入式系统上做malloc()/ free()s,原因与您所说的相同。简直是不可理喻。除非你使用大量的返回码/ setjmp()s 来释放内存一直到堆栈......

于 2015-09-12T09:38:06.190 回答
0

用于setjmp(3)设置恢复点并longjmp(3)跳转到它,将堆栈恢复到 setjmp 点的状态。它不会释放分配的内存。

一般来说,如果可以避免的话,在嵌入式程序中使用 malloc/free 并不是一个好主意。例如,静态数组可能就足够了,甚至使用alloca()稍微好一点。

于 2015-09-12T09:37:56.007 回答
0

最小化堆栈使用:

编写程序以便调用是并行的,而不是函数调用子函数调用子函数调用子函数.... IE 顶级函数调用子函数,其中子函数立即返回,并带有状态信息。顶级函数然后调用下一个子函数......等

程序架构的(不利于堆栈限制)嵌套方法:

top level function
    second level function
        third level function
            forth level function

在嵌入式系统中应该避免

嵌入式系统程序架构的首选方法是:

top level function (the reset event handler)
    (variations in the following depending on if 'warm' or 'cold' start)
    initialize hardware
    initialize peripherals
    initialize communication I/O
    initialize interrupts
    initialize status info
    enable interrupts
    enter background  processing

interrupt handler
    re-enable the interrupt
    using 'scheduler' 
        select a foreground function 
        trigger dispatch for selected foreground function        
    return from interrupt

background processing 

(this can be, and often is implemented as a 'state' machine rather than a loop)
    loop:
        if status info indicates need to call second level function 1 
            second level function 1, which updates status info
        if status info indicates need to call second level function 2
            second level function 2, which updates status info
        etc
    end loop:

请注意,尽可能不存在“第三级函数 x”

请注意,前台功能必须在再次调度之前完成。

注意:我在上面省略了很多其他细节,比如

kicking the watchdog, 
the other interrupt events,
'critical' code sections and use of mutex(),
considerations between 'soft real-time' and 'hard real-time',
context switching
continuous BIT, commanded BIT, and error handling 
etc
于 2015-09-13T17:23:09.630 回答