Linux Hung Task分析

时间:2022-09-28 20:12:01

关键词:khungtaskd、TASK_UNINTERRUPTIBLE、nvcsw、nivcsw、last_switch_count等等。

经常会遇到内核打印“INFO: task xxx:xxx blocked for more than 120 seconds.”这样的log信息,这是内核的hung task机制在起作用。

hung task机制通过内核线程khungtaskd来实现的,khungtaskd监控TASK_UNINTERRUPTIBLE状态的进程,如果在120s周期内没有切换,就会打印详细信息。

1. hung task背景

处于D状态,即TASK_UNINTERRUPTIBLE状态的进程,不能接收kill信号。

如果一个进程长期处于D状态,用户往往无能为力。

进程处于长期处于D状态是不正常的,内核设计D状态目的是为了让进程等待IO完成,正常情况下IO应该会瞬息完成,然后唤醒响应D装固态进程。

即使在异常情况下,IO处理也有超时机制,原则上不应是进程长期处于D状态。

如果进程长期处于D状态,一是IO设备损坏,或者是内核中存在bug或机制不合理,导致进程长期处于D状态,无法唤醒。

针对这种情况,内核提供了hung task机制用于检测系统中是否有处于D状态进程超过120s没有切换过;如果存在则打印相关警告和堆栈。

2. hung task基本原理

hung task的实现通过创建khungtaskd内核线程,定期120s唤醒一次;

然后遍历内核所有进程,需要满足两个条件:进程处于TASK_UNINTERRUPTIBLE,并且nvcsw+nivcsw==last_switch_count;

最后打印进程信息和堆栈。

3. hung task代码分析

3.1 task_strcut中hung task相关成员

在进行hung task分析之前,需要了解struct task_strcut中的state、nvcsw、nivcsw、last_switch_count几个成员含义。

struct task_struct {
...
volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */---------------当前进程状态,TASK_UNINTERRUPTIBLE表示进程不会被打断。
...
unsigned long nvcsw, nivcsw; /* context switch counts */--------------------------nvcsw表示进程主动切换次数,nivcsw表示进程被动切换次数,两者之和就是进程总的切换次数。...
#ifdef CONFIG_DETECT_HUNG_TASK
/* hung task detection */
unsigned long last_switch_count;--------------------------------------------------这个变量只有两个地方修改,一是在新建进程的时候设置初始值last_switch_count=nvcsw+nivcsw。另一个是在khungtaskd中进行更新。
#endif
...
};

3.2 khungtaskd线程创建

watchdog()是khuangtaskd线程主函数,线程每隔sysctl_hung_task_timeout_secs醒来一次,调用check_hung_uninterruptible_tasks()检查所有进程。

static int watchdog(void *dummy)
{
unsigned long hung_last_checked = jiffies; set_user_nice(current, );---------------------------------------------------设置当前进程nice为0,即普通优先级。 for ( ; ; ) {
unsigned long timeout = sysctl_hung_task_timeout_secs;-------------------获取进程hung时间上限。
long t = hung_timeout_jiffies(hung_last_checked, timeout); if (t <= ) {
if (!atomic_xchg(&reset_hung_task, ))
check_hung_uninterruptible_tasks(timeout);
hung_last_checked = jiffies;
continue;
}
schedule_timeout_interruptible(t);-----------------------------------------休眠sysctl_hung_task_timeout_secs秒。
} return ;
} static int __init hung_task_init(void)
{
atomic_notifier_chain_register(&panic_notifier_list, &panic_block);------------注册panic通知链,在panic时执行相关操作。
watchdog_task = kthread_run(watchdog, NULL, "khungtaskd");---------------------创建内核线程khungtaskd。 return ;
}
subsys_initcall(hung_task_init);

panic_block注册到panic_notifier_list通知链表上,如果系统产生panic,那么did_panic就会被置1。

static int
hung_task_panic(struct notifier_block *this, unsigned long event, void *ptr)
{
did_panic = ; return NOTIFY_DONE;
} static struct notifier_block panic_block = {
.notifier_call = hung_task_panic,
};

3.3 检查进程是否hung

check_hung_uninterruptible_tasks()遍历内核中所有进程、线程,首先判断状态是否是TASK_UNINTERRUPTIBLE。

static void check_hung_uninterruptible_tasks(unsigned long timeout)
{
int max_count = sysctl_hung_task_check_count;-------------------检测最大进程数,默认为最大进程号。
int batch_count = HUNG_TASK_BATCHING;---------------------------每次遍历进程数上限1024。
struct task_struct *g, *t; /*
* If the system crashed already then all bets are off,
* do not report extra hung tasks:
*/
if (test_taint(TAINT_DIE) || did_panic)
return; rcu_read_lock();
for_each_process_thread(g, t) {
if (!max_count--)
goto unlock;
if (!--batch_count) {
batch_count = HUNG_TASK_BATCHING;
if (!rcu_lock_break(g, t))--------------------------------防止rcu_read_lock占用过长时间。释放rcu,并主动调度。调度回来后检查响应进程是否还在,不在则退出遍历,否则继续。
goto unlock;
}
/* use "==" to skip the TASK_KILLABLE tasks waiting on NFS */
if (t->state == TASK_UNINTERRUPTIBLE)-------------------------khungtaskd只监控TASK_UNINTERRUPTIBLE状态的进程线程。
check_hung_task(t, timeout);
}
unlock:
rcu_read_unlock();
} static void check_hung_task(struct task_struct *t, unsigned long timeout)
{
unsigned long switch_count = t->nvcsw + t->nivcsw;----------------表示线程总的切换次数,包括主动和被动的。 /*
* Ensure the task is not frozen.
* Also, skip vfork and any other user process that freezer should skip.
*/
if (unlikely(t->flags & (PF_FROZEN | PF_FREEZER_SKIP)))
return; /*
* When a freshly created task is scheduled once, changes its state to
* TASK_UNINTERRUPTIBLE without having ever been switched out once, it
* musn't be checked.
*/
if (unlikely(!switch_count))
return; if (switch_count != t->last_switch_count) {-------------------------如果总切换次数和last_switch_count不等,表示在上次khungtaskd更新last_switch_count之后就发生了进程切换;反之,相等则表示120s时间内没有发生切换。
t->last_switch_count = switch_count;----------------------------更新last_switch_count。
return;
} trace_sched_process_hang(t); if (!sysctl_hung_task_warnings && !sysctl_hung_task_panic)----------如果不使能warning和panic,返回。
return; /*
* Ok, the task did not get scheduled for more than 2 minutes,
* complain:
*/
if (sysctl_hung_task_warnings) {------------------------------------hung task错误打印次数限制,默认为10次,整个系统运行期间最多打印10次。
sysctl_hung_task_warnings--;
pr_err("INFO: task %s:%d blocked for more than %ld seconds.\n",
t->comm, t->pid, timeout);
pr_err(" %s %s %.*s\n",
print_tainted(), init_utsname()->release,
(int)strcspn(init_utsname()->version, " "),
init_utsname()->version);
pr_err("\"echo 0 > /proc/sys/kernel/hung_task_timeout_secs\""
" disables this message.\n");
sched_show_task(t);----------------------------------------------显示进程ID、名称、状态以及栈等信息。
debug_show_all_locks();------------------------------------------如果使能debug_locks,则打印进程持有的锁。
} touch_nmi_watchdog(); if (sysctl_hung_task_panic) {
trigger_all_cpu_backtrace();
panic("hung_task: blocked tasks");
}
}

下面看一下进程详细信息:

void sched_show_task(struct task_struct *p)
{
unsigned long free = ;
int ppid;
unsigned long state = p->state; if (!try_get_task_stack(p))
return;
if (state)
state = __ffs(state) + ;
printk(KERN_INFO "%-15.15s %c", p->comm,
state < sizeof(stat_nam) - ? stat_nam[state] : '?');------------------进程名称和状态,这里应该是D。
if (state == TASK_RUNNING)
printk(KERN_CONT " running task ");
#ifdef CONFIG_DEBUG_STACK_USAGE
free = stack_not_used(p);
#endif
ppid = ;
rcu_read_lock();
if (pid_alive(p))
ppid = task_pid_nr(rcu_dereference(p->real_parent));
rcu_read_unlock();
printk(KERN_CONT "%5lu %5d %6d 0x%08lx\n", free,
task_pid_nr(p), ppid,
(unsigned long)task_thread_info(p)->flags);------------------------------free表示栈空闲量;第二个表示线程/进程pid;第三个表示父进程pid;最后一个表示进程的flags。 print_worker_info(KERN_INFO, p);
show_stack(p, NULL);
put_task_stack(p);
}

如下log可以得到recvComm进程,pid为175,父进程为148,当前状态是D;当前hung的栈是read调用,卡在usb_sourceslink_read()函数。

Linux Hung Task分析

4. 对khungtaskd的配置

通过sysctl或者在/proc/sys/kernel/中进行配置:

hung_task_panic------------------------是否在检测到hung后panic,默认值0

hung_task_check_count---------------最大检查task数量,默认值32768

hung_task_timeout_secs--------------超时时间,默认值120

hung_task_warnings--------------------打印hung warning的次数,默认值10

还可以通过bootargs对hung后是否panic进行设置。

/*
* Should we panic (and reboot, if panic_timeout= is set) when a
* hung task is detected:
*/
unsigned int __read_mostly sysctl_hung_task_panic =
CONFIG_BOOTPARAM_HUNG_TASK_PANIC_VALUE; static int __init hung_task_panic_setup(char *str)
{
int rc = kstrtouint(str, , &sysctl_hung_task_panic); if (rc)
return rc;
return ;
}
__setup("hung_task_panic=", hung_task_panic_setup);

Linux Hung Task分析的更多相关文章

  1. kernel 3&period;10内核源码分析--hung task机制

    kernel 3.10内核源码分析--hung task机制 一.相关知识: 长期以来,处于D状态(TASK_UNINTERRUPTIBLE状态)的进程 都是让人比较烦恼的问题,处于D状态的进程不能接 ...

  2. hung task机制

    最近在修改内核源码的时候一直出现格式化磁盘的时候,进程会出现状态D,看内核日志会看到如下信息: INFO: task filebench: blocked seconds. Oct :: localh ...

  3. linux用户进程分析

           经过实验3的介绍.我们须要来点实在的.所以将我们理解的流程用于linux系统的分析.换句话说.通过类比的方式去进行描写叙述与理解linux相关的部分. 本节的内容非常详实.并且也分析 ...

  4. 【原创】Linux信号量机制分析

    背景 Read the fucking source code! --By 鲁迅 A picture is worth a thousand words. --By 高尔基 说明: Kernel版本: ...

  5. 《Unix&sol;Linux网络日志分析与流量监控》获2015年度最受读者喜爱的IT图书奖

    <Unix/Linux网络日志分析与流量监控>获2015年度最受读者喜爱的IT图书奖.刊登在<中华读书报>( 2015年01月28日 19 版) 我的2015年新作刊登在&lt ...

  6. Linux Bluetooth内核分析

    目录 1. 初始化 2. hci部分 Linux提供了对Bluetooth的支持,核心代码位于net/bluetooth 1. 初始化 主要由subsys_initcall调用函数bt_init()来 ...

  7. Linux之kernal分析与启动20160610

    说一下LINUX内核的分析与启动: 一. 内核启动流程,据此配置内核(机器ID) 1.1 修改Makefile 1.2 选择默认配置 : make s3c2410_defconfig 1.3 make ...

  8. linux源码分析2

    linux源码分析 这里使用的linux版本是4.8,x86体系. 这篇是 http://home.ustc.edu.cn/~boj/courses/linux_kernel/1_boot.html  ...

  9. Linux网络地址转换分析

    Linux网络地址转换分析 地址转换用来改变源/目的端口,是netfilter的一部分,也是通过hook点上注册相应的结构来工作. Nat注册的hook点和conntrack相同,只是优先级不同,数据 ...

随机推荐

  1. Hibernate整合Spring异常&&num;39&semi;sessionFactory&&num;39&semi; or &&num;39&semi;hibernateTemplate&&num;39&semi; is required

    今日在写GenericDao时,发现了一个异常,内容如下: org.springframework.beans.factory.BeanCreationException: Error creatin ...

  2. Eclipse中web项目部署至Tomcat步骤

    Eclipse的web工程至Tomcat默认的部署目录是在工程空间下,本文旨在将部署目录改为Tomcat安装目录,并解决依赖包输出问题. 1.在Eclipse中添加Tomcat服务器. 2.将web工 ...

  3. Android中事件传递机制的总结

    事件传递虽然算不上某个单独的知识点,但是在实际项目开发中肯定会碰到,如果不明白其中的原理,那在设计各种滑动效果时就会感到很困惑. 关于事件的传递,我们可能会有以下疑问: 事件是如何传递的 事件是如何处 ...

  4. ecshop获取url&lowbar;domain

    <?php function url_domain() { $curr = strpos($_SERVER['PHP_SELF'], '/') !== false ? preg_replace( ...

  5. 《Pointers On C》读书笔记&lpar;第四章 语句&rpar;

    1.空语句只包含一个分号,它本身并不执行任何任务,其适用的场合是语法要求出现一条完整的语句,但并不需要它执行任何任务. 2.C语言中并不存在专门的“赋值语句”,赋值就是一种操作,在表达式内进行.通过在 ...

  6. R语言入门(一)简介安装

    数据挖掘常用的语言有R语言,python,SQL等,其中R语言最受欢迎.(注:SQL Server包含微软研究院开发的两种数据挖掘算法:Microsoft决策树和Microsoft聚集,此外还支持第三 ...

  7. intelliJ idea常用快捷键 mac版

    目录 coding project coding Command + P 显示方法参数信息 Command + N 自动生成getter.setter.hashCode.equals.toString ...

  8. Android中获取文件路径的方法总结及对照

    最近在写文件存贮,Android中获取文件路径的方法比较多,所以自己也很混乱.找了好几篇博客,发现了以下的路径归纳,记录一下,以备不时之需 Environment.getDataDirectory() ...

  9. Saltstack报错小记

    这是之前的一篇文章,由于有小伙伴也遇到同样的错误,就拿出来分享下吧 [root@master ~]# salt 'minion.saltstack.com' state.sls init.pkg[ER ...

  10. javascript中IE浏览器不支持NEW DATE&lpar;&rpar;带参数的解决方法

    代码如下: var date1=new Date(dateTimes[z][1]); 在火狐下 可以正常取得时间,在IE7下 却是 NaN.纠结老长时间,放弃了new date 然后再老外的论坛中找了 ...