C语言 在发生错误时安全退出到特定状态



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

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()。在这种特殊情况下,如果文件的格式不正确,则在读取文件时将发生错误。

也许你在等待神圣和神圣的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);
}

Edit:出于与您所说的相同的原因,不希望在嵌入式系统上执行malloc()/free()。简直无法处理。除非你使用很多返回代码/setjmp() s来释放内存,直到堆栈…

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

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

assert()的实现可以像这样:

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

如果看门狗发生故障,系统将复位。重启时系统检查复位原因,若复位原因为看门狗复位,则系统进入安全状态

在进入while循环之前,assert也会输出错误信息,打印堆栈跟踪或将部分数据保存在非易失性存储器中。

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

是的,有一个行业实际的处理方法。这很简单:

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

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

void main (void)
{
  for(;;)
  {
    serve_watchdog();
    result = state_machine();
    if(result != good)
    {
      error_handler(result);
    }
  }
}

你不应该在裸机或RTOS微控制器应用程序中使用malloc,不是因为安全原因,而是因为使用它没有任何意义。编程时运用常识

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

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

最小化堆栈使用:

编写程序,使调用并行,而不是函数调用子函数,子函数调用子函数,子函数调用子函数....即顶级函数调用子函数,子函数立即返回状态信息。顶层函数然后调用下一个子函数…等

程序架构的嵌套方法:

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

相关内容

  • 没有找到相关文章

最新更新