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

INFO: task filebench: blocked for more than  seconds.
Oct :: localhost kernel: "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.

如是,查了一下为什么会出现这种情况,以及为什么进程的状态显示D之后是kill不了的。

一、相关知识
长期以来,处于D状态(TASK_UNINTERRUPTIBLE状态)的进程都是让人比较烦恼的问题,处于D状态的进程不能接收信号,kill不掉。在一些场景下,常见到进程长期处于D状态,用户对此无能为力,也不知道原因,只能重启恢复。
其实进程长期处于D状态肯定是不正常的,内核中设计D状态的目的是为了让进程等待IO完成,正常情况下IO应该会顺利完成,然后唤醒相应的D状态进程,即使在异常情况下(比如磁盘离或损坏、磁阵链路断开等),IO处理也是有超时机制的,原理上不会存在永久处于D状态的进程。但是就是因为内核代码流程中可能存在一些bug,或者用户内核模块中的相关机制不合理,可能导致进程长期处于D状态,无法唤醒,类似于死锁状态。
针对这种情况,内核中提供了hung task机制用于检测系统中是否存在处于D状态超过120s(时长可以设置)的进程,如果存在,则打印相关警告和进程堆栈。如果配置了hung_task_panic(proc或内核启动参数),则直接发起panic,结合kdump可以搜集到vmcore。从内核的角度看,如果有进程处于D状态的时间超过了120s,那肯定已经出现异常了,以此机制来收集相关的异常信息,用于分析定位问题。

二、基本原理
hung task机制的实现很简单,其基本原理为:
创建一个内核线程(khungtaskd),定期(120)唤醒后,遍历系统中的所有进程,检查是否存在处于D状态超过120s(时长可以设置)的进程,如果存在,则打印相关警告和进程堆栈。如果配置了hung_task_panic(proc或内核启动参数),则直接发起panic。

三、代码分析
1、初始化
hung_task_init():

  1. /*
  2. * hung task机制,初始化一个内核线程来检测系统中是否存在D状态超过120s的
  3. * 进程
  4. */
  5. static int __init hung_task_init(void)
  6. {
  7. /*注册panic通知链,在panic时执行相关操作。*/
  8. atomic_notifier_chain_register(&panic_notifier_list, &panic_block);
  9. /*创建内核线程khungtaskd,执行函数为watchdog*/
  10. watchdog_task = kthread_run(watchdog, NULL, "khungtaskd");
  11. return 0;
  12. }

2、内核线程处理:watchdog
watchdog():

  1. /*
  2. * kthread which checks for tasks stuck in D state
  3. */
  4. /*hung task机制中khungtaskd内核线程的处理函数*/
  5. static int watchdog(void *dummy)
  6. {
  7. /*设置当前khungtaskd内核线程的nice为0,即普通优先级,为了不影响业务运行*/
  8. set_user_nice(current, 0);
  9. /*死循环进行检测*/
  10. for ( ; ; ) {
  11. /*进程处于D状态的时间上线可通过sysctl/proc控制,默认为120s*/
  12. unsigned long timeout = sysctl_hung_task_timeout_secs;
  13. /*检测线程(watchdog)sleep 120s(默认)后,再次唤醒。*/
  14. while (schedule_timeout_interruptible(timeout_jiffies(timeout)))
  15. timeout = sysctl_hung_task_timeout_secs;
  16. /*醒来后执行实际的检测操作*/
  17. check_hung_uninterruptible_tasks(timeout);
  18. }
  19. return 0;
  20. }

watchdog()->check_hung_uninterruptible_tasks():

  1. /*
  2. * Check whether a TASK_UNINTERRUPTIBLE does not get woken up for
  3. * a really long time (120 seconds). If that happens, print out
  4. * a warning.
  5. */
  6. /*遍历系统中的所有进程,检测是否有处于D状态超过120的进程,如果有则打印告警或panic*/
  7. static void check_hung_uninterruptible_tasks(unsigned long timeout)
  8. {
  9. /*hung task检测是检查的最大进程数,默认为最大的进程号*/
  10. int max_count = sysctl_hung_task_check_count;
  11. /*
  12. * 每次遍历进程数的上限,默认为1024,这样做的目的是为了:
  13. * 1、防止rcu_read_lock的占用时间太长。
  14. * 2、hung task的watchdog占用CPU时间太长。如果没开内核抢占,则如果内核线程不主动调度的话,是不能发生进程切换的?
  15. */
  16. /*
  17. *Fixme:如果系统中的进程数比较多,那么就可能检测不到部分D状态进程了?不会,因为这里只是会调度一次,调度回来
  18. *后,会继续遍历后面的进程*/
  19. int batch_count = HUNG_TASK_BATCHING;
  20. struct task_struct *g, *t;
  21. /*
  22. * If the system crashed already then all bets are off,
  23. * do not report extra hung tasks:
  24. */
  25. /*如果系统已经处于crash状态了,就不在报hung task了。*/
  26. if (test_taint(TAINT_DIE) || did_panic)
  27. return;
  28. rcu_read_lock();
  29. /*遍历系统中的所有进程*/
  30. do_each_thread(g, t) {
  31. if (!max_count--)
  32. goto unlock;
  33. /*如果每次检测的进程数量超过1024了,则需要发起调度,结束rcu优雅周期*/
  34. if (!--batch_count) {
  35. batch_count = HUNG_TASK_BATCHING;
  36. /*释放rcu,并主动调度,调度回来后检查相应进程是否还在,如果不在了,则退出遍历,否则继续*/
  37. if (!rcu_lock_break(g, t))
  38. goto unlock;
  39. }
  40. /* use "==" to skip the TASK_KILLABLE tasks waiting on NFS */
  41. /*检测进程状态是否为D*/
  42. if (t->state == TASK_UNINTERRUPTIBLE)
  43. /*检测进程处于D状态的时间是否超过120s。*/
  44. check_hung_task(t, timeout);
  45. } while_each_thread(g, t);
  46. unlock:
  47. rcu_read_unlock();
  48. }

watchdog()->check_hung_uninterruptible_tasks()->check_hung_task():

  1. static void check_hung_task(struct task_struct *t, unsigned long timeout)
  2. {
  3. /*进程上下文切换计数,以此来判断该进程是否发生过调度*/
  4. unsigned long switch_count = t->nvcsw + t->nivcsw;
  5. /*
  6. * Ensure the task is not frozen.
  7. * Also, skip vfork and any other user process that freezer should skip.
  8. */
  9. if (unlikely(t->flags & (PF_FROZEN | PF_FREEZER_SKIP)))
  10. return;
  11. /*
  12. * When a freshly created task is scheduled once, changes its state to
  13. * TASK_UNINTERRUPTIBLE without having ever been switched out once, it
  14. * musn't be checked.
  15. */
  16. if (unlikely(!switch_count))
  17. return;
  18. /*
  19. * 如果当前switch_count不等于last_switch_count,则说明在khungtaskd进程被唤醒期间,该进程没有发生过调度。
  20. * 也就是说,该进程一直处于D状态,因为last_switch_count只在这里更新,其它地方不会。
  21. * hung task机制中的120s其实是通过khungtaskd内核线程的唤醒周期来控制的,不是通过per task其它计数。
  22. */
  23. if (switch_count != t->last_switch_count) {
  24. /*更新last_switch_count计数,只在这里更新,该计数专用于hung task的检测。*/
  25. t->last_switch_count = switch_count;
  26. return;
  27. }
  28. /*
  29. * hung task错误打印次数限制,防止dos攻击。默认为10次,由于是全局变量,
  30. * 表示系统运行期间最多打印10次,超过后就不打印了。该参数应该可以
  31. * 通过sysctl修改
  32. */
  33. if (!sysctl_hung_task_warnings)
  34. return;
  35. sysctl_hung_task_warnings--;
  36. /*
  37. * Ok, the task did not get scheduled for more than 2 minutes,
  38. * complain:
  39. */
  40. /*如下就是我们平常常见的hung task打印了*/
  41. printk(KERN_ERR "INFO: task %s:%d blocked for more than "
  42. "%ld seconds.\n", t->comm, t->pid, timeout);
  43. printk(KERN_ERR "\"echo 0 > /proc/sys/kernel/hung_task_timeout_secs\""
  44. " disables this message.\n");
  45. /*打印堆栈*/
  46. sched_show_task(t);
  47. /*如果开启了debug_lock,则打印锁的占用情况*/
  48. debug_show_held_locks(t);
  49. /*touch nmi_watchdog相关的计数器,防止在此过程中触发nmi_watchdog*/
  50. touch_nmi_watchdog();
  51. /*检测是否配置了/proc/sys/kernel/hung_task_panic,如果配置则直接触发panic*/
  52. if (sysctl_hung_task_panic) {
  53. /*打印所有CPU的堆栈*/
  54. trigger_all_cpu_backtrace();
  55. /*触发panic,如果配置了kdump就有用了*/
  56. panic("hung_task: blocked tasks");
  57. }
  58. }

hung task机制的更多相关文章

  1. kernel 3.10内核源码分析--hung task机制

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

  2. Linux Hung Task分析

    关键词:khungtaskd.TASK_UNINTERRUPTIBLE.nvcsw.nivcsw.last_switch_count等等. 经常会遇到内核打印“INFO: task xxx:xxx b ...

  3. linux 的那些hung 检测机制

    在dmesg中,看到如下信息: [:: seconds [:: seconds [:af: seconds [:af: seconds [:: seconds [:3b: seconds [:: se ...

  4. linux 出错 “INFO: task xxxxxx: 634 blocked for more than 120 seconds.”的3种解决方案(转)

    linux 出错 “INFO: task xxxxxx: 634 blocked for more than 120 seconds.”的3种解决方案 1 问题描述 服务器内存满了,ssh登录失败 , ...

  5. linux 出错 “INFO: task java: xxx blocked for more than 120 seconds.” 的3种解决方案

    1 问题描述 最近搭建的一个linux最小系统在运行到241秒时在控制台自动打印如下图信息,并且以后每隔120秒打印一次. 仔细阅读打印信息发现关键信息是“hung_task_timeout_secs ...

  6. Handler机制原理图、源码、使用!!!!!

    android的消息处理机制——Looper,Handler,Message  (原理图.源码) 转自:http://my.oschina.net/u/1391648/blog/282892 在开始讨 ...

  7. .NET中的async和await关键字使用及Task异步调用实例

    其实早在.NET 4.5的时候M$就在.NET中引入了async和await关键字(VB为Async和Await)来简化异步调用的编程模式.我也早就体验过了,现在写一篇日志来记录一下顺便凑日志数量(以 ...

  8. ZWave 中的消息队列机制

    文章主题   在我们的日常编程中,对消息队列的需求非常常见,使用一个简洁.高效的消息队列编程模型,对于代码逻辑的清晰性,对于事件处理的高效率来说,是非常重要的.这篇文章就来看看 ZWave 中是通过什 ...

  9. [源码解析] 并行分布式任务队列 Celery 之 Task是什么

    [源码解析] 并行分布式任务队列 Celery 之 Task是什么 目录 [源码解析] 并行分布式任务队列 Celery 之 Task是什么 0x00 摘要 0x01 思考出发点 0x02 示例代码 ...

随机推荐

  1. 为什么你用不好Numpy的random函数?

    为什么你用不好Numpy的random函数? 在python数据分析的学习和应用过程中,经常需要用到numpy的随机函数,由于随机函数random的功能比较多,经常会混淆或记不住,下面我们一起来汇总学 ...

  2. PC客户端开发细节记录:保存GUID到VARIANT

    有两个 API 可以实现保存 GUID 到 VARIANT InitVariantFromGUIDAsBuffer 以字节数组形式保存,保存类型为 VT_ARRAY | VT_UI1,相当于字节拷贝, ...

  3. 白盒测试实践-DAY1

    时间:2017.12.11 地点:软件学院 成员:张玉.周静.张双双 会议内容:讨论题目要求,分配任务 针对第一阶段的任务进行部署,共同学习白盒测试方法,根据自己选择的系统--餐厅网站,针对其中的管理 ...

  4. Oracle EBS OPM complete batch

    --complete_batch --created by jenrry SET serveroutput on DECLARE x_return_status VARCHAR2 (1); l_exc ...

  5. Oracle中,时间的相关操作方法

    无论是DATE还是timestamp都可以进行加减操作.可以对当前日期加年.月.日.时.分.秒,操作不同的时间类型,有三种方法: 1 使用内置函数numtodsinterval增加小时,分钟和秒2 加 ...

  6. seq 序列

    seq 序列用法: seq [option]...last seq [option]...first last seq [option]...first increment last 例如:seq 5 ...

  7. Coursera-AndrewNg(吴恩达)机器学习笔记——第四周

    神经网络 1.神经网络发展的动力:在逻辑回归解决复杂的分类问题时,我们使用属性的一些组合来构造新的属性(x12,x1x2,x22...),这样就会造成属性的数目n过多,带来了大量的运算,甚至造成过拟合 ...

  8. ES6+转ES5

    npm init //创建package.json文件 下载转换babel库及其100+依赖 npm install babel-cli -D npm install babel-preset-env ...

  9. 开通博客啦 Let‘s Go!

    入园两年半,在博客园学到很多知识.得到了很大帮助,今天终于开通博客啦,准备将自己所学到的有用知识分享给大家,共同学习共同进步.

  10. 将jar包添加到maven仓库

    Maven资源库配置 访问http://mvnrepository.com/,在搜索栏中输入你要搜索的 JAR 包的关键字 例如下载ImpalaJDBC41这个jar包   选择你想要下载的Jar包版 ...