Linux Watchdog 机制

前言

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]…

同时,系统会有一个高精度的计时器hrtimer(一般来源于APIC),该计时器能定期产生时钟中断,该中断对应的中断处理例程是kernel/watchdog.c: watchdog_timer_fn(),在该例程中:
- 要递增计数器hrtimer_interrupts,这个计数器同时为hard lockup detector用于判断CPU是否响应中断;
- 还要唤醒[watchdog/x]内核线程,该线程的任务是更新一个时间戳;
- soft lock detector检查时间戳,如果超过soft lockup threshold一直未更新,说明[watchdog/x]未得到运行机会,意味着CPU被霸占,也就是发生了soft lockup。

注意,这里面的内核线程[watchdog/x]的目的是更新时间戳,该时间戳是被watch的对象。而真正的看门狗,则是由时钟中断触发的 watchdog_timer_fn(),这里面 [watchdog/x]是被scheduler调用执行的,而watchdog_timer_fn()则是被中断触发的。

我们先看一下watchdog线程注册代码,通过smpboot_register_percpu_thread注册了watchdog_threads:


static struct smp_hotplug_thread watchdog_threads = {
    .store          = &softlockup_watchdog,
    .thread_should_run  = watchdog_should_run,
    .thread_fn      = watchdog,
    .thread_comm        = "watchdog/%u",
    .setup          = watchdog_enable,
    .park           = watchdog_disable,
    .unpark         = watchdog_enable,
};

void __init lockup_detector_init(void)
{
    set_sample_period();
    if (smpboot_register_percpu_thread(&watchdog_threads)) {
        pr_err("Failed to create watchdog threads, disabled\n");
        watchdog_disabled = -ENODEV;
    }
}

这个线程会定期(一定情况下,后面会讲)调用watchdog 函数,其目的是更新watchdog_touch_ts,如下:

static void __touch_watchdog(void)
{
    __this_cpu_write(watchdog_touch_ts, get_timestamp());
}

static void watchdog(unsigned int cpu)
{
    __this_cpu_write(soft_lockup_hrtimer_cnt,
             __this_cpu_read(hrtimer_interrupts));
    __touch_watchdog();
}

然后我们看看时钟中断,函数如下,该中断注册了名为watchdog_timer_fn的中断处理函数:

static void watchdog_enable(unsigned int cpu)
{
    struct hrtimer *hrtimer = &__raw_get_cpu_var(watchdog_hrtimer);

    /* kick off the timer for the hardlockup detector */
    hrtimer_init(hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
    hrtimer->function = watchdog_timer_fn;

    /* done here because hrtimer_start can only pin to smp_processor_id() */
    hrtimer_start(hrtimer, ns_to_ktime(sample_period),
              HRTIMER_MODE_REL_PINNED);
}

其中,watchdog_timer_fn处理过程如下:

static enum hrtimer_restart watchdog_timer_fn(struct hrtimer *hrtimer)
{
    unsigned long touch_ts = __this_cpu_read(watchdog_touch_ts);
    int duration;

    /* kick the hardlockup detector */
    watchdog_interrupt_count();

    duration = is_softlockup(touch_ts);
    if (unlikely(duration)) {
        if (softlockup_panic)
            panic("softlockup: hung tasks");
        __this_cpu_write(soft_watchdog_warn, true);
    } else
        __this_cpu_write(soft_watchdog_warn, false);

    return HRTIMER_RESTART;
}

该函数做了两件事情:
1. 更新hrtimer_interrupts变量。

static void watchdog_interrupt_count(void)
{
    __this_cpu_inc(hrtimer_interrupts);
}

这里我们就要回顾之前创建的那个kernel线程了,多久调用一次就和hrtimer_interrupts的值密切相关。

static int watchdog_should_run(unsigned int cpu)
{
    return __this_cpu_read(hrtimer_interrupts) !=
        __this_cpu_read(soft_lockup_hrtimer_cnt);
}
  1. 第二件事,是要探测是否有soft lockup发生
static int is_softlockup(unsigned long touch_ts)
{
    unsigned long now = get_timestamp();

    /* Warn about unreasonable delays: */
    if (time_after(now, touch_ts + get_softlockup_thresh()))
        return now - touch_ts;

    return 0;
}

很容易理解,其实就是查看watchdog_touch_ts变量在最近20秒的时间内,有没有被创建的kernel thread更新过。

假如没有,那就意味着线程得不到调度,所以很有可能就是在某个cpu core上抢占被关闭了,所以调度器没有办法进行调度。

这种情况下,系统往往不会死掉,但是会很慢。

有了soft lockup的机制,我们就能尽早的发现这样的问题了。

如果我们将内核参数 softlockup_panic设置为1,它将执行panic。否则,将warning信息打印出来。

softlockup设置

linux kernel会自动检测softlockup,在发生softlockup情况下,系统默认会打印相关warning信息。如果需要出发panic的话,可以设置

echo 1 > /proc/sys/kernel/softlockup_panic

可以同时设置 watchdog_thresh参数来定义发现softlockup以后系统panic的时间,默认是10s, 也就是说20s后系统panic。最大能设到60s,也就是说,120s后启动系统panic。

一般来说,在production system上,不建议使用softlockup_panic选项(有可能误伤)。可以在调试系统上使用。

Hardlockup

讲完了Softlockup,我们接着讲 Hardlockup。

Hard lockup比soft lockup更加严重,CPU不仅无法执行其它进程,而且不再响应中断。检测hard lockup的原理利用了PMU的NMI perf event,因为NMI中断是不可屏蔽的,在CPU不再响应中断的情况下仍然可以得到执行,它再去检查时钟中断的计数器hrtimer_interrupts是否在保持递增,如果停滞就意味着时钟中断未得到响应,也就是发生了hard lockup

Hardlockup 示例代码

下面代码示意了hardlockup如何产生的,其中重要一点就是关中断,这里,通过spin_lock_irqsave()

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

MODULE_LICENSE("GPL");

static int
hog_thread(void *data)
{
    static DEFINE_SPINLOCK(lock);
    unsigned long flags;

    printk(KERN_INFO "Hogging a CPU now\n");
    spin_lock_irqsave(&lock, flags);
    while (1);

    /* unreached */
    return 0;
}

static int __init
hog_init(void)
{
    kthread_run(&hog_thread, NULL, "hog");
    return 0;
}

module_init(hog_init);

在上述例子中,中断被关闭,普通中断无法被相应(包括时钟中断),线程无法被调度,因此,在这种情况下,不仅仅[watchdog/x]线程也无法工作,hrtimer也无法被相应。

Hardlockup 检测机制

Linux kernel设计了一个检测lockup的机制,称为NMI Watchdog,是利用NMI中断实现的,用NMI是因为lockup有可能发生在中断被屏蔽的状态下,这时唯一能把CPU抢下来的方法就是通过NMI,因为NMI中断是不可屏蔽的。

NMI watchdog会利用到之前讲到的hrtimer。它的触发条件是基于PMU的NMI perf event,当PMU的计数器溢出时会触发NMI中断,对应的中断处理例程是 kernel/watchdog.c: watchdog_overflow_callback(),hard lockup detector就在其中,它会检查上述hrtimer的中断次数(hrtimer_interrupts)是否在保持递增,如果停滞则表明hrtimer中断未得到响应,也就是发生了hard lockup。

这里面,被watch的对象是hrtimer,而watchdog则是由PMU设备发起的NMI中断处理程序 watchdog_overflow_callback()

我们看看代码

当我们将内核参数nmi_watchdog设为1时候,会执行以下函数:

static int watchdog_nmi_enable(unsigned int cpu)
{
    struct perf_event_attr *wd_attr;

    wd_attr = &wd_hw_attr;
    wd_attr->sample_period = hw_nmi_get_sample_period(watchdog_thresh);

    /* Try to register using hardware perf events */
    event = perf_event_create_kernel_counter(wd_attr, cpu, NULL, watchdog_overflow_callback, NULL);
}

在上面函数里,perf_event_create_kernel_counter函数主要是注册了一个硬件的事件。

这个硬件在x86里叫performance monitoring,这个硬件有一个功能就是在cpu clock经过了多少个周期后发出一个NMI中断出来。

相应的中断处理函数为watchdog_overflow_callback,如下:

static void watchdog_overflow_callback(struct perf_event *event,
         struct perf_sample_data *data,
         struct pt_regs *regs)
{
    if (is_hardlockup()) {
        int this_cpu = smp_processor_id();

        if (hardlockup_panic)
            panic("Watchdog detected hard LOCKUP on cpu %d", this_cpu);
        else
            WARN(1, "Watchdog detected hard LOCKUP on cpu %d", this_cpu);

        return;
    }

    return;
}

上述函数会通过is_hardlockup检测是否有hardlockup,如果是的话,检测内核参数hardlockup_panic设置情况,如果为1(默认),则panic;否则,打印warning信息。

is_hardlockup()检测代码如下:

static int is_hardlockup(void)
{
    unsigned long hrint = __this_cpu_read(hrtimer_interrupts);

    if (__this_cpu_read(hrtimer_interrupts_saved) == hrint)
        return 1;

    __this_cpu_write(hrtimer_interrupts_saved, hrint);
    return 0;
}

上述可见,该函数主要就是查看hrtimer_interrupts变量在时钟中断处理函数里有没有被更新。
假如没有更新,就意味着中断出了问题,可能被错误代码长时间的关中断了。

hardlockup设置

hardlockup的检测需要启动NMI watchdog。可以通过设置内核参数实现:

echo 1 > /proc/sys/kernel/nmi_watchdog

在发生hardlockup情况下,如果我们需要系统panic,可以设置(默认已设定)

echo 1 > /proc/sys/kernel/hardlockup_panic

用户态watchdog

尽管内核watchdog能够检测到softlockup 和 hardlockup 并采取相应的措施,但并非所有的系统宕机都是因为内核线程lockup引起的,用户线程一样有可能到时系统宕机的情况。比如,用户程序有可能占着临界资源无法释放,系统太忙,疲于响应各种中断,导致无法执行调度程序。这都可能导致系统无法正常使用。

在这种情况下,时钟中断和NMI中断仍然能够被响应,所以内核lockup检测机制无法检查出来。但由于系统已经无法正常工作,我们需要一种机制,一种用户态的watchdog,来检测这种系统挂起的状态,并作出相应的动作。

用户态watchdog,自然检测的对象是用户态的程序(是否能被调度)。这里面,基于硬件支持程度不同,我们分为hardware watchdog和software watchdog。后者简称softdog。

需要额外安装watchdog 的rpm 包。

其基本原理都类似,如下:
1. 启动一个timer设备,可能是硬件设备,也可能为软件模拟设备(softdog.ko)。该设备在timeout的时候,会执行相应动作(如系统重启,系统panic)
2. 我们启动一个用户态daemon,叫做watchdog 的进程 (/usr/sbin/watchdog)。该进程定期(一般默认每一秒钟)往一个timer设备/dev/watchdog[x] (或者/dev/watchdog1)里面写东西,这样该设备的时间戳就能够被更新,而不至于timeout重启。
3. 假如因为什么原因,导致系统很忙,watchdog进程无法执行,无法往/dev/watchdog[x]里面写信息,那么当timer设备timeout后, 就会执行相应动作。

接下来,我们继续详解hardware watchdog和softdog的使用。

softdog

先讲softdog,因为无论什么linux 系统(只要kernel中包含softdog.ko驱动),都可以用配置softdog。

使用softdog很简单,只需要:
1. 安装 watchdog rpm
2. 启动softdog服务
- systemctl start softdog.service

当我们启动softdog service 的时候,会执行以下操作:
1. modprobe softdog // 如果内核没有加载softdog,加载之
2. watchdog -c /etc/softdog.conf

默认情况下,watchdog程序会通过softdog.ko创建一个叫做/dev/watchdog1的设备(timer 设备),并且定期往它写东西(用于更新时间戳)。

位于内核的softdog会模拟timer设备(通过时钟中断的方式模拟),并执行相应的中断处理例程,该例程的目的是检查timer 设备(即/dev/watchdog1)是否timeout。如果timeout,则执行相应动作(默认为panic)。

聪明的小朋友们看出来一个问题了,假如系统无法响应时钟中断,是不是softdog就没法工作了?

Bingo,的确是这样的。如果系统无法响应中断(比如关中断了),softdog.ko中的timer模拟程序就无法工作,自然无法响应。但在这种情况下,之前介绍过的 NMI watchdog是能够工作的。

另外的,我们可以通过 /proc/devices查看 watchdog对应的设备号

[root@node227 /]# cat /proc/devices  | grep watchdog
252 watchdog
[root@node227 /]# ls -alt /dev/watchdog1
crw------- 1 root root 252, 1 Aug 16 19:21 /dev/watchdog1

可以看到,这里面softdog 注册了major no 为252的设备号。

hardware watchdog

讲完了softdog,我们再讲讲hardware watchdog。

hardware watchdog,既然是hardware,那肯定是独立于OS的存在。在不同设备上实现方式也不一样。在我们的实验室环境下,有两种实现方式:

  1. 通过BMC实现。
  2. 通过iTCO实现。

先讲讲BMC实现的。

BMC,又叫做Baseboard mother controller,是集成在主板上,独立于CPU和内存的后台控制器。可以通过BMC对系统执行开机,关机等操作。目前,通常的做法是使用ipmi工具通过往BMC发命令,从而来达到获取系统信息的目的。比如,获取CPU温度,状态,Power on, Power off系统。

记住,BMC是主板上的硬件设备,所以它也不知道主机上装的什么操作系统,他只能开机,关机,发中断信息,并不能直接启动系统panic。

有两种方式可以启动BMC上的timer:

  1. 第一是在进入BIOS设置界面的时候,启用 OS Timer(具体设置位置因主板型号而定)。

设置以后,系统会启动一个硬件timer (由BMC控制的硬件计时器)。在不启动硬件watchdog的情况下,他会自动超时触发系统Reset。

  1. 第二种方式是通过启动ipmidog服务,在安装了watchdog rpm的前提下,执行以下操作启动ipmidog服务:
    • systemctl start ipmidog

为了了解这个机制,我们先看看 ipmidog 的系统服务做了什么:

[Service]
Type=forking
ExecStart=-/bin/sh -c 'if [ ! -e /dev/watchdog0 ]; then  modprobe ipmi_watchdog;fi;/usr/sbin/watchdog'

这里会检查是否存在 /dev/watchdog0 (不是特别清楚什么意思,网上很多人说/dev/watchdog0 和/dev/watchdog指向同一个设备,不管了)。如果没有,就会加载 ipmi_watchdog.ko。这个module是通过IPMI的方式与BMC进行通信的。然后启动watchdog。

默认情况下,watchdog会读取/etc/watchdog.conf里面的配置,并且默认情况下往/dev/watchdog里面写东西(用于更新timer时间戳)。/dev/watchdog是一个设备号为 “10, 130” 的硬件设备,可以由BMC控制,当它超时以后,可以触发系统Reset(可配置为ignore)。

我们可以通过ipmitool来查看该timer的具体信息:

[root@node51 lib]# ipmitool mc watchdog get
Watchdog Timer Use: SMS/OS (0x44)
Watchdog Timer Is: Started/Running
Watchdog Timer Actions: Hard Reset (0x01)
Pre-timeout interval: 0 seconds
Timer Expiration Flags: 0x10
Initial Countdown: 600 sec
Present Countdown: 599 sec

这里面,”Initial Countdown” 为用户设定的超时时限,”Present Countdown” 为目前的计时。如果”Present Countdown” 变为 0,那么就会触发 “Timer Action”,即 “Hard Reset”。

正常情况下,由于watchdog程序会每隔一秒钟往/dev/watchdog写东西,”Initial Countdown” 和 “Present Countdown” 的差值不会超过 1s。只有当 watchdog程序无法执行的时候,才会引起 “Present Countdown” 的一直减少。

记住,ipmi watchdog无法产生panic,只能reboot 机器。

现在讲讲iTCO

由于iTCO的相关资料较少,就简单介绍一下。

iTCO是intel自己提供的一种计时器方式,在配置上,不需要重启机器进入BIOS,他是通过一个叫做iTCO_wdt的驱动对硬件 timer 进行配置的。能提供和ipmi watchdog类似的机制,并且不需要额外的BMC这类硬件支持。

[root@node227 /]# lsmod | grep wdt
iTCO_wdt               13480  0 
iTCO_vendor_support    13718  1 iTCO_wdt

unknown_nmi_panic

这个有点题外话,unknown_nmi_panic 是一个内核参数,该参数和watchdog没有直接关系,但对于调试系统宕机,远程触发panic 有好处。所以简单的介绍一下。

首先,为什么这里要提到unknown_nmi_panic这个内核参数呢?

当系统发生宕机(hang住)的时候,根据前面所讲的,我们有kernel watchdog用于检测soft lockup,有NMI watchdog用于检测hard lockup,有watchdog 软件来检测用户空间的系统挂起状态。但这并不能覆盖所有的系统挂起的情况。另外就是当使用softdog 来监测用户进程调度情况时,它受制于系统中断能否响应;而使用hardware watchdog来监测用户进程调度情况时,在发生hang的时候也只能重启系统而不能触发panic。

所以,我们希望有一种机制,能够在所有情况下(所有系统hang的情况,甚至于系统正常运行状态下),都能有一种机制,可以手动触发panic,用于收集crash dump,以便对系统hang的原因进行分析。

其中一种机制,就是利用NMI 来触发。

我们前面知道,NMI是非可屏蔽中断,也就是说,它一定能被系统捕获。如果我们将unknow_nmi_panic设置为1 ,它会启用一个中断处理例程,在系统接受到unknown 的 NMI的时候,强制触发panic。

在super micro的主板上,BMC可以向中断控制器发出unknown NMI。可以通过以下方式实现(不是所有BMC都支持这种方式):

ipmitool -I lanplus -H {ipmi lan IP address} -U {username} -P {passwd} chassis power diag

上述ipmitool 命令是通过另外一台主机向被监测主机的BMC发出的。其中 “-H” 代表该BMC自身用于IPMI远程管理的IP 地址,这需要保证命令发起的主机和BMC lan口相通。上述ipmitool命令会使得BMC往中断控制器发出一个未被定义的NMI中断。当unknown_nmi_panic设为 0 的时候,被监测主机的控制台会打印信息,表示收到了不认识的 NMI 中断;当unknown_nmi_panic设为 1 的时候,则会引起系统panic。

这里需要注意的是,有些其他的监测软件也会用到unknown NMI (启用自定义的NMI 中断处理程序 ),所以,当使用到这些软件/配置的时候,unknown_nmi_panic必须禁用。

其中,前面提到的NMI watchdog就是这种机制,当nmi_watchdog设为 1 的时候,unknown_nmi_panic必须禁用。(本人在某些机型上实验过,没发现上述冲突,但因为这是官网资料,为了保险起见,还是尽量禁用)。

另外,还有Oprofile软件,也是利用了NMI 作为系统监控的软件,在使用Oprofile时候,unknown_nmi_panic也必须禁用。

SysRq 魔术键

最后,另外一个题外话,提一下SysRq 魔术键。

当我们启用内核参数sysrq(设为1)的时候,我们可以通过Alt+SysRq+{功能键}来控制系统的启动或panic,其中:
1. Alt+SysRq+c 触发panic
2. Alt+SysRq+b 触发reboot

当然,由于这里用到了键盘,所以,使用的前提是系统还能够响应键盘中断。

参考资料:
1: IBM Developer 正在整合其语言站点组合。 – IBM Developer
2: 内核如何检测soft lockup与hard lockup? | Linux Performance
3: NMI Watchdog Timer_编码人的专栏-CSDN博客_nmi中断的中断类型编码
4: IT log book: Linux - what are "CPU lockups”? - News, tips & guidance for agile, development, Atlassian-Software (JIRA, Confluence, Bitbucket, ...) and Google Cloud
5: soft lockup和hard lockup介绍_潘振杰的工作室-CSDN博客

转载至https://blog.csdn.net/ericstarmars/article/details/81750919

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值