看门狗应用编程
看门狗应用编程介绍
看门狗定时器的基本概念
-
看门狗是一个可以在一定时间内被复位/重置的计数器
-
如果在规定时间内没有复位,看门狗计时器溢出会对CPU产生复位信号使系统重启
-
有些看门狗可以只产生中断信号而不会使系统复位
I.MX6UL/I.MX6ULL SoC 中的看门狗定时器
-
集成了两个看门狗定时器:WDOG1 和 WDOG2
-
WDOG2 用于安全目的
-
WDOG1 是一个普通的看门狗,支持产生中断信号和复位CPU
Linux 系统中的看门狗设备节点
-
注册的看门狗外设在 /dev/ 目录下生成设备节点,通常命名为 watchdogX(X为数字编号)
-
例如 /dev/watchdog0、/dev/watchdog1
-
这些设备节点用于控制看门狗外设
默认看门狗设备节点
-
/dev/watchdog0 对应 I.MX6U 的 WDOG1
-
系统中可能注册多个看门狗设备,/dev/watchdog 是系统默认的看门狗设备节点
-
通常 /dev/watchdog 代表系统默认的看门狗设备,即 watchdog0
应用层控制看门狗的方法
-
通过 ioctl() 函数控制看门狗
-
需要包含 <linux/watchdog.h> 头文件
-
头文件中定义了多种 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
- #define WDIOC_GETSUPPORT _IOR(WATCHDOG_IOCTL_BASE, 0, struct watchdog_info)
-
常用的 ioctl 指令宏
看门狗应用编程流程
打开设备
-
在调用 ioctl()函数之前,需要先打开看门狗设备得到文件描述符
- int fd;
fd = open(“/dev/watchdog”, “O_RDWR”);
if (0 > fd)
fprintf(stderr, “open error: %s: %s\n”, “/dev/watchdog”, strerror(errno));
获取设备支持哪些功能:WDIOC_GETSUPPORT
-
调用 ioctl() 函数来获取功能信息
-
ioctl(int fd, WDIOC_GETSUPPORT, struct watchdog_info *info);
-
fd:文件描述符
-
WDIOC_GETSUPPORT:指令宏,获取设备的信息
-
info:指向 struct watchdog_info 的指针
-
-
struct watchdog_info 结构体
-
struct watchdog_info {
__u32 options; /* Options the card/driver supports /
_u32 firmware_version; / Firmware version of the card /
__u8 identity[32]; / Identity of the board */
} -
options:记录设备支持的功能或选项
-
#define WDIOF_OVERHEAT 0x0001 /* Reset due to CPU overheat /
#define WDIOF_FANFAULT 0x0002 / Fan failed /
#define WDIOF_EXTERN1 0x0004 / External relay 1 /
#define WDIOF_EXTERN2 0x0008 / External relay 2 /
#define WDIOF_POWERUNDER 0x0010 / Power bad/power fault /
#define WDIOF_CARDRESET 0x0020 / Card previously reset the CPU /
#define WDIOF_POWEROVER 0x0040 / Power over voltage /
#define WDIOF_SETTIMEOUT 0x0080 / Set timeout (in seconds) /
#define WDIOF_MAGICCLOSE 0x0100 / Supports magic close char /
#define WDIOF_PRETIMEOUT 0x0200 / Pretimeout (in seconds), get/set /
#define WDIOF_ALARMONLY 0x0400 / Watchdog triggers a management or other external alarm
not a reboot /
#define WDIOF_KEEPALIVEPING 0x8000 / Keep alive ping reply */ -
常见的 options 值
-
WDIOF_SETTIMEOUT:设备支持设置超时时间
-
WDIOF_KEEPALIVEPING:设备支持“喂狗”操作(重置看门狗计时器)
-
-
-
firmware_version:记录设备的固件版本号
-
identity:描述性的字符串
-
-
使用示例
- 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”);
- struct watchdog_info info;
获取/设置超时时间:WDIOC_GETTIMEOUT、WDIOC_SETTIMEOUT
-
获取超时时间
-
使用指令:WDIOC_GETTIMEOUT
-
功能:获取设备当前设置的超时时间
-
ioctl(int fd, WDIOC_GETTIMEOUT, int *timeout);
-
timeout:指向一个整数的指针,用于存储获取的超时时间(单位:秒)
-
-
设置超时时间
-
使用指令:WDIOC_SETTIMEOUT
-
功能:设置看门狗的超时时间
-
ioctl(int fd, WDIOC_SETTIMEOUT, int *timeout);
-
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;
}
开启/关闭看门狗:WDIOC_SETOPTIONS
-
开启看门狗计时
-
设置好超时时间后,可以开启看门狗计时
-
使用指令:WDIOC_SETOPTIONS
-
-
停止看门狗计时
- 同样使用指令:WDIOC_SETOPTIONS
-
调用方式
-
ioctl(int fd, WDIOC_SETOPTIONS, int *option);
-
option:指向一个 int 类型变量的指针,用于控制开启或停止看门狗计时
- #define WDIOS_DISABLECARD 0x0001 /* Turn off the watchdog timer /
#define WDIOS_ENABLECARD 0x0002 / Turn on the watchdog timer */
- #define WDIOS_DISABLECARD 0x0001 /* Turn off the watchdog timer /
-
-
使用示例
- 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;
}
- int option = WDIOS_ENABLECARD; // 开启看门狗计时
-
注意事项
-
当调用 open() 打开看门狗设备时,即使程序中没有显式开启看门狗计时器,当 close() 关闭设备时,看门狗会自动启动
-
因此,在打开设备后,需要立即使用 WDIOC_SETOPTIONS 指令停止看门狗计时器,完成所有设置后再开启看门狗计时器
-
喂狗:WDIOC_KEEPALIVE
-
看门狗计时器启动后喂狗的必要性
-
启动看门狗计时器后,需要在设定的超时时间之前进行“喂狗”操作
-
如果未在超时前喂狗,计时器溢出将导致系统复位或产生中断信号
-
-
喂狗的指令
-
使用指令:WDIOC_KEEPALIVE
-
作用:重置看门狗计时器,防止系统复位或产生中断信号
-
-
调用方式
-
ioctl(int fd, WDIOC_KEEPALIVE, NULL);
-
fd:看门狗设备的文件描述符
-
WDIOC_KEEPALIVE:喂狗指令宏
-
NULL:指示不需要额外的参数
-
-
使用示例
- if (0 > ioctl(fd, WDIOC_KEEPALIVE, NULL)) {
fprintf(stderr, “ioctl error: WDIOC_KEEPALIVE: %s\n”, strerror(errno));
}
- if (0 > ioctl(fd, WDIOC_KEEPALIVE, NULL)) {
看门狗应用编程实战
#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);}/* 喂狗 */time = (timeout * 1000 - 100) * 1000;//喂狗时间设置us微秒、在超时时间到来前100ms喂狗for ( ; ; ) {usleep(time);ioctl(fd, WDIOC_KEEPALIVE, NULL);}
}
大致流程与上一级内容一致,实现了看门狗计时器的启动、停止、超时时间设置以及喂狗操作,确保系统在正常工作时不会因为超时导致复位重启
在开发板测试
看门狗默认已经被其他功能使用,需要其手动关闭
-
打开/etc/init.d/watchdog.sh 文件,在开头加个 exit 0 将整个文件注释掉,然后开发板重新启动,再执行程序
执行程序后,看门狗计时器自动启动,并持续进行喂狗操作。当程序被终止(如通过 Ctrl + C)而未停止看门狗计时器时,计时器会溢出导致系统重启
-
计时器溢出系统重启