兰州网站推广优化,网站网站怎么定位,二级分销佣金分配表,wordpress zhong前言 linux检测会有各类异常检测#xff0c;如softlock hungtask deadlock 等#xff0c;这些到底检查了什么#xff0c;有什么不同#xff1f;具体的原理是什么#xff1f;让我们一起来看看 源码#xff1a;linux7.0 hungtask hungtask检测是否有任务长时间处于D状态。 …前言linux检测会有各类异常检测如softlock hungtask deadlock 等这些到底检查了什么有什么不同具体的原理是什么让我们一起来看看源码linux7.0hungtaskhungtask检测是否有任务长时间处于D状态。源码解析初始化static int __init hung_task_init(void) { atomic_notifier_chain_register(panic_notifier_list, panic_block); /* Disable hung task detector on suspend */ pm_notifier(hungtask_pm_notify, 0); watchdog_task kthread_run(watchdog, NULL, khungtaskd); // 启动一个内核线程 hung_task_sysctl_init(); return 0; }检测static bool task_is_hung(struct task_struct *t, unsigned long timeout) { unsigned long switch_count t-nvcsw t-nivcsw; unsigned int state READ_ONCE(t-__state); /* * skip the TASK_KILLABLE tasks -- these can be killed * skip the TASK_IDLE tasks -- those are genuinely idle * skip the TASK_FROZEN task -- it reasonably stops scheduling by freezer */ if (!(state TASK_UNINTERRUPTIBLE) || (state (TASK_WAKEKILL | TASK_NOLOAD | TASK_FROZEN))) // 查看是不是D状态 return false; /* * When a freshly created task is scheduled once, changes its state to * TASK_UNINTERRUPTIBLE without having ever been switched out once, it * musnt be checked. */ if (unlikely(!switch_count)) return false; if (switch_count ! t-last_switch_count) { // t-nvcsw t-nivcsw也就是上下文切换总次数如果有变化则认为没有hung住 t-last_switch_count switch_count; t-last_switch_time jiffies; return false; } if (time_is_after_jiffies(t-last_switch_time timeout * HZ)) // 时间不满足也任务没有hungtask return false; return true; } static void check_hung_task(struct task_struct *t, unsigned long timeout, unsigned long prev_detect_count) { if (!task_is_hung(t, timeout)) return; ... 输出信息 } static void check_hung_uninterruptible_tasks(unsigned long timeout) { ... rcu_read_lock(); for_each_process_thread(g, t) { // 对每个任务都检测一遍 if (!max_count--) goto unlock; if (time_after(jiffies, last_break HUNG_TASK_LOCK_BREAK)) { if (!rcu_lock_break(g, t)) goto unlock; last_break jiffies; } check_hung_task(t, timeout, prev_detect_count); // 检测是否hung_task } unlock: rcu_read_unlock(); if (!(sysctl_hung_task_detect_count - prev_detect_count)) return; if (need_warning || hung_task_call_panic) { si_mask | SYS_INFO_LOCKS; if (sysctl_hung_task_all_cpu_backtrace) si_mask | SYS_INFO_ALL_BT; } sys_info(si_mask); if (hung_task_call_panic) panic(hung_task: blocked tasks); } /* * kthread which checks for tasks stuck in D state */ static int watchdog(void *dummy) { unsigned long hung_last_checked jiffies; set_user_nice(current, 0); for ( ; ; ) { unsigned long timeout sysctl_hung_task_timeout_secs; unsigned long interval sysctl_hung_task_check_interval_secs; long t; if (interval 0) interval timeout; interval min_t(unsigned long, interval, timeout); t hung_timeout_jiffies(hung_last_checked, interval); if (t 0) { if (!atomic_xchg(reset_hung_task, 0) !hung_detector_suspended) check_hung_uninterruptible_tasks(timeout); hung_last_checked jiffies; continue; } schedule_timeout_interruptible(t); } return 0; }总结hungtask是从任务task维度的检测看某个任务是否长时间处于D状态。如等锁/等IO…softlockupftlockup 检测的不是“CPU 完全没中断”而是某个 CPU 长时间卡在内核态导致 watchdog 自己那个需要被调度执行的‘喂狗’工作一直跑不起来。是cpu维度的对每个cpu进行检测。简单理解查看调度器是不是正常工作原理当前实现可以概括成这几步watchdog_enable() 给每个 CPU 启一个 per-cpu 的 hrtimer这个 timer 周期触发 watchdog_timer_fn()watchdog_timer_fn() 会异步在本 CPU 上安排一次 softlockup_fn()softlockup_fn() 真正执行时会调用 update_touch_ts()刷新这个 CPU 的“最后一次成功被调度运行”的时间戳下一次 watchdog_timer_fn() 再进来时用 is_softlockup() 比较“当前时间”和这个时间戳如果超过 soft 阈值就打印栈、模块、IRQ 信息必要时 panic源码解析初始化lockup_detector_init cpumask_copy(watchdog_cpumask, // 只对housekeeping的cpus如果是隔离核不检查 housekeeping_cpumask(HK_TYPE_TIMER)); lockup_detector_setup mutex_lock(watchdog_mutex); __lockup_detector_reconfigure(false); --重新配置detector softlockup_initialized true; mutex_unlock(watchdog_mutex);设置detectorstatic void __lockup_detector_reconfigure(bool thresh_changed) { cpus_read_lock(); // 拿cpu上下线读锁 watchdog_hardlockup_stop();// 停止 softlockup_stop_all(); // 停止软检测 /* * To prevent watchdog_timer_fn from using the old interval and * the new watchdog_thresh at the same time, which could lead to * false softlockup reports, it is necessary to update the * watchdog_thresh after the softlockup is completed. */ if (thresh_changed) watchdog_thresh READ_ONCE(watchdog_thresh_next); set_sample_period(); lockup_detector_update_enable(); if (watchdog_enabled watchdog_thresh) softlockup_start_all(); watchdog_hardlockup_start(); cpus_read_unlock(); } // 每个cpu都执行一次start fn使能定时器 static void softlockup_start_all(void) { int cpu; cpumask_copy(watchdog_allowed_mask, watchdog_cpumask); // 对每个cpu都执行 softlockup_start_fn for_each_cpu(cpu, watchdog_allowed_mask) smp_call_on_cpu(cpu, softlockup_start_fn, NULL, false); } // 使能看门狗其实就是开了定时器 static void watchdog_enable(unsigned int cpu) { struct hrtimer *hrtimer this_cpu_ptr(watchdog_hrtimer); struct completion *done this_cpu_ptr(softlockup_completion); WARN_ON_ONCE(cpu ! smp_processor_id()); init_completion(done); complete(done); /* * Start the timer first to prevent the hardlockup watchdog triggering * before the timer has a chance to fire. */ hrtimer_setup(hrtimer, watchdog_timer_fn, CLOCK_MONOTONIC, HRTIMER_MODE_REL_HARD); hrtimer_start(hrtimer, ns_to_ktime(sample_period), HRTIMER_MODE_REL_PINNED_HARD); /* Initialize timestamp */ update_touch_ts(); // 初始化时间戳 /* Enable the hardlockup detector */ if (watchdog_enabled WATCHDOG_HARDLOCKUP_ENABLED) watchdog_hardlockup_enable(cpu); }如何检测watchdog_timer_fn ... // 如果softlockup_completion 完成了则重新开一个避免每次进来都开一个kthead // 这里其实是一个内核线程softlockup_fn调用update_touch_ts更新喂狗时间 // 如果起了一个worker但实际一直这个worker没有得到调度喂狗时间回一直落后 /* kick the softlockup detector */ if (completion_done(this_cpu_ptr(softlockup_completion))) { reinit_completion(this_cpu_ptr(softlockup_completion)); stop_one_cpu_nowait(smp_processor_id(), softlockup_fn, NULL, this_cpu_ptr(softlockup_stop_work)); } now get_timestamp(); // 获取当前时间 /* Check for a softlockup. */ touch_ts __this_cpu_read(watchdog_touch_ts); // 获取喂狗时间,记录在percpu的变量上 duration is_softlockup(touch_ts, period_ts, now); // 通过喂狗时间 touch_ts 与now对比如果大于两个设置的周期则认为有softlock。 if (unlikely(duration)) { // 有softlock 打印信息总结依赖时钟中断检测说明有时间但是调度器不正常导致长时间不喂狗。异常场景如for循环太久一直没有让出cpuhard lock某个 CPU 长时间卡在内核里连定时中断这类正常中断都没法推进cpu维度的原理共同核心无论是 perf 版还是 buddy 版最后都走公共判断函数 watchdog.c:187 的 watchdog_hardlockup_check()。它真正检查的依据非常简单每个 CPU 都有一个 hrtimer_interrupts 计数正常情况下本地 watchdog hrtimer 周期性触发会把这个计数加一检查时如果发现某个 CPU 的 hrtimer_interrupts 和上次保存值完全一样就认为这个 CPU 很可能硬锁死了通过什么机制检测连中断都没有了这个是通过其他cpu来检测的。watchdog_timer_fn // 看门狗fn watchdog_hardlockup_kick watchdog_buddy_check_hardlockup /* check for a hardlockup on the next CPU */ next_cpu watchdog_next_cpu(smp_processor_id()); // 这里取得是下一个cpu的 if (next_cpu nr_cpu_ids) return; watchdog_hardlockup_check is_hardlockup { int hrint atomic_read(per_cpu(hrtimer_interrupts, cpu)); if (per_cpu(hrtimer_interrupts_saved, cpu) hrint) // 如果这个中断没有变说明有hard lockup了 return true; /* * NOTE: we dont need any fancy atomic_t or READ_ONCE/WRITE_ONCE * for hrtimer_interrupts_saved. hrtimer_interrupts_saved is * written/read by a single CPU. */ per_cpu(hrtimer_interrupts_saved, cpu) hrint; return false; }总结hardlockup 是检测连中断没有情况下依赖于上一个索引cpu帮忙检测自己的cpu是不是卡主了。deadlock在每次加锁时记录“已持有锁 - 新锁”的依赖边并在加入新边前搜索依赖图是否成环一旦成环就立即报告潜在死锁。原理看当前 task 已经持有哪些锁对“已有锁 - 新锁”准备加一条依赖边先检查这条边会不会形成环没问题才把依赖正式加入图怎么判断会不会形成死锁最核心的逻辑在 lockdep.c:3121 的 check_prev_add()准备加新边 prev - next先从 next 出发做一次 BFS看能不能走回 prev如果能走回去就形成环prev - … - nextnext - … - prev这就是典型锁顺序反转死锁。源码注释写得很清楚见 lockdep.c:3155源码解析待更新rcu检测一个 grace period 开始后RCU 等太久还没等到所有必须的 quiescent state于是判定“有人把 GP 拖住了”。总结hungtask是检测任务是否长时间处于D状态task维度softlock是检测有中断但是软件异常导致调度不正常的场景cpu维度hardlock是检测连中断都没有的场景。cpu维度RCU 等太久还没等到所有必须的 quiescent state于是判定“有人把 GP 拖住了deadlock是检测有循环依赖关系依赖成环。rcu stall 跟 softlock对比这两个应该比较像softlockup 检测的是某个 CPU 长时间在内核态不调度别的任务RCU stall 检测的是某个 grace period 长时间结束不了所以两者经常一起出现是因为它们的根因常常重叠但判定对象并不一样。为什么经常一起出现典型重叠场景是某 CPU 在内核里长时间死循环长时间关中断 / 关抢占RCU softirq、RCU GP kthread、调度时钟都得不到运行机会这时会同时满足watchdog 觉得这个 CPU 很久没被正常调度触发 softlockupRCU 发现 grace period 一直收不到 quiescent state触发 RCU stallRCU stall 不一定伴随 softlockup只要 GP 被拖住就会报例如某个 task 在 PREEMPT_RCU 下长期挂着 read-side critical section或者 rcu_gp_kthread 被饿死或者 timer/wakeup 出问题这些情况系统未必满足“某 CPU 20 秒不调度”的 softlockup 条件。相关打印路径见 tree_stall.h:631 和 tree_stall.h:568。反过来softlockup 也不一定伴随 RCU stall当时可能根本没有 grace period 在进行或者虽然 CPU 很忙但没挡住当前 GP 结束RCU 文档直接写了No grace period, no CPU stall warnings见 stallwarn.rst:125。一句话判断两者经常同因共现但没有必然因果softlockup 更像“CPU 调度/执行卡住”RCU stall 更像“RCU 前进性被破坏”