watchdog ,hang_detect分析

本文详细介绍了MTK平台中Watchdog监控机制的实现原理和技术细节,包括通过/dev/RT_Monitor设备监控Watchdog行为的过程,以及在不同版本下的异常处理方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

作用:
用于监控,system_server 中的watchdog 线程的行为是否异常。
实现:
mtk 在kernel 中专门注册了一个设备( /dev/RT_Monitor ),用于和上层通信监控 watchdog是否正常。
大概为:watchdog 向/dev/RT_Monitor 设置一个值,然后hang_detect根据此值,计算出一个时间,在此时间内,watchdog,必须再次通知一下hang_detect, 来表示并未超时。

代码流程:
kernel-3.18/drivers/misc/mediatek/aee/aed/monitor_hang.c

//定义/dev/RT_Monitor  设备。
static struct miscdevice aed_wdt_RT_Monitor_dev = { 
    .minor = MISC_DYNAMIC_MINOR,
    .name = "RT_Monitor",
    .fops = &aed_wdt_RT_Monitor_fops,
};
//定义/dev/RT_Monitor 设备对应的操作函数。
static const struct file_operations aed_wdt_RT_Monitor_fops = {  
    .owner = THIS_MODULE,
    .open = monitor_hang_open,
    .release = monitor_hang_release,
    .poll = monitor_hang_poll,
    .read = monitor_hang_read,
    .write = monitor_hang_write,
    //watchdog 调用ioctl  RT_Monitor驱动时,会调用 此方法monitor_hang_ioctl,来从新设置超时时间。
    .unlocked_ioctl = monitor_hang_ioctl,
#ifdef CONFIG_COMPAT
    .compat_ioctl = monitor_hang_ioctl, 
#endif
}
//注册设备,并初始化 hang_detect
static int __init monitor_hang_init(void)
{
    //注册一个字符设备,/dev/RT_Monitor
    err = misc_register(&aed_wdt_RT_Monitor_dev);
    //初始化hang_detect
    hang_detect_init();
    return err;
}
//启动一个新线程,初始化hang_detect
int hang_detect_init(void)
{
    struct task_struct *hd_thread;
    unsigned char *name = "hang_detect";
    //创建monitor hang 线程,去监控 watchdog
    //hang_detect_thread 新线程要运行的方法
    //name  新线程的名字
    hd_thread = kthread_create(hang_detect_thread, NULL, name);
    //唤醒开始运行hd_thread,
    wake_up_process(hd_thread);
    return 0;
}
//在新线程中运行此函数,去监控watchdog
static int hang_detect_thread(void *arg)                                                                            
{
    struct sched_param param = {.sched_priority = 99 };
    //设置调度策略
    sched_setscheduler(current, SCHED_FIFO, &param);

    //无限循环开始监控watchdog
    while (!kthread_should_stop()) {
        if ((1 == hd_detect_enabled) && (FindTaskByName("system_server") != -1)) {

            if (hang_detect_counter <= 0)
                ShowStatus();

            if (hang_detect_counter == 0) {
                //根据当前版本做不同的选择,
                //如果是user版本,去通过aee_kernel_warning_api 抓取oops,并上报warning
                if (aee_mode != AEE_MODE_CUSTOMER_USER) {
                    aee_kernel_warning_api
                        (__FILE__, __LINE__,
                         DB_OPT_NE_JBT_TRACES | DB_OPT_DISPLAY_HANG_DUMP,
                         "\nCRDISPATCH_KEY:SS Hang\n",
                         "we triger HWT ");
                    msleep(30 * 1000);
                } else {
                    //如果不是user版本,向kernel上报bug,
                    BUG();
                }
            }
            //如果本次循环没有问题,hang_detect_counter 减一,并休眠30s
            //上层会通过ioctl RT_Monitor 设备,来重设 hang_detect_counter 的值
            hang_detect_counter--;
        } else {
            //如果没有找到system_server线程,表示系统在重启,那么等待hang_detect_counter*30s
            if (1 == hd_detect_enabled) {
                hang_detect_counter = hd_timeout + 4;
                hd_detect_enabled = 0;
            }
        }
        //每次循环休眠30s
        msleep((HD_INTER) * 1000);
    }
    return 0;
}
//如果是user版本:
kernel-3.18/drivers/misc/mediatek/aee/common/aee-common.c
    void aee_kernel_warning_api(const char *file, const int line, const int db_opt, const char *module, const char *msg, ...)
{
    char msgbuf[KERNEL_REPORT_LENGTH];
    int offset = 0;
    va_list args;

    va_start(args, msg);
    offset += snprintf(msgbuf, KERNEL_REPORT_LENGTH, "<%s:%d> ", file, line);
    offset += vsnprintf(msgbuf + offset, KERNEL_REPORT_LENGTH - offset, msg, args);
    if (g_aee_api && g_aee_api->kernel_reportAPI)
        //如果g_aee_api存在且初始化成功,调用他的 kernel_reportAPI方法
        g_aee_api->kernel_reportAPI(AE_DEFECT_WARNING, db_opt, module, msgbuf);
    else
        LOGE("AEE kernel warning: %s", msgbuf);
    va_end(args);
}
EXPORT_SYMBOL(aee_kernel_warning_api);

//g_aee_api 的初始化为:
drivers/misc/mediatek/aee/aed/aed-main.c
module_init(aed_init);

static int __init aed_init(void)    
{
    ......
    aee_register_api(&kernel_api);
    ......
}

此kernel_api 是在本类中定义的,他的kernel_reportAPI 方法也是本类中的方法

static struct aee_kernel_api kernel_api = {
    .kernel_reportAPI = kernel_reportAPI,
    .md_exception = external_exception,
    .md32_exception = external_exception,
    .scp_exception = external_exception,
    .combo_exception = external_exception
};

drivers/misc/mediatek/aee/common/aee-common.c
会在aee_register_api 方法中将kernel_api 赋值给g_aee_api

 void aee_register_api(struct aee_kernel_api *aee_api) 
 {
     if (!aee_api)
         BUG();
     g_aee_api = aee_api;
 }

上层与底层通信是:

frameworks/base/services/core/java/com/android/server/Watchdog.java
@Override
public void run() {
    while (true) {
        if (exceptionHWT != null) {
            exceptionHWT.WDTMatterJava(300);
        }
    }
}
frameworks/base/core/java/com/mediatek/aee/ExceptionLog.java
public void WDTMatterJava(long lParam) {
    WDTMatter(lParam) ;
}
private static native void WDTMatter(long lParam) ;
frameworks/base/core/java/com/mediatek/aee/jni/com_mediatek_aee_exceptionlog.cpp
static void com_mediatek_exceptionlog_WDTMatter (JNIEnv* env, jobject clazz, jlong lParam){ 
    //AE_WDT_DEVICE_PATH 即为设备 "/dev/RT_Monitor"
    int fd = open(AE_WDT_DEVICE_PATH, O_RDONLY);
    //通过ioctl 将 300 传入 RT_Monitor 驱动
    if (ret = ioctl(fd, AEEIOCTL_RT_MON_Kick, (int)(lParam)))
    ......
    close (fd) ;
    return;
}

在之后会调用到monitor_hang 类的 monitor_hang_ioctl 方法(在注册此设备时,就已经指定,ioctl此设备,会调用此函数)

 // 对 /dev/RT_Monitor 使用ioctl 操作时会调用此方法。
 //cmd 为 AEEIOCTL_RT_MON_Kick
 // arg 为 300
 static long monitor_hang_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 {
     ......
     //正常会走这里
     if (cmd == AEEIOCTL_RT_MON_Kick) {
         LOGE("AEEIOCTL_RT_MON_Kick ( %d)\n", (int)arg);
         aee_kernel_RT_Monitor_api((int)arg);
         return ret;
     }
    ......
 }
 //在此方法中根据lParam(300),从新计算hang_detect_counter 的值
void aee_kernel_RT_Monitor_api(int lParam)                                      
{
        ......
        hd_detect_enabled = 1;
        //HD_INTER 为 30
        //300 对应的为 10,
        //那么表示在10*30s内,watchdog 必须再次ioctl RT_monitor,否则认为watchdog 发生异常。
        hang_detect_counter = hd_timeout = ((long)lParam + HD_INTER - 1) / (HD_INTER);
        ......
    }
}

参考:
http://blog.youkuaiyun.com/ldinvicible/article/details/51042811

<think>好的,用户现在问的是“watchdog无感知卡死是什么意思”。我需要先理解这个术语的具体含义。从字面来看,“watchdog”通常指的是看门狗定时器,用于检测系统是否正常运行,而“无感知卡死”可能指系统卡死但看门狗没有触发或检测到的情况。 首先,我需要确认用户的问题背景。用户可能遇到了系统卡死的问题,但看门狗没有起作用,导致系统无法自动恢复。这种情况可能出现在嵌入式系统、服务器或其他需要高可靠性的设备中。 接下来,我需要分析可能的原因。看门狗无感知卡死可能有几个因素:硬件故障、软件配置错误、看门狗定时器未被正确喂狗,或者系统卡死在无法被看门狗检测到的状态。 然后,我需要考虑用户的使用场景。用户可能是开发人员、系统管理员或技术支持人员,他们需要了解问题的原因和解决方法。因此,回答需要技术深度,同时要易于理解,提供排查步骤和解决方案。 还要注意用户之前的问题是关于“hang detect time out: 0x0”,可能用户在处理系统挂起或卡死的问题,现在进一步询问看门狗相关的问题,说明他们在进行系统稳定性调试。需要确保回答与之前的建议保持一致,并提供更深入的指导。 需要检查是否涵盖了所有可能的原因,比如硬件连接、驱动程序问题、喂狗逻辑错误、中断阻塞等。同时,应提供具体的排查方法,如检查日志、测试硬件、代码审查等。 另外,用户可能需要知道如何预防这种情况,因此建议部分应包括配置建议和设计最佳实践,比如合理设置超时时间,避免阻塞操作,使用硬件看门狗等。 最后,要确保回答结构清晰,分点说明,便于用户逐步排查和解决问题。同时使用中文,避免专业术语过于晦涩,必要时举例说明。</think>### **Watchdog "无感知卡死" 现象解析** **"Watchdog无感知卡死"** 指系统因软件或硬件故障陷入完全卡死(无响应)状态,但**看门狗(Watchdog)**未能检测到异常并触发复位,导致系统无法自动恢复。这种现象可能由设计缺陷、配置错误或硬件失效引起,常见于嵌入式系统、服务器或工业控制设备中。 --- #### **1. 核心概念:看门狗(Watchdog)的作用** 看门狗是一种硬件或软件机制,用于监控系统运行状态。其核心逻辑为: - **喂狗(Kick the Dog)**:系统正常运行时,需周期性(例如每隔 1 秒)向看门狗发送信号(喂狗)。 - **超时触发复位**:若系统卡死导致喂狗中断,看门狗在超时后强制复位系统以恢复运行。 **"无感知卡死" 的直接表现**: - 系统已完全卡死(如死锁、硬件故障),但看门狗未复位系统。 - 用户需手动重启才能恢复,失去自动修复能力。 --- #### **2. 常见原因分析** ##### **(1)硬件看门狗未正确启用** - **硬件连接问题**:看门狗芯片与主控的复位信号线(RESET)未连通或接触不良。 - **供电异常**:看门狗芯片供电不稳定,导致其无法正常工作。 - **配置寄存器错误**:未通过代码正确初始化看门狗(如设置超时时间、启用复位功能)。 ##### **(2)喂狗逻辑设计缺陷** - **喂狗线程被阻塞**: - 喂狗任务依赖的线程/进程因优先级反转、死锁等原因无法运行。 - **案例**:在实时操作系统中,高优先级任务长期占用 CPU,导致低优先级的喂狗任务无法执行。 - **中断服务程序(ISR)卡死**: - 若喂狗操作在中断中执行,而中断因异常(如未清除中断标志)被持续触发,可能导致喂狗失效。 - **全局中断关闭**: - 关键代码段关闭全局中断时间过长,导致喂狗任务无法被调度。 ##### **(3)看门狗超时时间设置不合理** - **超时时间过长**: - 例如设置为 60 秒,但系统可能在 10 秒内已卡死,剩余 50 秒期间用户已感知到故障。 - **超时时间过短**: - 系统正常任务执行时间偶尔超过看门狗超时,导致误复位。 ##### **(4)系统卡死在喂狗逻辑内部** - **喂狗代码路径中存在阻塞**: - 喂狗函数本身调用阻塞操作(如等待信号量、执行复杂计算)。 - **示例**: ```c void feed_watchdog() { // 错误:在喂狗函数中执行耗时操作 process_data(); // 可能阻塞或耗时过长 write_to_wdt_reg(); // 实际喂狗操作 } ``` ##### **(5)多核系统中的同步问题** - **核间通信故障**: - 在多核系统中,若喂狗任务依赖其他核的状态同步,通信失败可能导致喂狗中断。 - **缓存一致性错误**: - 看门狗寄存器写入因缓存未刷新未实际生效。 --- #### **3. 排查与解决方法** ##### **(1)硬件排查** - **测量复位信号**: 使用示波器检测看门狗芯片的复位引脚(RESET),确认超时后是否产生复位脉冲。 - **检查供电与接线**: 确保看门狗芯片的电源电压稳定,信号线连接正确。 ##### **(2)软件逻辑验证** - **简化喂狗测试**: 注释所有业务代码,仅保留喂狗操作,测试看门狗是否能正常复位。 - **添加调试输出**: 在喂狗函数中插入日志,确认其执行频率是否符合预期。 ```c void feed_watchdog() { log("Feeding watchdog at %lld", get_timestamp()); write_to_wdt_reg(); } ``` ##### **(3)调整超时时间** - **根据业务需求设置**: 超时时间应略大于系统最耗时任务的执行时间(例如:最大任务周期 + 20% 冗余)。 ##### **(4)优化喂狗代码路径** - **确保原子性和最小化**: 喂狗函数应仅包含必要操作(如写寄存器),避免调用可能阻塞的代码。 - **分离喂狗与业务逻辑**: 使用独立线程或中断上下文执行喂狗,避免被主任务阻塞。 ##### **(5)多核系统专项检查** - **核间通信超时处理**: 在跨核状态同步中添加超时机制,避免无限等待。 - **内存屏障与缓存控制**: 使用 `DSB`(数据同步屏障)或 `__sync_synchronize()`(GCC)确保寄存器写入生效。 --- #### **4. 设计预防建议** - **冗余看门狗**: 使用两级看门狗(硬件 + 软件),或主备双看门狗芯片。 - **心跳监测扩展**: 结合其他健康检查机制(如网络心跳包、关键进程存活监控)。 - **故障注入测试**: 人为模拟系统卡死场景(如 `kill -STOP` 喂狗进程),验证看门狗响应。 --- ### **总结** "Watchdog无感知卡死" 的根源在于**看门狗机制未能覆盖实际故障场景**。需从硬件连接、喂狗逻辑、超时参数、多核同步等维度全面审查,并通过压力测试验证可靠性。设计时应遵循“最小化喂狗路径”和“防御性编码”原则,确保看门狗始终作为系统最后的恢复防线。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值