9. Watchdog 操作指南¶
本文档说明在 CV184x 平台上通过 Linux 标准 Watchdog 框架操作看门狗,包括驱动配置、常用 ioctl 及示例代码。
9.1. 操作准备¶
1. 内核要求
使用 SDK 发布的内核,并确保以下配置已开启(驱动随内核编译,无需 insmod):
CONFIG_WATCHDOG=y
CONFIG_WATCHDOG_CORE=y
CONFIG_DW_WATCHDOG=y
2. 使用方式
在控制台通过命令行操作 /dev/watchdog 设备,或在内核态/用户态程序中通过 open、ioctl、write、close 等接口操作看门狗即可。
9.2. 操作流程¶
典型使用流程如下:
打开设备 :打开
/dev/watchdog,打开即启动看门狗,硬件开始计时。(可选)设置超时 :通过
WDIOC_SETTIMEOUT设置超时时间(秒);不设置则使用驱动默认值(42 秒)。立即喂狗 :打开后应尽快调用
WDIOC_KEEPALIVE进行一次喂狗,避免在默认/设定超时内未喂狗导致重启。周期喂狗 :在业务运行期间,在超时时间到达前定期调用
WDIOC_KEEPALIVE喂狗;喂狗间隔须小于当前超时时间。关闭设备 :不再需要看门狗时,按顺序执行:先
WDIOC_SETOPTIONS传入WDIOS_DISABLECARD禁用看门狗,再write(fd, "V", 1)写入魔术字符,最后close(fd);否则关闭文件描述符后硬件仍会继续计时,超时将触发重启。
需要查询当前超时或剩余时间时,可在上述流程中穿插使用 WDIOC_GETTIMEOUT、WDIOC_GETTIMELEFT。
9.3. 操作示例¶
概述
Watchdog 采用标准 Linux 看门狗框架,提供硬件看门狗。用户通过打开设备、设置超时时间、定期喂狗(keepalive)或关闭设备即可使用。
当看门狗超时未被喂狗时,系统将重启。
默认状态 :看门狗默认关闭,由用户决定是否开启。
可设定的超时时间 (单位:秒):1、2、5、10、21、42、85。若设定的值不在上述列表,驱动会选用 大于等于 该值的最接近项;例如设定 8 秒时,实际采用 10 秒。若未设定超时,驱动默认使用 42 秒。
以下示例需包含头文件 <linux/watchdog.h>,ioctl 宏(如 WDIOC_SETTIMEOUT、WDIOC_KEEPALIVE)由该头文件提供,无需自行定义。
9.3.1. 打开 Watchdog 并立即喂狗¶
打开 /dev/watchdog 即启动看门狗,硬件开始计时。打开后应尽快进行一次喂狗 ,否则在超时时间内未喂狗会导致系统重启。
/* 需包含头文件:stdio.h, stdlib.h, unistd.h, fcntl.h, string.h, errno.h, sys/ioctl.h, linux/watchdog.h */
int wdt_fd;
int ret;
wdt_fd = open("/dev/watchdog", O_WRONLY);
if (wdt_fd < 0) {
printf("打开 /dev/watchdog 失败: %s\n", strerror(errno));
return -1;
}
printf("[步骤 1] 打开 /dev/watchdog 成功, fd=%d\n", wdt_fd);
ret = ioctl(wdt_fd, WDIOC_KEEPALIVE, 0);
if (ret != 0)
printf("警告: 打开后立即喂狗 ioctl 返回 %d: %s\n", ret, strerror(errno));
else
printf(" 打开后已执行一次喂狗 (WDIOC_KEEPALIVE)\n\n");
9.3.2. 设置超时时间¶
通过 ioctl WDIOC_SETTIMEOUT 设置超时时间,单位为秒。可设置值为:1、2、5、10、21、42、85;传入其他值时,驱动会取大于等于该值的最接近支持值。
int timeout = 10; /* 单位:秒,支持 1/2/5/10/21/42/85 */
int ret = ioctl(wdt_fd, WDIOC_SETTIMEOUT, &timeout);
if (ret != 0) {
printf("[步骤 2] 设置超时失败: %s\n", strerror(errno));
} else {
printf("[步骤 2] 设置超时 %d 秒成功,驱动实际采用: %d 秒\n\n", 10, timeout);
}
9.3.3. 喂狗(Keepalive)¶
在超时时间到达前,需定期调用 WDIOC_KEEPALIVE 喂狗,否则看门狗超时将触发系统重启。喂狗间隔应小于当前设定的超时时间。
const int keepalive_loops = 5; /* 演示喂狗次数,可改为 while(running) 等 */
int i;
printf("[步骤 3] 开始周期性喂狗(共 %d 次,每次间隔 1 秒)\n", keepalive_loops);
for (i = 0; i < keepalive_loops; i++) {
ioctl(wdt_fd, WDIOC_KEEPALIVE, 0);
printf(" 第 %d 次喂狗\n", i + 1);
sleep(1);
}
printf("\n");
9.3.4. 获取当前超时时间¶
通过 WDIOC_GETTIMEOUT 可查询驱动当前采用的超时时间(秒)。
int timeout_sec = 0;
if (ioctl(wdt_fd, WDIOC_GETTIMEOUT, &timeout_sec) == 0) {
printf("[步骤 4] 当前超时时间: %d 秒\n", timeout_sec);
} else {
printf("[步骤 4] 获取超时时间失败(可选)\n");
}
9.3.5. 获取剩余时间¶
若驱动支持,可通过 WDIOC_GETTIMELEFT 查询距离下次超时还剩多少秒,便于调试或监控。
int timeleft_sec = 0;
if (ioctl(wdt_fd, WDIOC_GETTIMELEFT, &timeleft_sec) == 0) {
printf("[步骤 5] 距离下次超时剩余: %d 秒\n\n", timeleft_sec);
} else {
printf("[步骤 5] 获取剩余时间不支持或失败(可选)\n\n");
}
9.3.6. 关闭 Watchdog(Magic Close)¶
本驱动支持 Magic Close :在关闭设备前,必须先 禁用看门狗 ,再向设备写入魔术字符 'V',最后才能 close。否则即使关闭了文件描述符,硬件看门狗仍会继续计时,超时未喂狗将导致系统重启。
正确顺序:WDIOS_DISABLECARD → write(..., "V", 1) → close。
int option = WDIOS_DISABLECARD;
int ret;
printf("[步骤 6] 关闭 Watchdog (Magic Close)\n");
ret = ioctl(wdt_fd, WDIOC_SETOPTIONS, &option);
if (ret != 0)
printf(" 警告: WDIOC_SETOPTIONS WDIOS_DISABLECARD 返回 %d: %s\n", ret, strerror(errno));
if (wdt_fd >= 0) {
if (write(wdt_fd, "V", 1) != 1)
printf(" 警告: 写入魔术字符 'V' 失败: %s\n", strerror(errno));
else
printf(" 已写入魔术字符 'V'\n");
close(wdt_fd);
wdt_fd = -1;
printf(" 已 close 设备\n");
}
printf("\n程序正常结束\n");
9.3.7. 完整用户态程序示例¶
以下为将上述各步骤串联而成的完整 C 程序,可直接编译运行。需包含头文件 <linux/watchdog.h>,ioctl 宏由该头文件提供。
1/*
2 * Watchdog 操作示例(遵循标准 Linux 看门狗框架)
3 * 需包含头文件 <linux/watchdog.h>,ioctl 宏由该头文件提供。
4 */
5#include <stdio.h>
6#include <stdlib.h>
7#include <unistd.h>
8#include <fcntl.h>
9#include <string.h>
10#include <errno.h>
11#include <sys/ioctl.h>
12#include <linux/watchdog.h>
13
14int main(void)
15{
16 int wdt_fd;
17 int timeout;
18 int timeout_sec;
19 int timeleft_sec;
20 int option;
21 int ret;
22 int i;
23 const int keepalive_loops = 5; /* 演示喂狗次数,可改为 while(running) 等 */
24
25 printf("=== Watchdog 操作示例 ===\n\n");
26
27 /* 步骤 1:打开 Watchdog 并立即喂狗 */
28 wdt_fd = open("/dev/watchdog", O_WRONLY);
29 if (wdt_fd < 0) {
30 printf("打开 /dev/watchdog 失败: %s\n", strerror(errno));
31 return -1;
32 }
33 printf("[步骤 1] 打开 /dev/watchdog 成功, fd=%d\n", wdt_fd);
34
35 ret = ioctl(wdt_fd, WDIOC_KEEPALIVE, 0);
36 if (ret != 0)
37 printf("警告: 打开后立即喂狗 ioctl 返回 %d: %s\n", ret, strerror(errno));
38 else
39 printf(" 打开后已执行一次喂狗 (WDIOC_KEEPALIVE)\n\n");
40
41 /* 步骤 2:设置超时时间(单位:秒,支持 1/2/5/10/21/42/85) */
42 timeout = 10;
43 ret = ioctl(wdt_fd, WDIOC_SETTIMEOUT, &timeout);
44 if (ret != 0) {
45 printf("[步骤 2] 设置超时失败: %s\n", strerror(errno));
46 } else {
47 printf("[步骤 2] 设置超时 %d 秒成功,驱动实际采用: %d 秒\n\n", 10, timeout);
48 }
49
50 /* 步骤 3:设置超时后再次喂狗,并开始周期性喂狗 */
51 ret = ioctl(wdt_fd, WDIOC_KEEPALIVE, 0);
52 if (ret != 0) {
53 printf("[步骤 3] 设置超时后立即喂狗失败: %s\n", strerror(errno));
54 } else {
55 printf("[步骤 3] 设置超时后已立即喂狗一次\n");
56 }
57
58 printf("[步骤 3] 开始周期性喂狗(共 %d 次,每次间隔 1 秒)\n", keepalive_loops);
59 for (i = 0; i < keepalive_loops; i++) {
60 ret = ioctl(wdt_fd, WDIOC_KEEPALIVE, 0);
61 if (ret != 0) {
62 printf(" 第 %d 次喂狗失败: %s\n", i + 1, strerror(errno));
63 break;
64 }
65 printf(" 第 %d 次喂狗成功\n", i + 1);
66 sleep(1);
67 }
68 printf("\n");
69
70 /* 步骤 4:获取当前超时时间 */
71 timeout_sec = 0;
72 if (ioctl(wdt_fd, WDIOC_GETTIMEOUT, &timeout_sec) == 0) {
73 printf("[步骤 4] 当前超时时间: %d 秒\n", timeout_sec);
74 } else {
75 printf("[步骤 4] 获取超时时间失败(可选)\n");
76 }
77
78 /* 步骤 5:获取剩余时间(可选,部分驱动支持) */
79 timeleft_sec = 0;
80 if (ioctl(wdt_fd, WDIOC_GETTIMELEFT, &timeleft_sec) == 0) {
81 printf("[步骤 5] 距离下次超时剩余: %d 秒\n\n", timeleft_sec);
82 } else {
83 printf("[步骤 5] 获取剩余时间不支持或失败(可选)\n\n");
84 }
85
86 /* 步骤 6:关闭 Watchdog(Magic Close)*/
87 /* 正确顺序: WDIOS_DISABLECARD -> write("V", 1) -> close */
88 printf("[步骤 6] 关闭 Watchdog (Magic Close)\n");
89 option = WDIOS_DISABLECARD;
90 ret = ioctl(wdt_fd, WDIOC_SETOPTIONS, &option);
91 if (ret != 0)
92 printf(" 警告: WDIOC_SETOPTIONS WDIOS_DISABLECARD 返回 %d: %s\n", ret, strerror(errno));
93
94 if (wdt_fd >= 0) {
95 if (write(wdt_fd, "V", 1) != 1)
96 printf(" 警告: 写入魔术字符 'V' 失败: %s\n", strerror(errno));
97 else
98 printf(" 已写入魔术字符 'V'\n");
99 close(wdt_fd);
100 wdt_fd = -1;
101 printf(" 已 close 设备\n");
102 }
103 printf("\n程序正常结束\n");
104 return 0;
105}
9.3.8. 命令行快速示例¶
以下说明如何在命令行下实现与上文「完整用户态程序示例」相同的功能。注意 :打开 /dev/watchdog 后若不定期喂狗,系统会在超时后重启,请仅在测试环境使用。设置超时(WDIOC_SETTIMEOUT)、查询超时/剩余时间(WDIOC_GETTIMEOUT / WDIOC_GETTIMELEFT)需通过 C 程序或等价工具完成;推荐直接编译并运行上文的完整用户态程序以一次性完成所有步骤。
推荐:一次性完成所有功能
编译并运行上文「完整用户态程序示例」中的完整 C 程序,可依次完成:打开并立即喂狗、设置超时、获取当前超时与剩余时间、周期性喂狗、Magic Close 关闭。
# 编译(示例:假设源文件为 watchdog_demo.c ,编译工具为 arm-none-linux-uclibcgnueabihf-gcc)
arm-none-linux-uclibcgnueabihf-gcc -o watchdog_demo watchdog_demo.c -static
# 将运行文件拷贝到开发板,并运行
./watchdog_demo
步骤 1:手动激活看门狗(无其他进程干扰)
在无其他进程占用或喂狗的前提下,手动启动喂狗进程,使看门狗开始计时并在超时前被持续喂狗。
# 方式 1:用 watchdog 工具(10 秒超时,后台运行)
watchdog -t 10 /dev/watchdog &
# 方式 2:无工具则用 Shell 循环
( while true; do echo -n '.' > /dev/watchdog; sleep 1; done ) &
步骤 2:关闭 Watchdog(Magic Close)
killall watchdog # 自带 magic close 功能,会自动执行关闭看门狗操作