admin管理员组

文章数量:1588124

前言

Watchdog 是 Linux 系统一个很重要的机制,其目的是监测系统运行的情况,一旦出现锁死,死机的情况,能及时重启机器(取决于设置策略),并收集crash dump.

watchdog,顾名思义,看门狗。这就说明,有一个被watch的对象,和一个watch它的程序。

无论是内核watchdog,还是userland watchdog,其基本思路都是: 
1. 假定某一个对象的状态能表征系统运行是否健康(比如interrupt的次数,比如/dev/watchdog的时间戳); 
2. 启动一个watchdog程序,定期(通过内部或者外部时钟触发)来观测这个对象,来判定系统是否健康,并采取相应动作。

Watchdog 有好几种不同的实现机制,最近我在公司鼓捣了一下各种机制的原理,并在实验室机器上面进行了实验操作,总结了以下两种机制: 
1. kernel watchdog 
2. Userland watchdog

下面,我们分别阐述他们的机制。

kernel watchdog

kernel watchdog 目的

当我们引入这么一个Watchdog的时候,首先关心的是它是做什么用的。简单的说,kernel watchdog是用来检测Lockup 的。

所谓lockup,是指某段内核代码占着CPU不放。Lockup严重的情况下会导致整个系统失去响应。Lockup有几个特点:

首先只有内核代码才能引起lockup,因为用户代码是可以被抢占的,不可能形成lockup(只有一种情况例外,就是SCHED_FIFO优先级为99的实时进程即使在用户态也可能使[watchdog/x]内核线程抢不到CPU而形成soft lockup)
其次内核代码必须处于禁止内核抢占的状态(preemption disabled),因为Linux是可抢占式的内核,只在某些特定的代码区才禁止抢占(例如spinlock),在这些代码区才有可能形成lockup。
Lockup分为两种:soft lockup 和 hard lockup,它们的区别是 hard lockup 发生在CPU屏蔽中断的情况下。而soft lockup则是单个CPU被一直占用的情况(中断仍然可以响应)。

这里我们先要介绍一下NMI。

NMI

NMI,即非可屏蔽中断。即使在内核代码中设置了屏蔽所有中断的时候,NMI也是不可以被屏蔽的。

中断分为可屏蔽中断和非可屏蔽中断。

其中,可屏蔽中断包含时钟中断,外设中断(比如键盘中断,I/O设备中断,等等),当我们处理中断处理程序的时候,在中断处理程序top half时候,在不允许嵌套的情况下,需要关闭中断。

但NMI就不一样了,即便在关闭中断的情况下,他也能被响应。触发NMI的条件一般都是ECC error之类的硬件Error。但NMI也给我们提供了一种机制,在系统中断被误关闭的情况下,依然能通过中断处理程序来执行一些紧急操作,比如kernel panic。

这里涉及到了3个东西:kernel线程,时钟中断,NMI中断(不可屏蔽中断)。

这3个东西具有不一样的优先级,依次是kernel线程 < 时钟中断 < NMI中断。其中,kernel 线程是可以被调度的,同时也是可以被中断随时打断的。

接下来,我们分别看什么是soft lockup,什么是hard lockup.

SoftLockup

Soft lockup是指CPU被内核代码占据,以至于无法执行其它进程。检测soft lockup的原理是给每个CPU分配一个定时执行的内核线程[watchdog/x],如果该线程在设定的期限内没有得到执行的话就意味着发生了soft lockup,[watchdog/x]是SCHED_FIFO实时进程,优先级为最高的99,拥有优先运行的特权。

SoftLockup 示例代码

以下是一段Soft lockup的示例代码,通过一直占用某个CPU,而达到soft lockup的目的:

#include<linux/kernel.h>
#include<linux/module.h>
#include<linux/kthread.h>

struct task_struct *task0;
static spinlock_t spinlock;
int val;

int task(void *arg)
{
    printk(KERN_INFO "%s:%d\n",__func__,__LINE__);
    /* To generate panic uncomment following */
    /* panic("softlockup: hung tasks"); */

    while(!kthread_should_stop()) {
        printk(KERN_INFO "%s:%d\n",__func__,__LINE__);
        spin_lock(&spinlock);
        /* busy loop in critical section */
        while(1) {
            printk(KERN_INFO "%s:%d\n",__func__,__LINE__);
        }

        spin_unlock(&spinlock);
    }

    return val;
}

static int softlockup_init(void)
{
    printk(KERN_INFO "%s:%d\n",__func__,__LINE__);

    val = 1;
    spin_lock_init(&spinlock);
    task0 = kthread_run(&task,(void *)val,"softlockup_thread");
    set_cpus_allowed_ptr(task0, cpumask_of(0));

    return 0;
}

static void softlockup_exit(void)
{
    printk(KERN_INFO "%s:%d\n",__func__,__LINE__);
    kthread_stop(task0);
}

module_init(softlockup_init);
module_exit(softlockup_exit);

上述代码是从某个网站上找到的,它通过spinlock()实现关抢占,使得该CPU上的[watchdog/x]无法被调度。另外,通过set_cpus_allowed_ptr()将该线程绑定到特定的CPU上去。

SoftLockup 检测机制

SoftLockup 检测首先需要对每一个CPU core注册叫做watchdog的kernel线程。即[watchdog/0],[watchdog/1],[watchdog/2

本文标签: Linuxwatchdog