假设我在嵌入式环境中有一个协作调度程序。我有很多进程在运行。我想利用看门狗定时器,以便我可以检测进程何时因任何原因停止运行并重置处理器。
在没有RTOS的简单应用程序中,我总是会从主循环接触看门狗,这总是足够的。但是,在这里,有许多进程可能会挂起。什么是定期触摸看门狗定时器的干净方法,同时确保每个过程都处于良好状态?
我在想我可以为每个进程提供一个回调函数,这样它就可以让另一个监督所有进程的函数知道它仍然存在。回调将传递一个参数,该参数将是任务唯一ID,因此监督者可以确定谁正在回叫。
答案 0 :(得分:19)
一种常见的方法是将看门狗委托给特定任务(通常是最高优先级或最低优先级,每种方法的权衡/动机),然后让所有其他任务“检入”此任务。
这样:
如果中断挂起(100%CPU),踢球者任务将无法运行,您重置
如果踢球者任务挂起,则重置
如果另一个任务挂起,踢球者任务没有检查,踢球者任务没有踢WDG,你重置
现在有一些实施细节需要考虑。有些人让每个任务在全局变量中设置自己的专用位(原子地);踢球者任务以特定的速率检查这组位标志,并在每个人都签入时清除/重置(当然还有踢WDG。)我避开像瘟疫一样的全局变量并避免这种方法。 RTOS事件标志提供了一种更优雅的机制。
我通常将我的嵌入式系统设计为事件驱动系统。在这种情况下,每个任务都会阻塞在一个特定的位置 - 在消息队列中。所有任务(和ISR)通过发送事件/消息相互通信。通过这种方式,你不必担心任务没有检查,因为它被阻塞在信号量“那里”(如果这没有意义,对不起,没有写更多我无法解释它更好)。
还有考虑因素 - 执行任务“自主”检查或者回复/响应来自踢球者任务的请求。自治 - 例如,每秒一次,每个任务在其队列中接收一个事件“告诉踢球者任务你还活着”。回复请求 - 每秒一次(或其他),踢球任务告诉每个人(通过队列)“检查时间” - 并最终每个任务运行其队列,获取请求并回复。任务优先级,排队理论等的考虑适用。
有100种方法可以对这只猫进行换肤,但是单一任务的基本原则是负责踢踢WDG并让其他任务漏斗到踢球者任务的标准。
至少还有一个方面需要考虑 - 在这个问题的范围之外 - 而且正在处理中断。如果ISR正在占用CPU,那么上面描述的方法将触发WDG复位(好),但是相反的情况如何 - 一个ISR(可悲地)变得偶然和无意中被禁用。在许多情况下,这不会被捕获,并且您的系统仍然会启动WDG,但系统的一部分仍然瘫痪。有趣的东西,这就是我喜欢嵌入式开发的原因。
答案 1 :(得分:5)
一种解决方案模式:
这样,任何永远不会返回健康状态的线程都会停止看门狗任务,直到发生硬件看门狗超时。
在抢先式操作系统中,监视程序线程将是最低优先级或空闲线程。在协作调度程序中,它应该在回调调用之间产生。
回调函数本身的设计取决于特定任务及其行为和周期性。每个功能都可以根据任务的需要和特点进行定制。高周期性的任务可能只是增加一个计数器,当调用回调时,该计数器设置为零。如果计数器在输入时为零,则自上次看门狗检查后该任务没有计划。具有低或非周期性行为的任务可能会对其调度加时间戳,如果在某个指定时间段内未安排任务,则回调可能会返回失败。可以通过这种方式监视任务和中断处理程序。此外,因为线程负责向看门狗注册,所以你可能有一些线程根本没有注册。
答案 2 :(得分:1)
传统方法是使用具有最低优先级的监督程序
PROCESS(watchdog, PRIORITY_LOWEST) { while(1){reset_timer(); sleep(1);} }
实际硬件定时器可能每隔3或5秒重置一次CPU。
跟踪单个进程可以通过反向逻辑实现:每个进程都会设置一个计时器,其回调会向监视程序发送“停止”消息。然后,每个进程都需要取消之前的计时器事件,并在“从队列中接收事件/消息”循环中的某处设置一个新事件。
PROCESS(watchdog, PRIORITY_LOWEST) {
while(1) {
if (!messages_in_queue()) reset_timer();
sleep(1);
}
}
void wdg_callback(int event) {
msg = new Message();
send(&msg, watchdog);
};
PROCESS(foo, PRIORITY_HIGH) {
timer event=new Timer(1000, wdg_callback);
while (1) {
if (receive(msg, TIMEOUT)) {
// handle msg
} else { // TIMEOUT expired
cancel_event(event);
event = new Timer(1000,wdg_callback);
}
}
}
答案 3 :(得分:1)
每个任务都应该有自己的模拟监视程序。只有当所有模拟监视器都没有超时时,真正的监视程序才会被高优先级的实时任务提供。
即:
void taskN_handler()
{
watchdog *wd = watchdog_create(100); /* Create an simulated watchdog with timeout of 100 ms */
/* Do init */
while (task1_should_run)
{
watchdog_feed(wd); /* feed it */
/* do stuff */
}
watchdog_destroy(wd); /* destroy when no longer necessary */
}
void watchdog_task_handler()
{
int i;
bool feed_flag = true;
while(1)
{
/* Check if any simulated watchdog has timeout */
for (i = 0; i < getNOfEnabledWatchdogs(); i++)
{
if (watchogHasTimeout(i)) {
feed_flag = false;
break;
}
}
if (feed_flag)
WatchdogFeedTheHardware();
task_sleep(10);
}
现在,可以说系统真的受到保护,不会冻结,甚至不会冻结,而且大多数情况下都没有不需要的看门狗触发器。
答案 4 :(得分:0)
其他答案已经涵盖了您的问题,我只是建议您在旧程序中添加一些内容(不使用RTOS)。不要只从main()无条件地启动看门狗,有些ISR可能会卡住,但系统会继续工作而不通知(Dan提到的问题也与RTOS有关)。
我一直在做的是关联主要和定时器中断,以便在定时器内对变量进行倒计时,直到它为零,并从主要我检查它是否为零,然后才喂养看门狗。当然,喂食后将变量返回初始值。很简单,如果变量停止递减,则会得到重置。如果主要停止为看门狗供电,则会重置。
这个概念很容易仅适用于已知的周期性事件,但它仍然比从主要事件做的更好。另一个好处是,乱码不太可能导致看门狗,因为主要内部的看门狗馈送程序已经在一些狂野的循环中结束。