出现错误时安全退出到特定状态
Safely Exiting to a Particular State in Case of Error
在编写代码时,我经常检查是否发生错误。一个例子是:
char *x = malloc( some_bytes );
if( x == NULL ){
fprintf( stderr, "Malloc failed.\n" );
exit(EXIT_FAILURE);
}
我过去也用过 strerror( errno )
。
我只编写过小型桌面应用程序,在这些应用程序中,程序是否 exit()
ed 并不重要,以防出现错误。
但是,现在我正在为嵌入式系统 (Arduino) 编写 C 代码,我不希望系统在出现错误时退出。我希望它转到特定的 state/function ,在那里它可以关闭系统电源、发送错误报告和安全空闲。
我可以简单地调用一个 error_handler()
函数,但我可能处于堆栈深处并且内存不足,导致 error_handler()
无法运行。
相反,我希望执行能够有效地折叠堆栈,释放大量内存并开始整理掉电和错误报告。如果系统不能安全关闭电源,则存在严重的火灾风险。
是否有在低内存嵌入式系统中实现安全错误处理的标准方法?
编辑 1:
我将限制在嵌入式系统中使用 malloc()
。在这种特殊情况下,如果文件格式不正确,则在读取文件时会发生错误。
使用setjmp(3)
设置恢复点,然后longjmp(3)
跳转到它,将堆栈恢复到setjmp点处的状态。它不会释放 malloced 内存。
一般在嵌入式程序中not a good idea to use malloc/free如果可以避免的话。例如,静态数组可能就足够了,甚至使用 alloca()
稍微好一些。
也许你正在等待神圣的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()
s。简直拿不准。除非你使用 很多 的 return 代码/setjmp()
来释放堆栈中的所有内存...
如果您的系统有看门狗,您可以使用:
char *x = malloc( some_bytes );
assert(x != NULL);
assert()
的实现可能是这样的:
#define assert (condition) \
if (!(condition)) while(true)
万一发生故障,看门狗会触发,系统会进行重置。重启时系统会检查重置原因,如果重置原因是"watchdog reset",系统会进入安全状态。
更新
在进入while
循环之前,assert
cold也会输出一条错误信息,打印堆栈跟踪或者在非易失性内存中保存一些数据。
尽量减少堆栈使用:
编写程序,使调用是并行的,而不是函数调用子函数调用子函数调用子函数.... I.E.顶级函数调用子函数,其中子函数立即 returns,带有状态信息。顶层函数然后调用下一个子函数...等等
程序架构的嵌套方法(不利于堆栈限制):
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:
注意,尽量没有'third level function 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
Is there a standard way that safe error handling is implemented in low memory embedded systems?
是的,有一种行业实际的处理方式。一切都很简单:
- 对于程序中的每个模块,您都需要有一个结果类型,例如自定义枚举,它描述了该模块内的函数可能出错的所有可能的事情。
- 您正确记录了每个函数,说明出错时 return 的代码以及成功时 return 的代码。
- 您将所有错误处理留给调用者。
- 如果调用者是另一个模块,它也会将错误传递给它自己的调用者。在适用的情况下,可能将错误重命名为更合适的名称。
- 错误处理机制位于调用堆栈底部的 main() 中。
这与经典状态机配合使用效果很好。一个典型的主要是:
void main (void)
{
for(;;)
{
serve_watchdog();
result = state_machine();
if(result != good)
{
error_handler(result);
}
}
}
您不应在裸机或 RTOS 微控制器应用程序中使用 malloc,与其说是出于安全原因,不如说是因为 it doesn't make any sense whatsoever 要使用它。编程时运用常识。
在编写代码时,我经常检查是否发生错误。一个例子是:
char *x = malloc( some_bytes );
if( x == NULL ){
fprintf( stderr, "Malloc failed.\n" );
exit(EXIT_FAILURE);
}
我过去也用过 strerror( errno )
。
我只编写过小型桌面应用程序,在这些应用程序中,程序是否 exit()
ed 并不重要,以防出现错误。
但是,现在我正在为嵌入式系统 (Arduino) 编写 C 代码,我不希望系统在出现错误时退出。我希望它转到特定的 state/function ,在那里它可以关闭系统电源、发送错误报告和安全空闲。
我可以简单地调用一个 error_handler()
函数,但我可能处于堆栈深处并且内存不足,导致 error_handler()
无法运行。
相反,我希望执行能够有效地折叠堆栈,释放大量内存并开始整理掉电和错误报告。如果系统不能安全关闭电源,则存在严重的火灾风险。
是否有在低内存嵌入式系统中实现安全错误处理的标准方法?
编辑 1:
我将限制在嵌入式系统中使用 malloc()
。在这种特殊情况下,如果文件格式不正确,则在读取文件时会发生错误。
使用setjmp(3)
设置恢复点,然后longjmp(3)
跳转到它,将堆栈恢复到setjmp点处的状态。它不会释放 malloced 内存。
一般在嵌入式程序中not a good idea to use malloc/free如果可以避免的话。例如,静态数组可能就足够了,甚至使用 alloca()
稍微好一些。
也许你正在等待神圣的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()
s。简直拿不准。除非你使用 很多 的 return 代码/setjmp()
来释放堆栈中的所有内存...
如果您的系统有看门狗,您可以使用:
char *x = malloc( some_bytes );
assert(x != NULL);
assert()
的实现可能是这样的:
#define assert (condition) \
if (!(condition)) while(true)
万一发生故障,看门狗会触发,系统会进行重置。重启时系统会检查重置原因,如果重置原因是"watchdog reset",系统会进入安全状态。
更新
在进入while
循环之前,assert
cold也会输出一条错误信息,打印堆栈跟踪或者在非易失性内存中保存一些数据。
尽量减少堆栈使用:
编写程序,使调用是并行的,而不是函数调用子函数调用子函数调用子函数.... I.E.顶级函数调用子函数,其中子函数立即 returns,带有状态信息。顶层函数然后调用下一个子函数...等等
程序架构的嵌套方法(不利于堆栈限制):
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:
注意,尽量没有'third level function 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
Is there a standard way that safe error handling is implemented in low memory embedded systems?
是的,有一种行业实际的处理方式。一切都很简单:
- 对于程序中的每个模块,您都需要有一个结果类型,例如自定义枚举,它描述了该模块内的函数可能出错的所有可能的事情。
- 您正确记录了每个函数,说明出错时 return 的代码以及成功时 return 的代码。
- 您将所有错误处理留给调用者。
- 如果调用者是另一个模块,它也会将错误传递给它自己的调用者。在适用的情况下,可能将错误重命名为更合适的名称。
- 错误处理机制位于调用堆栈底部的 main() 中。
这与经典状态机配合使用效果很好。一个典型的主要是:
void main (void)
{
for(;;)
{
serve_watchdog();
result = state_machine();
if(result != good)
{
error_handler(result);
}
}
}
您不应在裸机或 RTOS 微控制器应用程序中使用 malloc,与其说是出于安全原因,不如说是因为 it doesn't make any sense whatsoever 要使用它。编程时运用常识。