当前位置:首页>Linux>Linux如果重启是看门狗引起,怎么知道?

Linux如果重启是看门狗引起,怎么知道?

  • 2026-09-10 11:42:00
Linux如果重启是看门狗引起,怎么知道?
大家好,我是王鸽,从事嵌入式久了各种问题都遇到过,比如概率设备重启,死机等,尤其是极小的概率性问题,这种需要不断修改测试验证,如果没有找到问题那是相当头疼的。Linux系统中如果重启是看门狗引起,怎么知道?

其实在 Linux 系统中,要判断系统复位是否由看门狗触发,核心思路是利用硬件 / 内核的 “复位原因记录机制” —— 看门狗复位后会留下专属 “痕迹”,我们只需读取这些痕迹即可定位原因。以下是从「硬件层→内核层→用户层」的完整排查方法,覆盖嵌入式 / 工业场景的主流方案:

一、核心原理:看门狗复位会留下 “复位原因标识”

几乎所有带硬件看门狗的 SOC(如 STM32、IMX6/8、RK3399、AM335x),都会在复位状态寄存器(Reset Status Register, RSR) 中记录复位原因:

  • 看门狗复位:对应寄存器的WDOG_RST位会置 1;
  • 其他复位:如上电复位(POR)、手动复位(RST 按键)、软件复位(reboot)、掉电复位,对应不同的寄存器位。

Linux 的核心排查逻辑就是:读取该寄存器值 → 解析复位原因 → 记录 / 展示给用户。

二、方法 1:直接读取硬件复位状态寄存器(最准确)

这是嵌入式场景的 “终极方案”,直接从 SOC 硬件寄存器读取复位原因,不受软件层干扰。

步骤 1:找到 SOC 的复位状态寄存器信息

需查阅 SOC 的数据手册,找到 “复位原因寄存器” 的物理地址和位定义:

  • 示例 1(IMX6ULL):
  • 复位状态寄存器SRC_SRSR1(物理地址0x020C4004),BIT2表示看门狗复位;
  • 示例 2(STM32MP157):
  • 复位状态寄存器RCC_RSR(物理地址0x50000000),BIT3表示独立看门狗(IWDG)复位,BIT4表示窗口看门狗(WWDG)复位;
  • 示例 3(RK3399):
  • 复位状态寄存器GRF_SOC_CON0(物理地址0xFF770000),BIT18表示看门狗复位。

步骤 2:在 Linux 中读取寄存器(2 种方式)

方式 A:驱动 / 内核模块读取(推荐,可自动化记录)
#include<linux/module.h>#include<linux/io.h>// 以IMX6ULL为例,替换为你的SOC寄存器地址和位定义#define RST_STATUS_REG  0x020C4004  // 复位状态寄存器物理地址#define WDOG_RST_BIT    (1 << 2)    // 看门狗复位标识位static void __iomem *rst_reg;staticint __init wdt_rst_check_init(void){    u32 rst_status;    // 1. 映射物理寄存器到内核虚拟地址    rst_reg = ioremap(RST_STATUS_REG, 4);    if (!rst_reg) {        pr_err("ioremap failed\n");        return -ENOMEM;    }    // 2. 读取复位状态寄存器    rst_status = readl(rst_reg);    pr_info("reset status register value: 0x%08x\n", rst_status);    // 3. 解析是否为看门狗复位    if (rst_status & WDOG_RST_BIT) {        pr_crit("SYSTEM RESET BY WATCHDOG!\n");        // 可选:将复位原因写入文件系统,供用户态读取        // echo "watchdog_reset" > /tmp/reset_reason    } else {        pr_info("reset reason: not watchdog (0x%08x)\n", rst_status);    }    // 4. 清除复位状态寄存器(部分SOC需手动清除,否则下次读取仍为旧值)    writel(rst_status, rst_reg);  // 写原值清除(不同SOC清除方式不同)    iounmap(rst_reg);    return 0;}staticvoid __exit wdt_rst_check_exit(void){    pr_info("module exit\n");}module_init(wdt_rst_check_init);module_exit(wdt_rst_check_exit);MODULE_LICENSE("GPL");
方式 B:用户态直接读取(调试用)

通过devmem命令读取物理寄存器(需 root,且内核开启CONFIG_DEVMEM):

# 以IMX6ULL为例,读取复位状态寄存器devmem 0x020C4004 32# 输出示例:0x00000004 → 二进制第2位置1 → 看门狗复位# 输出0x00000001 → 上电复位(POR)

这个需要看数据手册了。

三、方法 2:利用 Linux 内核日志 / 设备树(简化方案)

1. 内核启动日志(dmesg)

多数 SOC 的 Linux 内核会在启动时解析复位原因,并打印到 dmesg 中:

# 查看内核启动日志中的复位原因dmesg | grep -i "reset\|watchdog\|wdt"
# 典型输出(看门狗复位):# [    0.000000] Reset cause: WDOG (Watchdog)# [    0.000000] imx6ull-pinctrl 20e0000.iomuxc: reset cause: watchdog reset# 非看门狗复位输出:# [    0.000000] Reset cause: POR (Power On Reset)# [    0.000000] Reset cause: RST_BTN (External Reset)

2. 设备树适配(自动解析)

若 SOC 的驱动已适配复位原因解析,可直接读取设备树导出的节点:

# 查看复位原因(部分系统支持)cat /sys/devices/platform/soc/reset-cause# 输出:watchdog / por / reset_button 等

四、方法 3:软件层面标记(兜底方案)

若硬件寄存器无法读取(如无手册 / 驱动不支持),可通过 “文件标记” 间接判断:

1. 原理

  • 系统正常启动时,创建一个 “正常启动标记文件”(如/tmp/normal_boot);
  • 看门狗复位是 “异常复位”,系统重启后不会执行正常启动流程,该文件不会被创建;
  • 若重启后无该文件 → 大概率是看门狗复位。

2. 实现(添加到开机自启脚本/etc/rc.local)

#!/bin/sh# 1. 检查是否为看门狗复位(无正常启动标记)if [ ! -f /tmp/normal_boot ]; then    echo "WATCHDOG RESET DETECTED! $(date)" >> /var/log/wdt_reset.logelse    rm -f /tmp/normal_boot  # 清除旧标记fi

# 2. 创建正常启动标记(系统启动完成后执行)

touch /tmp/normal_boot

# 3. 启动喂狗线程(可选)

/usr/bin/watchdog_feed &

五、关键注意点(避坑)

  1. 复位状态寄存器需及时清除
    多数 SOC 的复位状态寄存器是 “只读且置 1 后保持”,若不清除,下次读取会误判复位原因;
  2. 软件复位不会触发看门狗位
    通过reboot/init 6重启系统,复位寄存器中看门狗位不会置 1,可区分 “主动重启” 和 “看门狗复位”;
  3. 硬件看门狗 vs 软件看门狗
    软件看门狗复位本质是内核触发的软件重启,部分 SOC 不会标记为 “看门狗复位”,需结合日志判断;
  4. 多看门狗场景
    若系统有多个看门狗(如独立 WDT 芯片 + SOC 内置 WDT),需确认每个看门狗对应的复位寄存器位;
  5. 日志持久化
    将复位原因写入持久化存储(如 SD 卡 / Flash),避免重启后日志丢失(嵌入式系统/tmp是内存文件系统,重启即失)。

总结

方法
优点
缺点
适用场景
读取硬件寄存器
最准确、不受软件干扰
需 SOC 手册、懂寄存器
嵌入式 / 工业场景(生产环境)
内核日志 / 设备树
简单、无需改代码
依赖内核驱动适配
通用 Linux 系统
软件标记文件
无硬件依赖、易实现
间接判断、可能误判
调试 / 无硬件手册场景

核心排查流程:

  1. 优先看dmesg | grep reset → 快速定位;
  2. 若日志无信息,用devmem读取复位寄存器 → 精准判断;
  3. 兜底用软件标记文件 → 记录异常复位。

实战举一个例子,针对RK3399平台,如果出现出现看门狗复位情况。

一、RK3399 复位原因核心硬件信息(必看)

RK3399 的复位原因由GRF(General Register Files)寄存器 记录,核心寄存器和位定义如下:

寄存器名称
物理地址
位定义(关键)
说明
GRF_SOC_CON0
0xFF770000
BIT18:看门狗复位(WDT_RST)
内置看门狗复位标识
BIT0:上电复位(POR)
首次上电 / 掉电复位
BIT1:外部复位(RST_BTN)
复位按键触发
BIT2:软件复位(SYS_RST)
reboot
命令触发
GRF_WDOG_CON
0xFF770070
BIT0:看门狗使能位
辅助验证看门狗是否开启

注:RK3399 有两个看门狗(WDT0/WDT1),默认使用 WDT1(地址0xFF1A0000),复位后都会置位 GRF_SOC_CON0 的 BIT18。

二、方法 1:直接读取硬件寄存器(精准判断,推荐生产环境)

1. 调试阶段:用devmem命令快速读取(root 权限)

# 1. 读取复位原因寄存器(GRF_SOC_CON0)devmem 0xFF770000 32# 2. 解析结果(重点看BIT18是否置1):# 输出 0x00040000 → 二进制BIT18=1 → 看门狗复位# 输出 0x00000001 → BIT0=1 → 上电复位# 输出 0x00000002 → BIT1=1 → 外部按键复位# 输出 0x00000004 → BIT2=1 → 软件复位(reboot)# 3. (可选)读取看门狗使能状态(验证看门狗是否开启)devmem 0xFF770070 32# 输出 BIT0=1 → 看门狗已使能;BIT0=0 → 未使能

2. 代码层面:内核模块 / 应用层读取(自动化记录)

方式 A:内核模块(开机自动解析,推荐)
#include<linux/module.h>#include<linux/io.h>#include<linux/kernel.h>// RK3399 复位原因寄存器定义#define RK3399_GRF_BASE     0xFF770000#define  RK3399_GRF_SOC_CON0 0x0000  // GRF_SOC_CON0 偏移#define RK3399_WDT_RST_BIT  (1 << 18)  // 看门狗复位位#define RK3399_POR_RST_BIT  (1 << 0)   // 上电复位位#define RK3399_EXT_RST_BIT  (1 << 1)   // 外部复位位#define RK3399_SOFT_RST_BIT (1 << 2)   // 软件复位位static void __iomem *grf_base;staticint __init rk3399_reset_check_init(void){    u32 rst_status;    // 1. 映射GRF寄存器到内核虚拟地址    grf_base = ioremap(RK3399_GRF_BASE, 0x1000); // 映射4KB空间    if (IS_ERR(grf_base)) {        pr_err("ioremap GRF failed: %ld\n", PTR_ERR(grf_base));        return PTR_ERR(grf_base);    }    // 2. 读取复位状态寄存器    rst_status = readl(grf_base + RK3399_GRF_SOC_CON0);    pr_info("RK3399 reset status: 0x%08x\n", rst_status);    // 3. 解析复位原因    if (rst_status & RK3399_WDT_RST_BIT) {        pr_crit("=== RK3399 RESET BY WATCHDOG ===\n");        // 可选:写入持久化存储(如/var/log/wdt_reset.log)        // 注意:嵌入式系统需确保/var是持久化分区(非tmpfs)        const char *log = "WDT_RESET " __DATE__ " " __TIME__ "\n";        struct file *fp = filp_open("/var/log/wdt_reset.log", O_WRONLY|O_APPEND|O_CREAT, 0644);        if (fp && !IS_ERR(fp)) {            kernel_write(fp, log, strlen(log), &fp->f_pos);            filp_close(fp, NULL);        }    } else if (rst_status & RK3399_POR_RST_BIT) {        pr_info("RK3399 reset reason: Power On Reset (POR)\n");    } else if (rst_status & RK3399_EXT_RST_BIT) {        pr_info("RK3399 reset reason: External Reset Button\n");    } else if (rst_status & RK3399_SOFT_RST_BIT) {        pr_info("RK3399 reset reason: Software Reset (reboot)\n");    } else {        pr_info("RK3399 reset reason: Unknown (0x%08x)\n", rst_status);    }    // 4. 清除复位状态(RK3399需写掩码清除,避免下次误判)    writel(rst_status, grf_base + RK3399_GRF_SOC_CON0); // 写原值清除    iounmap(grf_base);    return 0;}staticvoid __exit rk3399_reset_check_exit(void){    pr_info("RK3399 reset check module exit\n");}module_init(rk3399_reset_check_init);module_exit(rk3399_reset_check_exit);MODULE_LICENSE("GPL");MODULE_DESCRIPTION("RK3399 Watchdog Reset Check Module");

方式 B:用户态 C 代码(调试用)

#include<stdio.h>#include<fcntl.h>#include<sys/mman.h>#include<unistd.h>#define GRF_BASE 0xFF770000#define GRF_SIZE 0x1000#define GRF_SOC_CON0 0x0000#define WDT_RST_BIT (1 << 18)intmain(){    int fd;    void *grf_map;    u32 *rst_reg;    u32 rst_status;    // 1. 打开/dev/mem(需root,内核开启CONFIG_DEVMEM)    fd = open("/dev/mem", O_RDWR | O_SYNC);    if (fd < 0) {        perror("open /dev/mem failed");        return -1;    }    // 2. 映射GRF寄存器    grf_map = mmap(NULL, GRF_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, GRF_BASE);    if (grf_map == MAP_FAILED) {        perror("mmap failed");        close(fd);        return -1;    }    // 3. 读取复位状态    rst_reg = (u32 *)(grf_map + GRF_SOC_CON0);    rst_status = *rst_reg;    printf("RK3399 reset status: 0x%08x\n", rst_status);    // 4. 解析    if (rst_status & WDT_RST_BIT) {        printf("=== RK3399 reset by WATCHDOG ===\n");    } else {        printf("RK3399 reset not by watchdog\n");    }    // 5. 释放资源    munmap(grf_map, GRF_SIZE);    close(fd);    return 0;}

编译运行:

gcc rk3399_wdt_check.c -o rk3399_wdt_check sudo ./rk3399_wdt_check

三、方法 2:读取 RK3399 内核启动日志(最简方案)

RK3399 的 Linux 内核(4.4+/5.10+)已内置复位原因解析,直接查看dmesg即可:

# 过滤复位相关日志dmesg | grep -i "reset\|wdt\|watchdog"# 看门狗复位典型输出:# [    0.000000] Rockchip reset reason: watchdog reset# [    0.000000] rk3399-grf ff770000.grf: reset reason: 0x40000 (WDT)# 非看门狗复位输出:# [    0.000000] Rockchip reset reason: power on reset# [    0.000000] Rockchip reset reason: external reset

四、方法 3:RK3399 设备树适配(自动导出复位原因)

若你的 RK3399 设备树已启用rockchip-reset-reason驱动,可直接读取系统节点:

# 查看复位原因(部分定制内核支持)cat /sys/devices/platform/ff770000.grf/reset_reason# 输出:watchdog / por / external / software

五、RK3399 看门狗复位排查关键注意点

  1. 寄存器清除时机
    RK3399 的 GRF_SOC_CON0 复位位是 “只读且锁存”,必须在读取后手动清除(写原值),否则下次重启仍会显示旧的复位原因;
  2. 看门狗硬件适配
    RK3399 的 WDT1(0xFF1A0000)是系统看门狗,WDT0(0xFF1B0000)为备用,需确认驱动使用的是哪个看门狗;
  3. 持久化日志
    RK3399 嵌入式系统默认/var是 tmpfs(内存盘),需将复位日志写入/mnt/flash(Flash 分区)或 SD 卡,避免重启丢失;
  4. 软件看门狗区别
    若使用内核soft_wdt模块,复位后 GRF_SOC_CON0 的 WDT 位不会置 1,需结合dmesg | grep soft_wdt判断;
  5. 内核配置依赖
    使用devmem//dev/mem需内核开启CONFIG_DEVMEM=y,否则会提示权限拒绝。

好的关于看看门狗复位这块大概到这里。

谢谢阅读!

最新文章

随机文章