soft lockup和hard lockup介绍

本文深入剖析Linux Kernel中Lockup探测机制,包括softlockup和hardlockup的实现原理。通过具体代码分析,介绍了如何利用kernel线程、时钟中断及NMI中断来检测系统抢占关闭或中断关闭等问题。

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

在linux kernel里,有一个debug选项LOCKUP_DETECTOR。

使能它可以打开kernel中的soft lockup和hard lockup探测。

这两个东西到底有什么用处那?

首先,soft/hard lockup的实现在kernel/watchdog.c中,

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

这3个东西具有不一样的优先级,依次是kernel线程 < 时钟中断 < NMI中断。

而正是用到了他们之间优先级的区别,所以才可以调试系统运行中的两种问题:

1. 抢占被长时间关闭而导致进程无法调度(soft lockup)

2. 中断被长时间关闭而导致更严重的问题(hard lockup)


接下来我们从具体代码入手分析linux(3.10)是如何实现这两种lockup的探测的:

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;
	}
}
首先,系统会为每个cpu core注册一个一般的kernel线程,名字叫watchdog/0, watchdog/1...以此类推。

这个线程会定期得调用watchdog函数

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多久被调用一次,我们就先简单的认为,这个线程是负责更新watchdog_touch_ts的。

然后我们要看一下时钟中断了:

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;
}
这个函数主要做2件事情:

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);
}
只有在hrtimer_interrupts发生了更新的情况下,kernel线程才会被得到运行。

那就是说,kernel线程和时钟中断函数的频率是相同的。默认情况是10*2/5=4秒一次。

int __read_mostly watchdog_thresh = 10;

static int get_softlockup_thresh(void)
{
	return watchdog_thresh * 2;
}

static void set_sample_period(void)
{
	/*
	 * convert watchdog_thresh from seconds to ns
	 * the divide by 5 is to give hrtimer several chances (two
	 * or three with the current relation between the soft
	 * and hard thresholds) to increment before the
	 * hardlockup detector generates a warning
	 */
	sample_period = get_softlockup_thresh() * ((u64)NSEC_PER_SEC / 5);
}
2.就是要探测是否有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的机制,我们就能尽早的发现这样的问题了。

分析完soft lockup,我们继续分析hard lockup

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中断出来。

u64 hw_nmi_get_sample_period(int watchdog_thresh)
{
	return (u64)(cpu_khz) * 1000 * watchdog_thresh;
}
在这里,根据当前cpu的频率,算出一个值,也就是20秒cpu clock经过的周期数。

这样一来,当cpu全负荷跑完20秒后,就会有一个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
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变量在时钟中断处理函数里有没有被更新。

假如没有更新,就意味着中断出了问题,可能被错误代码长时间的关中断了。
那这样,相应的问题也就暴露出来了。

### Linux Soft Lockup 原因 Soft lockup 是指某些进程或内核线程被卡在特定状态下无法继续执行,尽管系统其他部分仍能正常运作[^1]。这种状况通常是由于以下几个原因之一: - **内核锁争用**:当多个进程试图获取同一个资源上的互斥锁时可能发生死锁情况。 - **无限循环**:如果程序陷入了一个不会退出的循环中,并且该循环不包含任何可打断点,则可能导致处理器持续占用而不响应外部请求。 - **硬件故障**:偶尔也会因为底层硬件层面的问题引起此错误。 具体来说,在Linux环境中遇到 `BUG: soft lockup` 错误提示意味着某颗CPU核心已经停止响应超过一定时间长度(默认为20秒),这期间它既没有处理新的任务也没有完成当前正在做的工作[^2]。 ### 解决方案概述 针对上述提到的各种可能成因,可以采取如下措施来预防修复soft lockup问题: #### 调试与诊断工具的应用 利用诸如 perf、ftrace 或者 SystemTap 这样的性能分析工具可以帮助定位哪些函数调用路径消耗了大量的CPU周期而未能及时返回控制权给调度器[^3]。 ```bash perf record -g sleep 60; perf report ``` 这段命令会收集一分钟内的采样数据并生成报告用于后续审查。 #### 修改内核参数配置 调整一些关键性的内核参数有助于缓解由竞争条件引发的竞争态异常行为。例如增加最大允许等待的时间间隔(`watchdog_thresh`)可以让系统有更多机会自行恢复而不是立即触发警报;设置更激进的任务迁移策略能够减少跨NUMA节点间通信延迟带来的负面影响等。 ```bash echo "10" > /proc/sys/kernel/watchdog_thresh ``` 通过修改 `/etc/sysctl.conf` 文件中的相应条目实现永久生效。 #### 更新驱动程序版本 对于那些怀疑是由设备驱动缺陷所造成的案例而言,尝试升级到最新稳定版往往可以获得更好的兼容性稳定性表现。特别是涉及到网络接口控制器(Network Interface Controller, NIC)以及存储子系统的组件时更是如此。 #### 定期维护检查 定期重启服务端机器以清除累积下来的临时文件缓存其他潜在隐患同样重要。此外还需关注官方安全公告列表里是否有针对性补丁发布可供应用。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值