本文档依据当前 libstt_core 工程中的以下实现编写:
src/util/Util.hsrc/util/Util.cppsrc/base/Interface.cppsrc/misc/misc.cpp本文所述接口适用于当前运行库版本。其他版本的固件或 libstt_core.so 可能存在差异,部署前应使用目标工程实际携带的头文件和运行库复核。
看门狗用于检测 HMI 主循环是否长时间无法运行。
正常情况下,运行库会周期性向 Linux 看门狗设备 /dev/watchdog 发送保活信号,通常称为“喂狗”。如果主循环因为死循环、同步大数据操作、死锁或其他严重阻塞而在指定时间内没有继续喂狗,硬件或内核看门狗会触发设备复位。
看门狗适合处理以下故障:
看门狗不能直接解决以下问题:
看门狗只能在故障发生后尝试让设备复位,不能替代异步设计、业务超时、错误处理、数据库事务和数据备份。
#include "util/Util.h"
using stt::util::Util;
公开接口只有一个:
static bool Util::enableWatchdog(int timeout);
客户程序不需要也不应该自行打开 /dev/watchdog、调用 ioctl 或编写喂狗线程。打开设备和周期喂狗由 HmiAppBase 内部完成。
timeout 的单位为秒。
| 参数值 | 含义 |
|---|---|
0 |
将看门狗配置为关闭 |
1..100 |
配置看门狗超时时间 |
小于 0 |
参数非法,返回 false |
大于 100 |
参数非法,返回 false |
建议现场初始值使用 30 秒或 60 秒。不要未经验证直接使用 1..5 秒等很短的时间。
| 返回值 | 含义 |
|---|---|
true |
参数有效,并且看门狗配置值写入成功 |
false |
参数越界,或者 ARM 环境下配置写入失败 |
必须注意:返回 true 只证明配置写入成功,不证明以下事项:
/dev/watchdog 已经成功打开。当前实现没有公开“看门狗是否已打开”或“驱动实际采用的超时时间”查询接口。因此最终必须在 ARM 真机上验证。
调用:
Util::enableWatchdog(30);
ARM 真机上会把字符串 "30" 写入名为 watchdog 的持久化环境配置。设置 0 时会写入字符串 "0"。
该方法本身不会直接打开 /dev/watchdog,也不会启动独立喂狗线程。
在 ARM 应用完成主要初始化、即将进入 HMI 主循环之前,HmiAppBase 会读取 watchdog 配置:
0 时,打开 /dev/watchdog。WDIOC_SETTIMEOUT 设置超时时间。0、配置不存在或设备打开失败时,不执行自动喂狗。如果在应用的 onInit() 或 onReady() 中设置看门狗,当前实现会在随后进入主循环前读取到新值。但不建议每次开机都重复写同一个持久化配置,通常应在设备设置页面或首次安装流程中只写一次。
如果在按钮事件、页面定时器或其他运行期回调中修改配置,当前已经打开的看门狗不会被立即重新打开或重新设置。新配置应按“下一次应用启动生效”理解。
运行库在 HMI 主循环中执行以下工作:
WDIOC_KEEPALIVE。因此,看门狗实际监控的是 HMI 主循环能否继续运行。喂狗周期不是独立的精确定时器;如果某个 UI 回调阻塞了主循环,喂狗也会一起停止。
HMI 主循环正常结束后,运行库会关闭看门狗文件描述符。关闭设备后是否立即停用还受目标内核驱动的 nowayout、magic close 等策略影响,不能只根据用户态代码推断。现场应以目标固件的实际测试结果为准。
#include "util/Util.h"
using stt::util::Util;
bool ok = Util::enableWatchdog(30);
if (!ok) {
// 参数错误或ARM配置写入失败
}
如果这段代码在正常运行期间由按钮触发,应提示用户重新启动应用或设备,然后再验证看门狗是否生效。
bool ok = Util::enableWatchdog(0);
if (!ok) {
// ARM配置写入失败
}
运行期调用 enableWatchdog(0) 只会修改持久化配置,不会关闭当前实例已经打开的 /dev/watchdog。应正常重启应用或设备,让新配置在下次启动时生效。
不要通过强制杀进程来尝试关闭看门狗。若内核启用了 nowayout,进程退出后可能因停止喂狗而触发复位。
bool ok = Util::enableWatchdog(60);
如果当前实例已经使用 30 秒看门狗,运行期写入 60 并不会保证当前设备句柄立刻变成 60 秒。按下一次应用启动采用 60 秒处理。
下面示例适合放在“系统设置”页面的按钮事件中。假设工程已经建立 T_VCHAR 变量 系统提示。
#include "util/Util.h"
void FrmSystem::wMEnableWatchdog_clk_cb(uint16_t code, LvEvent e) {
const int timeoutSeconds = 30;
bool ok = stt::util::Util::enableWatchdog(timeoutSeconds);
if (ok) {
vm->setChar("系统提示", "看门狗已设为30秒,重启后生效");
} else {
vm->setChar("系统提示", "看门狗设置失败");
}
}
void FrmSystem::wMDisableWatchdog_clk_cb(uint16_t code, LvEvent e) {
bool ok = stt::util::Util::enableWatchdog(0);
if (ok) {
vm->setChar("系统提示", "看门狗已设为关闭,重启后生效");
} else {
vm->setChar("系统提示", "关闭配置写入失败");
}
}
#include "util/StringUtil.h"
#include "util/Util.h"
void FrmSystem::applyWatchdog(const std::string& input) {
int timeout = stt::util::StringUtil::safeStoi(input);
if (timeout < 0 || timeout > 100) {
vm->setChar("系统提示", "超时时间必须为0到100秒");
return;
}
if (!stt::util::Util::enableWatchdog(timeout)) {
vm->setChar("系统提示", "看门狗配置保存失败");
return;
}
if (timeout == 0) {
vm->setChar("系统提示", "已设置为关闭,重启后生效");
} else {
vm->setChar(
"系统提示",
"已设置为" + std::to_string(timeout) + "秒,重启后生效");
}
}
如果输入框允许空字符串,必须先单独判断空值。safeStoi("") 通常会得到 0,若直接使用可能把“没有输入”误解释为“关闭看门狗”。
超时时间应大于主线程中所有合法操作的最长阻塞时间,并保留足够余量。
可采用以下经验值:
看门狗超时 >= 已知最长合法主线程阻塞时间的 2~3 倍
例如,经过实测,最慢的页面切换和本地配置读取可能阻塞 6 秒,则不应设置成 5 秒;可以从 20 秒或 30 秒开始验证。
| 场景 | 建议初始值 | 说明 |
|---|---|---|
| 普通 HMI 控制项目 | 30 秒 | 兼顾恢复速度和瞬时负载 |
| 数据库、U盘、SD卡操作较多 | 60 秒 | 仍应把耗时操作改为异步 |
| 调试阶段 | 60..100 秒或暂时关闭 | 避免断点暂停时设备反复复位 |
| 已完成压力测试的封闭设备 | 按实测确定 | 不应只凭经验缩短 |
不建议把 100 秒当作解决卡顿的方法。如果一个同步导入需要两分钟,把看门狗改成 100 秒仍可能复位,而且界面会持续无响应。正确做法是把导入改成后台异步、分批处理,并给业务任务增加超时和进度反馈。
耗时操作应放到 AsynWorker 或当前 SDK 支持的后台任务机制中。后台线程禁止直接操作 UI 控件;应在完成回调中更新 UI,或者把结果写入组态变量。
AsynWorker* worker = new AsynWorker(
getRuntime()->getAppInstance(std::this_thread::get_id()));
worker->invoke([]() {
// 后台执行文件、数据库或网络操作
// 禁止在这里直接操作页面和控件
})->onFinished([this]() {
// 回到应用线程后更新UI
vm->setChar("任务状态", "处理完成");
});
worker->start();
异步任务带来两个重要结果:
所以后台任务仍应设置网络超时、文件大小上限、数据库分页或批量上限,以及取消机制。看门狗不能替代这些业务保护。
不要另建一个与 UI 无关的线程持续调用 /dev/watchdog。否则即使界面和业务主线程完全死锁,独立线程仍可能继续喂狗,设备将无法自动恢复。
以下写法是错误的:
void HmiApp::loop() {
stt::util::Util::enableWatchdog(30); // 错误:反复写持久化配置
}
enableWatchdog 是配置接口,不是喂狗接口。高频调用没有必要,还可能增加持久化存储写入次数。
调试器暂停 UI 主线程后,自动喂狗会停止。达到超时时间后真机可能复位,调试连接会断开。单步调试前应在受控条件下暂时关闭看门狗并正常重启,完成调试后再重新启用。
复位可能发生在数据库提交、CSV 写入、配方保存或参数持久化过程中。重要数据应使用临时文件、事务、原子替换、校验和恢复机制。不能因为启用了看门狗就认为数据不会损坏。
模拟器中的 Util::enableWatchdog() 直接返回 true,但不会写配置、打开设备或产生复位。因此看门狗只能在 ARM 真机上完成最终验证。
libstt_core.so 属于同一版本。Util::enableWatchdog(30),确认返回 true。只有在测试设备上才能执行以下验证:
实验室测试代码示例:
void FrmTest::wMWatchdogTest_clk_cb(uint16_t code, LvEvent e) {
// 仅限无生产数据、无设备动作的专用测试机。
// timeout=15秒时,阻塞30秒应使主循环停止喂狗并触发复位。
stt::util::Util::sleepfor(30000);
}
禁止把这段测试代码交付到生产工程。若测试后设备没有复位,应检查设备节点、驱动支持、权限、运行库日志和固件配置,不能只根据 enableWatchdog() 的返回值判断硬件看门狗已经生效。
Util::enableWatchdog(0)。true,但设备不复位可能原因:
/dev/watchdog 不存在或无法打开。当前库在打开 /dev/watchdog 失败时会记录 open Watchdog error。需要结合真机运行日志检查。
可能原因:
sleep。处理顺序:先定位主线程阻塞点并改为异步,再评估超时时间。不要只把超时时间不断调大。
0 后仍发生复位可能原因:
nowayout,已经启动的看门狗不能由普通关闭立即停用。需要。异步可以避免正常长任务阻塞主循环,看门狗用于兜底处理整个应用主循环失去响应。两者作用不同,应同时采用。
enableWatchdog技术上该方法只写配置,不操作 UI,但不建议由普通后台任务随意修改系统级看门狗设置。应由明确的系统设置流程调用,并防止多个线程同时重复写配置。
timeout 在 0..100 范围内。/dev/watchdog 或另建无条件喂狗线程。sleep、无限循环或同步大数据操作。普通项目可先使用 30 秒:
stt::util::Util::enableWatchdog(30);
涉及较多本地存储、数据库或外设初始化的项目,可先使用 60 秒,并通过真机压力测试确定最终值。所有可能超过数百毫秒到数秒的数据库、文件、网络和配方操作都应优先改为异步;看门狗只负责在主循环已经无法恢复时触发设备复位。