目录
一、什么是看门狗?
简单来说,看门狗就是一个定时器 Watchdog Timer(WDT),是一个在软件出错时可以让 Linux 系统复位的硬件电路。以下简述一下其工作原理:
在用户空间通过看门狗程序以“喂狗”的形式不断地给 watchdog 写入数据(即通过 /dev/watchdog 特殊设备文件来通知内核的 watchdog 驱动)。当在一定时间内如果不喂狗(即没有往 watchdog 写入数据),它就执行一次系统复位,也就跳到中断向量表执行复位向量。
二、看门狗内核源码分析
在内核中提供了一套完整的驱动接口,具体如下:
wdt_open : 打开设备,程序调用 open 时进入该函数
wdt_close : 关闭设备,程序调用 close 时进入该函数
wdt_write : 写设备,若传入数据大小不为 0 则喂狗; 程序调用 write 时进入该函数
wdt_ioctl : 这个函数是最主要的,原型如下(driver/watchdog)
wdt_ioctl 驱动源码实现如下(以 w83697hf 为例):
static long wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
void __user *argp = (void __user *)arg;
int __user *p = argp;
int new_timeout;
static const struct watchdog_info ident = {
.options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT
| WDIOF_MAGICCLOSE,
.firmware_version = 1,
.identity = "W83697HF WDT",
};
switch (cmd) {
case WDIOC_GETSUPPORT:
if (copy_to_user(argp, &ident, sizeof(ident)))
return -EFAULT;
break;
case WDIOC_GETSTATUS:
case WDIOC_GETBOOTSTATUS:
return put_user(0, p);
case WDIOC_SETOPTIONS:
{
int options, retval = -EINVAL;
if (get_user(options, p))
return -EFAULT;
if (options & WDIOS_DISABLECARD) {
wdt_disable();
retval = 0;
}
if (options & WDIOS_ENABLECARD) {
wdt_enable();
retval = 0;
}
return retval;
}
case WDIOC_KEEPALIVE:
wdt_ping();
break;
case WDIOC_SETTIMEOUT:
if (get_user(new_timeout, p))
return -EFAULT;
if (wdt_set_heartbeat(new_timeout))
return -EINVAL;
wdt_ping();
/* Fall */
case WDIOC_GETTIMEOUT:
return put_user(timeout, p);
default:
return -ENOTTY;
}
return 0;
}
简述其中重要的参数:
WDIOC_KEEPALIVE : 喂狗,类似于 write 函数功能
WDIOC_SETTIMEOUT : 设置超时值
WDIOC_GETTIMEOUT : 获取超时值
WDIOC_SETOPTIONS : 设置看门狗状态,开启(WDIOS_ENABLECARD) 或 关闭(WDIOS_DISABLECARD)
三、编写看门狗程序
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <linux/watchdog.h>
static int open_watchdog(void)
{
int wtd_fd = 0;
wtd_fd = open("/dev/watchdog", O_RDWR);
if (wtd_fd < 0)
return -1;
int timeout = 60; //60s
ioctl(wtd_fd, WDIOC_SETOPTIONS, WDIOS_ENABLECARD);
ioctl(wtd_fd, WDIOC_SETTIMEOUT, &timeout);
return wtd_fd;
}
static void feed_watchdog(int wtd_fd)
{
while (1) {
ioctl(wtd_fd, WDIOC_KEEPALIVE, NULL);
sleep(10);
}
}
static int close_watchdog(int wtd_fd)
{
ioctl(wtd_fd, WDIOC_SETOPTIONS, WDIOS_DISABLECARD);
close(wtd_fd);
}
int main(void)
{
int wtd_fd = 0;
/* 打开设备 */
wtd_fd = open_watchdog(void);
if (wtd_fd < 0) {
printf("wtd open failed\n");
return -1;
}
/* 喂狗 */
feed_watchdog(wtd_fd);
/* 关闭设备 */
close_watchdog(wtd_fd);
return 0;
}
四、关于看门狗作用的特别说明
1. 在实际应用中,一般都是不需要主动去关闭看门狗(close 或 WDIOS_DISABLECARD)的,因为关闭看门狗后它就不能工作了,也就无法实现系统复位。只有当你明确不需要看门狗功能生效时,才可主动关闭看门狗;
2. 当程序奔溃或者软重启失败后,看门狗是让系统复位的最后保障了(假如连看门狗也失效了,那就只能乖乖地拔插电源让系统重启了)。