Linux Watchdog 接口与 API 参考 文章封面

Linux Watchdog 接口与 API 参考

本文介绍 Linux 看门狗的用户态接口,包括 /dev/watchdog 基本操作、Magic Close、完整 ioctl API 及环境监测功能。内容基于 Linux Watchdog 驱动程序 API 官方文档。


一、Watchdog 简介

看门狗定时器(WDT,Watchdog Timer)是一种硬件电路,在发生软件故障时可以自动复位系统。

工作原理:

用户空间守护进程
    │ 定期向 /dev/watchdog 写入数据(喂狗)
    ▼
内核看门狗驱动
    │ 通知硬件:系统正常,重置倒计时
    ▼
硬件看门狗定时器
    │ 若超时内未收到喂狗信号
    ▼
强制复位系统(reboot)

如果用户空间发生故障(进程崩溃、死锁、内存错误等),喂狗停止,硬件看门狗在超时后自动重启系统。


二、基本使用

2.1 打开设备即启动

打开 /dev/watchdog 后,看门狗立即启动。必须在超时时间内持续喂狗,否则系统会被复位。

一个最简单的看门狗守护进程(来自 samples/watchdog/watchdog-simple.c):

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>

int main(void)
{
    int fd = open("/dev/watchdog", O_WRONLY);
    if (fd == -1) {
        perror("watchdog");
        exit(EXIT_FAILURE);
    }
    while (1) {
        write(fd, "", 1);   /* 喂狗:向设备写入任意数据 */
        sleep(10);
    }
    close(fd);
    return 0;
}

实际生产中,守护进程通常在喂狗前先检查关键服务是否正常(如 HTTP 服务器是否响应),确认系统整体健康后再喂狗。

2.2 nowayout:禁止关闭

正常情况下,关闭 /dev/watchdog 文件会停止看门狗。但这有风险——如果守护进程崩溃导致文件被关闭,看门狗也跟着停了,失去保护作用。

CONFIG_WATCHDOG_NOWAYOUT=y(编译时配置)或 nowayout=1(模块参数)可以禁止这种行为:一旦启动,看门狗就无法被关闭,只能持续喂狗。

2.3 Magic Close:安全关闭

支持 Magic Close 的驱动提供了一种安全关闭机制:关闭文件之前,必须先向 /dev/watchdog 写入字符 'V',驱动才会正常停止看门狗。

write(fd, "V", 1);   /* 魔术字,告知驱动这是主动关闭 */
close(fd);           /* 此时看门狗正常停止 */

如果关闭时没有发送 'V',驱动会认为守护进程异常退出,不会停止看门狗,超时后系统将重启。这是一种故障安全(fail-safe)机制。


三、ioctl API 速查

所有符合规范的看门狗驱动都支持 ioctl 接口:

ioctl 命令方向说明
WDIOC_GETSUPPORT读查询设备能力(struct watchdog_info)
WDIOC_GETSTATUS读获取当前状态标志
WDIOC_GETBOOTSTATUS读获取上次启动的状态(是否由看门狗触发重启)
WDIOC_KEEPALIVE写喂狗(等效于向设备写入数据)
WDIOC_SETTIMEOUT读写设置超时时间(秒)
WDIOC_GETTIMEOUT读获取当前超时时间(秒)
WDIOC_SETPRETIMEOUT读写设置预超时时间(秒)
WDIOC_GETPRETIMEOUT读获取当前预超时时间(秒)
WDIOC_GETTIMELEFT读获取距离下次复位的剩余秒数
WDIOC_GETTEMP读获取温度(华氏度,部分驱动支持)
WDIOC_SETOPTIONS写控制看门狗启停

四、ioctl 详解

4.1 喂狗(KEEPALIVE)

ioctl(fd, WDIOC_KEEPALIVE, 0);

效果与 write(fd, "", 1) 完全相同,参数被忽略。喂狗循环示例:

while (1) {
    ioctl(fd, WDIOC_KEEPALIVE, 0);
    sleep(10);
}

4.2 设置/获取超时时间

/* 设置超时时间(秒)。注意:驱动会将 timeout 修改为实际生效值 */
int timeout = 30;
ioctl(fd, WDIOC_SETTIMEOUT, &timeout);
printf("实际超时时间:%d 秒n", timeout);   /* 硬件粒度可能导致与设置值不同 */

/* 获取当前超时时间 */
ioctl(fd, WDIOC_GETTIMEOUT, &timeout);
printf("当前超时时间:%d 秒n", timeout);

⚠️ WDIOC_SETTIMEOUT 调用后,timeout 变量会被驱动修改为实际生效值。硬件计数器的精度可能导致实际值与请求值不同,务必读回确认。

4.3 预超时(PRETIMEOUT)

部分看门狗支持预超时(Pre-timeout):在正式复位之前,先触发一个 NMI 或中断,让内核有机会记录 panic 信息、生成 coredump 等。

/* 设置预超时。预超时 = 超时前 N 秒触发 */
int pretimeout = 10;
ioctl(fd, WDIOC_SETPRETIMEOUT, &pretimeout);
/* 例:超时 60s,预超时 10s → 第 50s 触发预超时,第 60s 复位 */

/* 获取预超时 */
ioctl(fd, WDIOC_GETPRETIMEOUT, &pretimeout);
printf("预超时时间:%d 秒n", pretimeout);

将预超时设为 0 可禁用该功能。并非所有驱动都支持预超时。

4.4 获取剩余时间

int timeleft;
ioctl(fd, WDIOC_GETTIMELEFT, &timeleft);
printf("距下次复位还剩:%d 秒n", timeleft);

4.5 查询设备能力(GETSUPPORT)

struct watchdog_info ident;
ioctl(fd, WDIOC_GETSUPPORT, &ident);
printf("驱动标识:%sn", ident.identity);
printf("固件版本:%dn", ident.firmware_version);
printf("支持能力:0x%08xn", ident.options);

ident.options 各标志位含义:

标志含义
WDIOF_OVERHEAT支持检测 CPU 过热,上次复位因过热触发
WDIOF_FANFAULT支持检测风扇故障
WDIOF_EXTERN1支持外部监控继电器/源 1
WDIOF_EXTERN2支持外部监控继电器/源 2
WDIOF_POWERUNDER支持检测欠电压
WDIOF_POWEROVER支持检测过电压
WDIOF_CARDRESET上次复位由看门狗触发(可通过 GETBOOTSTATUS 查询)
WDIOF_KEEPALIVEPING上次查询后收到过喂狗信号
WDIOF_SETTIMEOUT支持设置/获取超时时间
WDIOF_PRETIMEOUT支持预超时

4.6 查询状态(GETSTATUS / GETBOOTSTATUS)

int flags;

/* 获取当前状态 */
ioctl(fd, WDIOC_GETSTATUS, &flags);

/* 获取上次启动时的状态(判断是否由看门狗触发重启) */
ioctl(fd, WDIOC_GETBOOTSTATUS, &flags);
if (flags & WDIOF_CARDRESET)
    printf("上次重启由看门狗触发n");

注意:并非所有设备都同时支持两个调用,部分驱动只支持 GETBOOTSTATUS。

4.7 获取温度

int temperature;
ioctl(fd, WDIOC_GETTEMP, &temperature);
printf("当前温度:%d °Fn", temperature);   /* 返回值单位为华氏度 */

仅部分带温度传感器的看门狗卡支持。

4.8 控制启停(SETOPTIONS)

int options;

options = WDIOS_DISABLECARD;
ioctl(fd, WDIOC_SETOPTIONS, &options);   /* 停止看门狗定时器 */

options = WDIOS_ENABLECARD;
ioctl(fd, WDIOC_SETOPTIONS, &options);   /* 启动看门狗定时器 */

options = WDIOS_TEMPPANIC;
ioctl(fd, WDIOC_SETOPTIONS, &options);   /* 温度触发时执行 kernel panic */


参考资料

延伸阅读

Watchdog 是系统可靠性的一环,可结合启动流程和 Platform 驱动模型继续阅读。

发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注

滚动至顶部