Task Hung
针对不同级别的进程、CPU上可能发生的"hang",CPU/内核提供了4种检测机制,记录下比较简单的 task hung。
?:msleep(1)后,线程A在120s没有得到调度,hung task检测到线程A hung了。
debug选项
kernel/Makefile: obj-$(CONFIG_DETECT_HUNG_TASK) += hung_task.o
unsigned long __read_mostly sysctl_hung_task_timeout_secs = CONFIG_DEFAULT_HUNG_TASK_TIMEOUT;
启动watchdog_task(4中机制都沾了“watchdog”,但是真的不一样)
static int __init hung_task_init(void) { atomic_notifier_chain_register(&panic_notifier_list, &panic_block); watchdog_task = kthread_run(watchdog, NULL, "khungtaskd"); return 0; } module_init(hung_task_init);
"watchdog"函数,每120s调用一次check_hung_uninterruptible_tasks(timeout).
/* * We can use __set_current_state() here because schedule_timeout() calls * schedule() unconditionally. */ signed long __sched schedule_timeout_interruptible(signed long timeout) { __set_current_state(TASK_INTERRUPTIBLE); return schedule_timeout(timeout); } EXPORT_SYMBOL(schedule_timeout_interruptible); /* * kthread which checks for tasks stuck in D state */ static int watchdog(void *dummy) { set_user_nice(current, 0); for ( ; ; ) { unsigned long timeout = sysctl_hung_task_timeout_secs; while (schedule_timeout_interruptible(timeout_jiffies(timeout))) timeout = sysctl_hung_task_timeout_secs;
//原来的reset_hung_task作为返回值,并把reset_hung_task置成0 if (atomic_xchg(&reset_hung_task, 0)) continue; check_hung_uninterruptible_tasks(timeout); } return 0; }
看下check_hung_uninterruptible_tasks的说明:两个条件都得满足
/* * Check whether a TASK_UNINTERRUPTIBLE does not get woken up for * a really long time (120 seconds). If that happens, print out * a warning. */
先看第一个条件,很简单(忽略RCU)
static void check_hung_uninterruptible_tasks(unsigned long timeout) |-->//int sysctl_hung_task_check_count = PID_MAX_LIMIT; |-->int max_count = sysctl_hung_task_check_count; |-->struct task_struct *g, *t; |-->do_each_thread(g, t) { if (!max_count--) return; if (t->state == TASK_UNINTERRUPTIBLE) //首先是TASK_UNINTERRUPTIBLE check_hung_task(t, timeout); } while_each_thread(g, t); |
第二个条件,对于120s的检测
static void check_hung_task(struct task_struct *t, unsigned long timeout) |-->unsigned long switch_count = t->nvcsw + t->nivcsw; |-->不检测被frozen的task,对于刚被创建的task也需要特殊处理,除此而外: |-->if (switch_count != t->last_switch_count) {//如果在120s内被调度了,则switch_count必然不等于la t->last_switch_count = switch_count; return; } |-->//如果在120s内被调度了,则switch_count必然不等于last_switch_count,否则说明该task处于饥饿hung状态 |-->打印相关task信息,例如: |-->sched_show_task(t); | |-->一种情形:一个task_A等待某个资源时处于TASK_UNINTERRUPTIBLE,这是由于task_B持有该资源时间太长而导致的, |-->task_A 和 task_B可能处于不同的rq,处于不同的CPU,因此需要把其它CPU上当前运行的task也打印出来: |-->if (sysctl_hung_task_panic) { trigger_all_cpu_backtrace(); //发送IPI到其它CPU,使其dump其上的运行task的信息 panic("hung_task: blocked tasks"); } | 附:使用命令行选项控制是否需要其它CPU也dump相应信息 unsigned int sysctl_hung_task_panic = CONFIG_BOOTPARAM_HUNG_TASK_PANIC_VALUE; static int __init hung_task_panic_setup(char *str) { sysctl_hung_task_panic = simple_strtoul(str, NULL, 0); return 1; } __setup("hung_task_panic=", hung_task_panic_setup);
很自然的,在主调度函数__schedule()中,如果task_A将被切换为task_B. 此时task_A的调度次数将会被加1(++*switch_count)(为什么不是task_B?不过也无妨),即task->nivcsw++或者task->nvcsw++。顺便问下:nivcsw和nvcsw的区别是什么?
问题还是没结啊,明天把120s缩短下,也许可以加速复现……
浙公网安备 33010602011771号