嵌入式Linux C应用编程——看门狗
看门狗应用编程
1. 看门狗基础概念
1.1 什么是看门狗
- 看门狗(Watchdog) 本质上是一个在一定时间内可以被复位的计数器,也叫做看门狗定时器(Watchdog Timer)
- 当看门狗启动后,计数器开始自动计数,经过一定时间如果没有被复位,计数器溢出就会对 CPU 产生一个复位信号使系统重启(俗称 "被狗咬")
- 系统正常运行时,需要在看门狗允许的时间间隔内对看门狗计数器清零(俗称 "喂狗"),不让复位信号产生
- 如果程序跑飞,没有及时"喂狗",系统就会"被咬"复位
1.2 看门狗的工作模式
- 有些看门狗也可以只产生中断信号而不会使系统复位
- 在产品化的嵌入式系统中,为了使系统在异常情况下能自动复位,一般都需要引入看门狗
1.3 I.MX6U 的看门狗硬件
- I.MX6UL/I.MX6ULL SoC 集成了两个看门狗定时器(WDOG):
- WDOG1:普通看门狗,支持产生中断信号以及复位 CPU
- WDOG2:用于安全目的
2. 看门狗设备节点
2.1 设备节点
- Linux 系统中所注册的看门狗外设,都会在
/dev/目录下生成对应的设备节点 - 设备节点名称通常为
watchdogX(X 表示数字编号 0、1、2、3...),例如:/dev/watchdog0/dev/watchdog1
watchdog0就是 I.MX6U 的 WDOG1 所对应的设备节点
2.2 默认看门狗设备
/dev/watchdog代表系统默认的看门狗设备- 通常
/dev/watchdog就等于/dev/watchdog0,代表同一个硬件外设

3. 应用层控制看门狗 — ioctl 接口
3.1 头文件
#include <linux/watchdog.h>
3.2 常用 ioctl 指令汇总
| ioctl 指令 | 说明 |
|---|---|
WDIOC_GETSUPPORT |
获取看门狗支持哪些功能 |
WDIOC_SETOPTIONS |
用于开启或关闭看门狗 |
WDIOC_KEEPALIVE |
喂狗操作 |
WDIOC_SETTIMEOUT |
设置看门狗超时时间 |
WDIOC_GETTIMEOUT |
获取看门狗超时时间 |
3.3 完整的 ioctl 指令宏定义
#define WDIOC_GETSUPPORT _IOR(WATCHDOG_IOCTL_BASE, 0, struct watchdog_info)
#define WDIOC_GETSTATUS _IOR(WATCHDOG_IOCTL_BASE, 1, int)
#define WDIOC_GETBOOTSTATUS _IOR(WATCHDOG_IOCTL_BASE, 2, int)
#define WDIOC_GETTEMP _IOR(WATCHDOG_IOCTL_BASE, 3, int)
#define WDIOC_SETOPTIONS _IOR(WATCHDOG_IOCTL_BASE, 4, int)
#define WDIOC_KEEPALIVE _IOR(WATCHDOG_IOCTL_BASE, 5, int)
#define WDIOC_SETTIMEOUT _IOWR(WATCHDOG_IOCTL_BASE, 6, int)
#define WDIOC_GETTIMEOUT _IOR(WATCHDOG_IOCTL_BASE, 7, int)
#define WDIOC_SETPRETIMEOUT _IOWR(WATCHDOG_IOCTL_BASE, 8, int)
#define WDIOC_GETPRETIMEOUT _IOR(WATCHDOG_IOCTL_BASE, 9, int)
#define WDIOC_GETTIMELEFT _IOR(WATCHDOG_IOCTL_BASE, 10, int)
4. 操作步骤详解
4.1 打开看门狗设备
int fd;
fd = open("/dev/watchdog", O_RDWR);
if (0 > fd)
fprintf(stderr, "open error: %s: %s\n", "/dev/watchdog", strerror(errno));
4.2 获取设备支持功能 — WDIOC_GETSUPPORT
ioctl(int fd, WDIOC_GETSUPPORT, struct watchdog_info *info);
struct watchdog_info 结构体
struct watchdog_info {
__u32 options; /* 设备支持的功能/选项 */
__u32 firmware_version; /* 设备的固件版本号 */
__u8 identity[32]; /* 描述性字符串 */
};
options 字段取值
| 宏定义 | 值 | 说明 |
|---|---|---|
WDIOF_OVERHEAT |
0x0001 | CPU 过热复位置位 |
WDIOF_FANFAULT |
0x0002 | 风扇故障 |
WDIOF_EXTERN1 |
0x0004 | 外部继电器 1 |
WDIOF_EXTERN2 |
0x0008 | 外部继电器 2 |
WDIOF_POWERUNDER |
0x0010 | 电源欠压 |
WDIOF_CARDRESET |
0x0020 | 设备之前复位过 CPU |
WDIOF_POWEROVER |
0x0040 | 电源过压 |
WDIOF_SETTIMEOUT |
0x0080 | 支持设置超时时间(秒) |
WDIOF_MAGICCLOSE |
0x0100 | 支持 magic close 字符 |
WDIOF_PRETIMEOUT |
0x0200 | 支持预超时(秒) |
WDIOF_ALARMONLY |
0x0400 | 仅触发告警,不复位 |
WDIOF_KEEPALIVEPING |
0x8000 | 支持喂狗操作 |
常见值:
WDIOF_SETTIMEOUT— 设备支持设置超时时间WDIOF_KEEPALIVEPING— 设备支持"喂狗"操作
使用示例
struct watchdog_info info;
if (0 > ioctl(fd, WDIOC_GETSUPPORT, &info)) {
fprintf(stderr, "ioctl error: WDIOC_GETSUPPORT: %s\n", strerror(errno));
return -1;
}
printf("identity: %s\n", info.identity);
printf("version: %u\n", info.firmware_version);
if (0 == (WDIOF_KEEPALIVEPING & info.options))
printf("设备不支持喂狗操作\n");
if (0 == (WDIOF_SETTIMEOUT & info.options))
printf("设备不支持设置超时时间\n");
4.3 获取/设置超时时间 — WDIOC_GETTIMEOUT / WDIOC_SETTIMEOUT
获取超时时间
ioctl(int fd, WDIOC_GETTIMEOUT, int *timeout);
设置超时时间
ioctl(int fd, WDIOC_SETTIMEOUT, int *timeout);
- 超时时间以秒为单位
- 设置超时时间时,不可超过其最大值,否则
ioctl()调用将会失败
使用示例
int timeout;
/* 获取超时时间 */
if (0 > ioctl(fd, WDIOC_GETTIMEOUT, &timeout)) {
fprintf(stderr, "ioctl error: WDIOC_GETTIMEOUT: %s\n", strerror(errno));
return -1;
}
printf("current timeout: %ds\n", timeout);
/* 设置超时时间 */
timeout = 10; // 10 秒钟
if (0 > ioctl(fd, WDIOC_SETTIMEOUT, &timeout)) {
fprintf(stderr, "ioctl error: WDIOC_SETTIMEOUT: %s\n", strerror(errno));
return -1;
}
4.4 启用/禁用看门狗 — WDIOC_SETOPTIONS
ioctl(int fd, WDIOC_SETOPTIONS, int *option);
option 取值
| 宏定义 | 值 | 说明 |
|---|---|---|
WDIOS_DISABLECARD |
0x0001 | 禁用看门狗(停止计时) |
WDIOS_ENABLECARD |
0x0002 | 启用看门狗(开启计时) |
使用示例
int option = WDIOS_ENABLECARD; // 启用
// int option = WDIOS_DISABLECARD; // 禁用
if (0 > ioctl(fd, WDIOC_SETOPTIONS, &option)) {
fprintf(stderr, "ioctl error: WDIOC_SETOPTIONS: %s\n", strerror(errno));
return -1;
}
注意:这是 Linux 看门狗驱动的关键特性——当调用
open()打开看门狗设备时,看门狗计时器就会自动启动。所以正确的流程是:打开设备后,先使用WDIOC_SETOPTIONS设置选项为WDIOS_DISABLECARD来禁用看门狗,等所有设置完成之后,再使用WDIOS_ENABLECARD重新启用看门狗。
4.5 喂狗 — WDIOC_KEEPALIVE
ioctl(int fd, WDIOC_KEEPALIVE, NULL);
使用示例
if (0 > ioctl(fd, WDIOC_KEEPALIVE, NULL)) {
fprintf(stderr, "ioctl error: WDIOC_KEEPALIVE: %s\n", strerror(errno));
}
5. 实战示例程序
5.1 完整代码
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <string.h>
#include <linux/watchdog.h>
#define WDOG_DEV "/dev/watchdog"
int main(int argc, char *argv[])
{
struct watchdog_info info;
int timeout;
int time;
int fd;
int op;
if (2 != argc) {
fprintf(stderr, "usage: %s <timeout>\n", argv[0]);
exit(EXIT_FAILURE);
}
/* 打开看门狗 */
fd = open(WDOG_DEV, O_RDWR);
if (0 > fd) {
fprintf(stderr, "open error: %s: %s\n", WDOG_DEV, strerror(errno));
exit(EXIT_FAILURE);
}
/* 打开之后看门狗计时器会开启,先停止它 */
op = WDIOS_DISABLECARD;
if (0 > ioctl(fd, WDIOC_SETOPTIONS, &op)) {
fprintf(stderr, "ioctl error: WDIOC_SETOPTIONS: %s\n", strerror(errno));
close(fd);
exit(EXIT_FAILURE);
}
timeout = atoi(argv[1]);
if (1 > timeout)
timeout = 1;
/* 设置超时时间 */
printf("timeout: %ds\n", timeout);
if (0 > ioctl(fd, WDIOC_SETTIMEOUT, &timeout)) {
fprintf(stderr, "ioctl error: WDIOC_SETTIMEOUT: %s\n", strerror(errno));
close(fd);
exit(EXIT_FAILURE);
}
/* 开启看门狗计时器 */
op = WDIOS_ENABLECARD;
if (0 > ioctl(fd, WDIOC_SETOPTIONS, &op)) {
fprintf(stderr, "ioctl error: WDIOC_SETOPTIONS: %s\n", strerror(errno));
close(fd);
exit(EXIT_FAILURE);
}
/* 喂狗:在超时时间到来前 100ms 喂狗 */
time = (timeout * 1000 - 100) * 1000; // 微秒
for ( ; ; ) {
usleep(time);
ioctl(fd, WDIOC_KEEPALIVE, NULL);
}
}
5.2 程序逻辑说明
- 打开看门狗设备 — 获取文件描述符
fd - 先禁用看门狗 — 使用
WDIOC_SETOPTIONS设置选项为WDIOS_DISABLECARD来禁用看门狗(因为打开设备时它会自动启用) - 获取超时时间参数 — 通过命令行参数
argv[1]传入 - 设置超时时间 — 使用
WDIOC_SETTIMEOUT指令 - 启用看门狗 — 使用
WDIOC_SETOPTIONS设置选项为WDIOS_ENABLECARD来启用看门狗 - 循环喂狗 — 在超时时间到来前(预留 100ms)调用
WDIOC_KEEPALIVE喂狗,不断重置计时器
6. 开发板测试
6.1 编译
使用交叉编译工具链编译示例代码:
arm-linux-gnueabihf-gcc -o watchdog_test watchdog_test.c
6.2 常见问题:Device or resource busy
如果执行程序报错 open error: /dev/watchdog: Device or resource busy,说明看门狗默认已被其他功能使用。
解决方法:
- 打开
/etc/init.d/watchdog.sh文件 - 在文件开头添加
exit 0,将整个文件注释掉 - 开发板重新启动
6.3 测试运行
# 启动看门狗,设置超时时间为 2 秒
./watchdog_test 2
6.4 观察现象
- 程序运行后会不断喂狗,系统正常运行
- 按
Ctrl + C终止进程后,停止喂狗 - 看门狗计时器超时,系统复位重启
- 内核会打印:
watchdog watchdog0: watchdog did not stop!
6.5 测试完毕后恢复
将 /etc/init.d/watchdog.sh 文件中的 exit 0 去掉,让系统重新使用看门狗。
7. 总结
| 步骤 | 操作 | ioctl 指令 | 说明 |
|---|---|---|---|
| 1 | 打开设备 | open() |
打开 /dev/watchdog 或 /dev/watchdog0 |
| 2 | 禁用看门狗 | WDIOC_SETOPTIONS + WDIOS_DISABLECARD |
打开设备时看门狗已自动启用,先禁用以便配置 |
| 3 | 设置超时 | WDIOC_SETTIMEOUT |
设置超时时间(秒) |
| 4 | 启用看门狗 | WDIOC_SETOPTIONS + WDIOS_ENABLECARD |
配置完成后重新启用 |
| 5 | 喂狗 | WDIOC_KEEPALIVE |
在超时前重置计数器 |
核心思路:看门狗的目的是保证系统在异常情况下能自动复位。正常工作时程序周期性喂狗,一旦程序跑飞停止喂狗,看门狗超时就会复位系统,从而恢复系统的正常运行。

浙公网安备 33010602011771号