在调试内核的时候,经常会碰到几个相近的概念:进程 stop、进程 park、进程 freeze。这几个名词看起来都是停止进程,那么他们之间的区别和应用场景在分别是什么呢?下面就来分析一番。

本文的代码分析基于 Linux kernel 3.18.22,最好的学习方法还是 “RTFSC”

1. 进程 stop

进程 stop 分成两种:用户进程 stop 和内核进程 stop。

用户进程 stop 可以通过给进程发送 STOP 信号来实现,可以参考“Linux Signal”这一篇的描述。但是对内核进程来说不会响应信号,如果碰到需要 stop 内核进程的场景怎么处理?比如:我们在设备打开的时候创建了内核处理进程,在设备关闭的时候需要 stop 内核进程。

Linux 实现了一套 ​​kthread_stop()​​ 的机制来实现内核进程 stop。

1.1 内核进程的创建

内核进程创建过程,是理解本篇的基础。

可以看到 ​​kthread_create()​​ 并不是自己去创建内核进程,而是把创建任务推送给 ​​kthreadd()​​进程执行。

​kthreadd()​​ -> ​​create_kthread()​​ -> ​​kernel_thread()​​ 创建的新进程也不是直接使用用户的函数 ​​threadfn()​​,而是创建通用函数 ​​kthread()​​,​​kthread()​​ 再来调用 ​​threadfn()​​。

  • kernel/kthread.c:

Linux 进程中 Stop, Park, Freeze【转】_linux​kthread_create

1.2 内核进程的 stop

如果内核进程需要支持 ​​kthread_stop()​​,需要根据以下框架来写代码。用户在主循环中调用 ​​kthread_should_stop()​​ 来判断当前 kthread 是否需要 stop,如果被 stop 则退出循环。

这种代码为什么不做到通用代码 ​​kthread()​​ 中?这应该是和 Linux 的设计思想相关的。Linux 运行内核态的策略比较灵活,而对用户态的策略更加严格统一。

Linux 进程中 Stop, Park, Freeze【转】_内核进程_02​kthread_should_stop

​kthread_should_stop()​​ 和 ​​kthread_stop()​​ 的代码实现:

  • kernel/kthread.c:
  • kthread_should_stop()
    /kthread_stop()


  1.  
  2. bool kthread_should_stop(void)
  3. {
  4. // (1) 判断进程所在 kthread 结构中的 KTHREAD_SHOULD_STOP 是否被置位
  5. return test_bit(KTHREAD_SHOULD_STOP, &to_kthread(current)->flags);
  6. }
  7.  
  8. int kthread_stop(struct task_struct *k)
  9. {
  10. struct kthread *kthread;
  11. int ret;
  12.  
  13. trace_sched_kthread_stop(k);
  14.  
  15. get_task_struct(k);
  16. kthread = to_live_kthread(k);
  17. if (kthread) {
  18. // (2) 置位进程所在 kthread 结构中的 KTHREAD_SHOULD_STOP
  19. set_bit(KTHREAD_SHOULD_STOP, &kthread->flags);
  20. // (3) unpark & wake_up 进程来响应 stop 信号
  21. __kthread_unpark(k, kthread);
  22. wake_up_process(k);
  23. wait_for_completion(&kthread->exited);
  24. }
  25. ret = k->exit_code;
  26. put_task_struct(k);
  27.  
  28. trace_sched_kthread_stop_ret(ret);
  29. return ret;
  30. }


2. 进程 park

​smpboot_register_percpu_thread()​​ 用来创建 per_cpu 内核进程,所谓的 per_cpu 进程是指需要在每个 online cpu 上创建线程。比如执行 ​​stop_machine()​​ 中 cpu 同步操作的 migration 进程:


  1. shell@:/ $ ps | grep migration
  2. root 10 2 0 0 smpboot_th 0000000000 S migration/0
  3. root 11 2 0 0 smpboot_th 0000000000 S migration/1
  4. root 15 2 0 0 __kthread_ 0000000000 R migration/2
  5. root 19 2 0 0 __kthread_ 0000000000 R migration/3
  6. root 207 2 0 0 __kthread_ 0000000000 R migration/8
  7. root 247 2 0 0 __kthread_ 0000000000 R migration/4
  8. root 251 2 0 0 __kthread_ 0000000000 R migration/5
  9. root 265 2 0 0 __kthread_ 0000000000 R migration/6
  10. root 356 2 0 0 __kthread_ 0000000000 R migration/7
  11. root 2165 2 0 0 __kthread_ 0000000000 R migration/9
  12.  


问题来了,既然 per_cpu 进程是和 cpu 绑定的,那么在 cpu hotplug 的时候,进程需要相应的 disable 和 enable。实现的方法可以有多种:

  • 动态的销毁和创建线程。缺点是开销比较大。
  • 设置进程的 cpu 亲和力 set_cpus_allowed_ptr()
    。缺点是进程绑定的 cpu 如果被 down 掉,进程会迁移到其他 cpu 继续执行。

为了克服上述方案的缺点,适配 per_cpu 进程的 cpu hotplug 操作,设计了 ​​kthread_park()​​/​​kthread_unpark()​​ 机制。

2.1 ​​smpboot_register_percpu_thread()​

per_cpu 进程从代码上看,实际也是调用 ​​kthread_create()​​ 来创建的。

  • kernel/smpboot.c:
  • kernel/kthread.c:

Linux 进程中 Stop, Park, Freeze【转】_代码分析_03​smpboot_register_percpu_thread

我们可以看到 smpboot_register 又增加了一层封装:​​kthread()​​ -> ​​smpboot_thread_fn()​​ -> ​​ht->thread_fn()​​,这种封装的使用可以参考 cpu_stop_threads。

  • kernel/stop_machine.c:


  1. static struct smp_hotplug_thread cpu_stop_threads = {
  2. .store = &cpu_stopper_task,
  3. .thread_should_run = cpu_stop_should_run,
  4. .thread_fn = cpu_stopper_thread,
  5. .thread_comm = "migration/%u",
  6. .create = cpu_stop_create,
  7. .setup = cpu_stop_unpark,
  8. .park = cpu_stop_park,
  9. .pre_unpark = cpu_stop_unpark,
  10. .selfparking = true,
  11. };
  12.  
  13. static int __init cpu_stop_init(void)
  14. {
  15. unsigned int cpu;
  16.  
  17. for_each_possible_cpu(cpu) {
  18. struct cpu_stopper *stopper = &per_cpu(cpu_stopper, cpu);
  19.  
  20. spin_lock_init(&stopper->lock);
  21. INIT_LIST_HEAD(&stopper->works);
  22. }
  23.  
  24. BUG_ON(smpboot_register_percpu_thread(&cpu_stop_threads));
  25. stop_machine_initialized = true;
  26. return 0;
  27. }


我们可以看到 ​​smpboot_thread_fn()​​ 循环中实现了对 park 的支持,具体实现 ​​kthread_should_park()​​、​​kthread_parkme()​​、​​kthread_park()​​、​​kthread_unpark()​​ 的代码分析:

  • kernel/kthread.c:


  1. bool kthread_should_park(void)
  2. {
  3. // (1) 判断进程所在 kthread 结构中的 KTHREAD_SHOULD_PARK 是否被置位
  4. return test_bit(KTHREAD_SHOULD_PARK, &to_kthread(current)->flags);
  5. }
  6.  
  7. void kthread_parkme(void)
  8. {
  9. __kthread_parkme(to_kthread(current));
  10. }
  11. | →
  12. static void __kthread_parkme(struct kthread *self)
  13. {
  14. // (2) 如果当前进程的 KTHREAD_SHOULD_PARK 标志被置位 ,
  15. // 将当前进程进入 TASK_PARKED 的阻塞状态。
  16. // 如果 KTHREAD_SHOULD_PARK 不清除,
  17. // 就算被 wake_up 唤醒还是会循环进入 TASK_PARKED 的阻塞状态。
  18. __set_current_state(TASK_PARKED);
  19. while (test_bit(KTHREAD_SHOULD_PARK, &self->flags)) {
  20. if (!test_and_set_bit(KTHREAD_IS_PARKED, &self->flags))
  21. complete(&self->parked);
  22. schedule();
  23. __set_current_state(TASK_PARKED);
  24. }
  25. clear_bit(KTHREAD_IS_PARKED, &self->flags);
  26. __set_current_state(TASK_RUNNING);
  27. }
  28.  
  29. int kthread_park(struct task_struct *k)
  30. {
  31. struct kthread *kthread = to_live_kthread(k);
  32. int ret = -ENOSYS;
  33.  
  34. if (kthread) {
  35. // (3) 设置 KTHREAD_IS_PARKED 标志位,并且唤醒进程进入 park 状态
  36. if (!test_bit(KTHREAD_IS_PARKED, &kthread->flags)) {
  37. set_bit(KTHREAD_SHOULD_PARK, &kthread->flags);
  38. if (k != current) {
  39. wake_up_process(k);
  40. wait_for_completion(&kthread->parked);
  41. }
  42. }
  43. ret = 0;
  44. }
  45. return ret;
  46. }
  47.  
  48. void kthread_unpark(struct task_struct *k)
  49. {
  50. struct kthread *kthread = to_live_kthread(k);
  51.  
  52. if (kthread)
  53. __kthread_unpark(k, kthread);
  54. }
  55. | →
  56. static void __kthread_unpark(struct task_struct *k, struct kthread *kthread)
  57. {
  58. // (4) 清除 KTHREAD_IS_PARKED 标志位
  59. clear_bit(KTHREAD_SHOULD_PARK, &kthread->flags);
  60. /*
  61. * We clear the IS_PARKED bit here as we don't wait
  62. * until the task has left the park code. So if we'd
  63. * park before that happens we'd see the IS_PARKED bit
  64. * which might be about to be cleared.
  65. */
  66. // 如果进程已经被 park,并且 wake_up 唤醒进程
  67. if (test_and_clear_bit(KTHREAD_IS_PARKED, &kthread->flags)) {
  68. // 如果是 per_cpu 进程,重新绑定进程 cpu
  69. if (test_bit(KTHREAD_IS_PER_CPU, &kthread->flags))
  70. __kthread_bind(k, kthread->cpu, TASK_PARKED);
  71. wake_up_state(k, TASK_PARKED);
  72. }
  73. }
  74.  


2.2 cpu hotplug 支持

我们前面说到 park 机制的主要目的是为了 per_cpu 进程支持 cpu hotplug,具体怎么响应热插拔事件呢?

  • kernel/smpboot.c:

Linux 进程中 Stop, Park, Freeze【转】_内核进程_04​park_hotplug

3. 进程 freeze

在系统进入 suspend 的时候,会尝试冻住一些进程,以避免一些进程无关操作影响系统的 suspend 状态。主要的流程如下:

  • kernel/power/suspend.c:

Linux 进程中 Stop, Park, Freeze【转】_代码分析_05​suspend_freeze_processes

这 suspend_freeze 里面判断当前在那个阶段,有 3 个重要的变量:

  • system_freezing_cnt - >0 表示系统全局的 freeze 开始;
  • pm_freezing - =true 表示用户进程 freeze 开始;
  • pm_nosig_freezing - =true 表示内核进程 freeze 开始;

具体代码分析如下:

  • kernel/power/process.c:
  • kernel/freezer.c:
  • suspend_freeze_processes()
     -> freeze_processes()
     -> try_to_freeze_tasks()
     -> freeze_task()


  1.  
  2. int freeze_processes(void)
  3. {
  4. int error;
  5. int oom_kills_saved;
  6.  
  7. error = __usermodehelper_disable(UMH_FREEZING);
  8. if (error)
  9. return error;
  10.  
  11. // (1) 置位 PF_SUSPEND_TASK,确保当前进程不会被 freeze
  12. /* Make sure this task doesn't get frozen */
  13. current->flags |= PF_SUSPEND_TASK;
  14.  
  15. // (2) 使用全局 freeze 标志 system_freezing_cnt
  16. if (!pm_freezing)
  17. atomic_inc(&system_freezing_cnt);
  18.  
  19. pm_wakeup_clear();
  20. printk("Freezing user space processes ... ");
  21. // (3) 使用用户进程 freeze 标志 pm_freezing
  22. pm_freezing = true;
  23. oom_kills_saved = oom_kills_count();
  24. // (4) freeze user_only 进程
  25. // 判断进程是否可以被 freeze,唤醒进程 freeze 自己
  26. error = try_to_freeze_tasks(true);
  27. if (!error) {
  28. __usermodehelper_set_disable_depth(UMH_DISABLED);
  29. oom_killer_disable();
  30.  
  31. /*
  32. * There might have been an OOM kill while we were
  33. * freezing tasks and the killed task might be still
  34. * on the way out so we have to double check for race.
  35. */
  36. if (oom_kills_count() != oom_kills_saved &&
  37. !check_frozen_processes()) {
  38. __usermodehelper_set_disable_depth(UMH_ENABLED);
  39. printk("OOM in progress.");
  40. error = -EBUSY;
  41. } else {
  42. printk("done.");
  43. }
  44. }
  45. printk("\n");
  46. BUG_ON(in_atomic());
  47.  
  48. if (error)
  49. thaw_processes();
  50. return error;
  51. }
  52. | →
  53. static int try_to_freeze_tasks(bool user_only)
  54. {
  55. struct task_struct *g, *p;
  56. unsigned long end_time;
  57. unsigned int todo;
  58. bool wq_busy = false;
  59. struct timeval start, end;
  60. u64 elapsed_msecs64;
  61. unsigned int elapsed_msecs;
  62. bool wakeup = false;
  63. int sleep_usecs = USEC_PER_MSEC;
  64. #ifdef CONFIG_PM_SLEEP
  65. char suspend_abort[MAX_SUSPEND_ABORT_LEN];
  66. #endif

  67. do_gettimeofday(&start);
  68.  
  69. end_time = jiffies + msecs_to_jiffies(freeze_timeout_msecs);
  70.  
  71. // (4.1) 如果是 kernel freeze,
  72. // 停工有 WQ_FREEZABLE 标志的 workqueue
  73. // 将 wq 的 pwq->max_active 设置成 0,新的 work 不能被执行
  74. if (!user_only)
  75. freeze_workqueues_begin();
  76.  
  77. while (true) {
  78. todo = 0;
  79. read_lock(&tasklist_lock);
  80. // (4.2) 对每个进程执行 freeze_task()
  81. for_each_process_thread(g, p) {
  82. if (p == current || !freeze_task(p))
  83. continue;
  84.  
  85. if (!freezer_should_skip(p))
  86. todo++;
  87. }
  88. read_unlock(&tasklist_lock);
  89.  
  90. // (4.3) 如果是 kernel freeze,
  91. // 判断停工的 workqueue 中残留的 work 有没有执行完
  92. if (!user_only) {
  93. wq_busy = freeze_workqueues_busy();
  94. todo += wq_busy;
  95. }
  96.  
  97. if (!todo || time_after(jiffies, end_time))
  98. break;
  99.  
  100. if (pm_wakeup_pending()) {
  101. #ifdef CONFIG_PM_SLEEP
  102. pm_get_active_wakeup_sources(suspend_abort,
  103. MAX_SUSPEND_ABORT_LEN);
  104. log_suspend_abort_reason(suspend_abort);
  105. #endif
  106. wakeup = true;
  107. break;
  108. }
  109.  
  110. /*
  111. * We need to retry, but first give the freezing tasks some
  112. * time to enter the refrigerator. Start with an initial
  113. * 1 ms sleep followed by exponential backoff until 8 ms.
  114. */
  115. usleep_range(sleep_usecs / 2, sleep_usecs);
  116. if (sleep_usecs < 8 * USEC_PER_MSEC)
  117. sleep_usecs *= 2;
  118. }
  119.  
  120. do_gettimeofday(&end);
  121. elapsed_msecs64 = timeval_to_ns(&end) - timeval_to_ns(&start);
  122. do_div(elapsed_msecs64, NSEC_PER_MSEC);
  123. elapsed_msecs = elapsed_msecs64;
  124.  
  125. if (wakeup) {
  126. printk("\n");
  127. printk(KERN_ERR "Freezing of tasks aborted after %d.%03d seconds",
  128. elapsed_msecs / 1000, elapsed_msecs % 1000);
  129. } else if (todo) {
  130. printk("\n");
  131. printk(KERN_ERR "Freezing of tasks failed after %d.%03d seconds"
  132. " (%d tasks refusing to freeze, wq_busy=%d):\n",
  133. elapsed_msecs / 1000, elapsed_msecs % 1000,
  134. todo - wq_busy, wq_busy);
  135.  
  136. read_lock(&tasklist_lock);
  137. for_each_process_thread(g, p) {
  138. if (p != current && !freezer_should_skip(p)
  139. && freezing(p) && !frozen(p))
  140. sched_show_task(p);
  141. }
  142. read_unlock(&tasklist_lock);
  143. } else {
  144. printk("(elapsed %d.%03d seconds) ", elapsed_msecs / 1000,
  145. elapsed_msecs % 1000);
  146. }
  147.  
  148. return todo ? -EBUSY : 0;
  149. }
  150. || →
  151. bool freeze_task(struct task_struct *p)
  152. {
  153. unsigned long flags;
  154.  
  155. /*
  156. * This check can race with freezer_do_not_count, but worst case that
  157. * will result in an extra wakeup being sent to the task. It does not
  158. * race with freezer_count(), the barriers in freezer_count() and
  159. * freezer_should_skip() ensure that either freezer_count() sees
  160. * freezing == true in try_to_freeze() and freezes, or
  161. * freezer_should_skip() sees !PF_FREEZE_SKIP and freezes the task
  162. * normally.
  163. */
  164. if (freezer_should_skip(p))
  165. return false;
  166.  
  167. spin_lock_irqsave(&freezer_lock, flags);
  168. // (4.2.1) 检查当前进程是否可以被 freeze,
  169. // 或者是否已经被 freeze
  170. if (!freezing(p) || frozen(p)) {
  171. spin_unlock_irqrestore(&freezer_lock, flags);
  172. return false;
  173. }
  174.  
  175. // (4.2.2) 如果是用户进程,伪造一个 signal 发送给进程
  176. if (!(p->flags & PF_KTHREAD))
  177. fake_signal_wake_up(p);
  178. // (4.2.3) 如果是内核进程,wake_up 内核进程
  179. else
  180. wake_up_state(p, TASK_INTERRUPTIBLE);
  181.  
  182. spin_unlock_irqrestore(&freezer_lock, flags);
  183. return true;
  184. }
  185. ||| →
  186. static inline bool freezing(struct task_struct *p)
  187. { 具体代码分析如下:
  188.  
  189. - kernel/power/process.c:
  190. - kernel/freezer.c:
  191. // 如果 system_freezing_cnt 为 0,说明全局 freeze 还没有开始
  192. if (likely(!atomic_read(&system_freezing_cnt)))
  193. return false;
  194. return freezing_slow_path(p);
  195. }
  196. |||| →
  197. bool freezing_slow_path(struct task_struct *p)
  198. {
  199. // (PF_NOFREEZE | PF_SUSPEND_TASK) 当前进程不能被 freeze
  200. if (p->flags & (PF_NOFREEZE | PF_SUSPEND_TASK))
  201. return false;
  202.  
  203. if (test_thread_flag(TIF_MEMDIE))
  204. return false;
  205.  
  206. // 如果 pm_nosig_freezing 为 true,内核进程 freeze 已经开始,
  207. // 当前进程可以被 freeze
  208. if (pm_nosig_freezing || cgroup_freezing(p))
  209. return true;
  210.  
  211. // 如果 pm_freezing 为 true,且当前进程为用户进程
  212. // 当前进程可以被 freeze
  213. if (pm_freezing && !(p->flags & PF_KTHREAD))
  214. return true;
  215.  
  216. return false;
  217. }


3.1 用户进程 freeze

freeze 用户态的进程利用了 signal 机制,系统 suspend 使能了 suspend 以后,调用 ​​fake_signal_wake_up()​​ 伪造一个信号唤醒进程,进程在 ​​ret_to_user()​​ -> ​​do_notify_resume()​​ -> ​​do_signal()​​ -> ​​get_signal()​​ -> ​​try_to_freeze()​​ 中 freeze 自己。

具体代码分析如下:

  • kernel/freezer.c:


  1. static inline bool try_to_freeze(void)
  2. {
  3. if (!(current->flags & PF_NOFREEZE))
  4. debug_check_no_locks_held();
  5. return try_to_freeze_unsafe();
  6. }
  7. | →
  8. static inline bool try_to_freeze_unsafe(void)
  9. {
  10. might_sleep();
  11. // 当前进程是否可以被 freeze
  12. if (likely(!freezing(current)))
  13. return false;
  14. // 调用 __refrigerator() freeze 当前进程
  15. return __refrigerator(false);
  16. }
  17. || →
  18. bool __refrigerator(bool check_kthr_stop)
  19. {
  20. /* Hmm, should we be allowed to suspend when there are realtime
  21. processes around? */
  22. bool was_frozen = false;
  23. long save = current->state;
  24.  
  25. pr_debug("%s entered refrigerator\n", current->comm);
  26.  
  27. for (;;) {
  28. // (1) 设置当前进程进入 TASK_UNINTERRUPTIBLE 阻塞状态
  29. set_current_state(TASK_UNINTERRUPTIBLE);
  30.  
  31. spin_lock_irq(&freezer_lock);
  32. // (2) 设置已经 freeze 标志 PF_FROZEN
  33. current->flags |= PF_FROZEN;
  34. // (3) 如果当前进程已经不是 freeze 状态,
  35. // 退出 freeze
  36. if (!freezing(current) ||
  37. (check_kthr_stop && kthread_should_stop()))
  38. current->flags &= ~PF_FROZEN;
  39. spin_unlock_irq(&freezer_lock);
  40.  
  41. if (!(current->flags & PF_FROZEN))
  42. break;
  43. was_frozen = true;
  44. schedule();
  45. }
  46.  
  47. pr_debug("%s left refrigerator\n", current->comm);
  48.  
  49. /*
  50. * Restore saved task state before returning. The mb'd version
  51. * needs to be used; otherwise, it might silently break
  52. * synchronization which depends on ordered task state change.
  53. */
  54. set_current_state(save);
  55.  
  56. return was_frozen;
  57. }


3.2 内核进程 freeze

内核进程对 freeze 的响应,有两个问题:

  • wake_up_state(p, TASK_INTERRUPTIBLE) 能唤醒哪些内核进程。
  • 内核进程怎么样来响应 freeze 状态,怎么样来 freeze 自己。

如果进程阻塞在信号量、mutex 等内核同步机制上,wake_up_state 并不能解除阻塞。因为这些机制都有 while(1) 循环来判断条件,是否成立,不成立只是简单的唤醒随即又会进入阻塞睡眠状态。

  • kernel/locking/mutex.c:
  • mutex_lock()
     -> __mutex_lock_common()


  1. __mutex_lock_common(struct mutex *lock, long state, unsigned int subclass,
  2. struct lockdep_map *nest_lock, unsigned long ip,
  3. struct ww_acquire_ctx *ww_ctx, const bool use_ww_ctx)
  4. {
  5.  
  6. for (;;) {
  7. /*
  8. * Lets try to take the lock again - this is needed even if
  9. * we get here for the first time (shortly after failing to
  10. * acquire the lock), to make sure that we get a wakeup once
  11. * it's unlocked. Later on, if we sleep, this is the
  12. * operation that gives us the lock. We xchg it to -1, so
  13. * that when we release the lock, we properly wake up the
  14. * other waiters. We only attempt the xchg if the count is
  15. * non-negative in order to avoid unnecessary xchg operations:
  16. */
  17. // (1) 如果 mutex_lock 条件成立,才退出
  18. if (atomic_read(&lock->count) >= 0 &&
  19. (atomic_xchg(&lock->count, -1) == 1))
  20. break;
  21.  
  22. // (2) 如果如果有信号阻塞,也退出
  23. /*
  24. * got a signal? (This code gets eliminated in the
  25. * TASK_UNINTERRUPTIBLE case.)
  26. */
  27. if (unlikely(signal_pending_state(state, task))) {
  28. ret = -EINTR;
  29. goto err;
  30. }
  31.  
  32. if (use_ww_ctx && ww_ctx->acquired > 0) {
  33. ret = __mutex_lock_check_stamp(lock, ww_ctx);
  34. if (ret)
  35. goto err;
  36. }
  37.  
  38. // (3) 否则继续进入阻塞休眠状态
  39. __set_task_state(task, state);
  40.  
  41. /* didn't get the lock, go to sleep: */
  42. spin_unlock_mutex(&lock->wait_lock, flags);
  43. schedule_preempt_disabled();
  44. spin_lock_mutex(&lock->wait_lock, flags);
  45. }
  46.  
  47. }


所以 ​​wake_up_state()​​ 只能唤醒这种简单阻塞的内核进程,而对于阻塞在内核同步机制上是无能无力的:


  1. void user_thread()
  2. {
  3. while(1)
  4. {
  5. set_current_state(TASK_UNINTERRUPTIBLE);
  6. schedule();
  7.  
  8. }
  9. }


内核进程响应 freeze 操作,也必须显式的调用 ​​try_to_freeze()​​ 或者 ​​kthread_freezable_should_stop()​​ 来 freeze 自己:


  1. void user_thread()
  2. {
  3. while (!kthread_should_stop()) {
  4.  
  5. try_to_freeze();
  6.  
  7. }
  8. }


所以从代码逻辑上看内核进程 freeze,并不会 freeze 所有内核进程,只 freeze 了 2 部分:一部分是设置了 WQ_FREEZABLE 标志的 workqueue,另一部分是内核进程主动调用 ​​try_to_freeze()​​ 并且在架构上设计的可以响应 freeze。



附:

static int

kthread(void *vp)

{

struct ktstate *k;

DECLARE_WAITQUEUE(wait, current);

int more;

k = vp;

current->flags |= PF_NOFREEZE;

set_user_nice(current, -10); //内核线程默认优先级

complete(&k->rendez);/* tell spawner we're running */

do {

spin_lock_irq(k->lock);

more = k->fn();

if (!more) {

add_wait_queue(k->waitq, &wait);

__set_current_state(TASK_INTERRUPTIBLE);

}

spin_unlock_irq(k->lock);

if (!more) {

schedule();

remove_wait_queue(k->waitq, &wait);

} else

cond_resched();

} while (!kthread_should_stop());

complete(&k->rendez);/* tell spawner we're stopping */

return 0;

}