1、什么是看门狗?
在Linux系统中,看门狗机制主要包括硬件看门狗、软件看门狗和看门狗守护进程三个部分。硬件看门狗是一个独立的计时器设备,用于监视系统的运行状态。如果系统长时间没有发送喂狗信号,硬件看门狗将执行预设的动作,如自动重启系统。软件看门狗和守护进程则通过定期发送喂狗信号来确保系统的正常运行,并在系统出现异常时触发硬件看门狗执行复位操作。
本章主要讲的是软件看门狗!
2、软件看门狗
软件看门狗其实就是一个可以在一定时间内被复位的计数器。当看门狗启动后,计数器开始自动计数,经过一定时间,如果没有被复位,计数器溢出就会对 CPU 产生一个复位信号使系统重启(俗称"被狗咬")。系统正常运行时,需要在看门狗允许的时间间隔内对看门狗计数器清零(俗称"喂狗"),不让复位信号产生。如果系统不出问题,程序保证按时"喂狗",一旦程序跑飞,没有"喂狗",系统"被咬"复位。一般用ioctl即可实现对看门狗的开启跟喂狗。
3、linux中看门狗的相关函数介绍
3.1 watchdog_info结构体
cpp
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 字段记录了设备支持哪些功能或选项;
firmware_version 字段记录了设备的固件版本号;
identity 字段则是一个描述性的字符串。
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 */ 喂狗
---
3.2、ioctl指令宏
应用层控制看门狗其实非常简单,通过 ioctl()函数即可做到,应用程序中,需要包含头文件<linux/watchdog.h>头文件, 该头文件中定义了一些 ioctl 指令宏,每一个不同的指令宏表示向设备请求不同的操作,如下所示:
cpp
#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)
常用的:
ioctl 指令 说明
WDIOC_GETSUPPORT 获取看门狗支持哪些功能
WDIOC_SETOPTIONS 用于开启或关闭看门狗
WDIOC_KEEPALIVE 喂狗操作
WDIOC_SETTIMEOUT 设置看门狗超时时间
WDIOC_GETTIMEOUT 获取看门狗超时时间
4、看门狗开发流程
cpp
//打开看门狗设备
open("/dev/watchdog", "O_RDWR"); //需要注意的是,当调用 open()打开看门狗设备的时候, 即使程序中没有开启看门狗计时器,所以,当打开设备之后, 需要使用 指令停止看门狗计时,等所有设置完成之后再开启看门狗计时器
//开启/关闭看门狗: WDIOC_SETOPTIONS
使用 WDIOC_SETOPTIONS 指令可以开启看门狗计时或停止看门狗计时,使用方式如下:
ioctl(int fd, WDIOC_SETOPTIONS, int *option);
#define WDIOS_DISABLECARD 0x0001 /*Turn off the watchdog timer停止看门狗计时*/
#define WDIOS_ENABLECARD 0x0002 /* Turn on the watchdog timer开启看门狗计时*/
//获取/设置超时时间:WDIOC_GETTIMEOUT、WDIOC_SETTIMEOUT
使用 WDIOC_GETTIMEOUT 指令可获取设备当前设置的超时时间,使用方式如下:
ioctl(int fd, WDIOC_GETTIMEOUT, int *timeout);
使用 WDIOC_SETTIMEOUT 指令可设置看门狗的超时时间,使用方式如下:
ioctl(int fd, WDIOC_SETTIMEOUT, int *timeout);
超时时间是以秒为单位, 设置超时时间时,不可超过其最大值、否则 ioctl()调用将会失败
喂狗: WDIOC_KEEPALIVE
看门狗计时器启动之后,需要在超时之前,去"喂狗",否则计时器溢出超时将会导致系统复位或产生一个中断信号,通过 WDIOC_KEEPALIVE 指令喂狗,使用方式如下:
ioctl(int fd, WDIOC_KEEPALIVE, NULL);
看门狗的代码实例:
cpp
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/watchdog.h>
#define WATCHDOG_DEVICE "/dev/watchdog"
int main() {
int fd;
int timeout = 15; // 设置看门狗超时时间为15秒
int ret;
// 打开看门狗设备
fd = open(WATCHDOG_DEVICE, O_WRONLY);
if (fd == -1) {
perror("Cannot open watchdog device");
exit(EXIT_FAILURE);
}
// 设置看门狗超时时间
ret = ioctl(fd, WDIOC_SETTIMEOUT, &timeout);
if (ret == -1) {
perror("Cannot set watchdog timeout");
close(fd);
exit(EXIT_FAILURE);
}
printf("Watchdog timeout set to %d seconds\n", timeout);
// 循环喂狗,保持系统不被复位
while (1) {
printf("Feeding the watchdog...\n");
ret = ioctl(fd, WDIOC_KEEPALIVE, NULL); // 使用WDIOC_KEEPALIVE喂狗
if (ret == -1) {
perror("Cannot feed the watchdog");
break;
}
sleep(timeout - 5); // 在超时前喂狗,这里设置为超时时间减去5秒
}
close(fd);
return 0;
}