歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux綜合 >> Linux內核 >> Linux內核分析之工作隊列

Linux內核分析之工作隊列

日期:2017/2/28 16:00:37   编辑:Linux內核

可延遲函數和工作隊列非常相似,但是他們的區別還是很大的。主要區別在於:可延遲函數運行在中斷上下文中,而工作隊列中的函數運行在進程上下文中。在中斷上下文中不可能發生進程切換。可延遲函數和工作隊列中的函數都不能訪問進程的用戶態地址空間。

涉及數據結構

  1. /*
  2. * The per-CPU workqueue (if single thread, we always use the first
  3. * possible cpu).
  4. */
  5. struct cpu_workqueue_struct {
  6. spinlock_t lock;/*保護該數據結構的自旋鎖*/
  7. struct list_head worklist;/*掛起鏈表的頭結點*/
  8. /*等待隊列,其中的工作者線程因等待跟多
  9. 的工作而處於睡眠狀態*/
  10. wait_queue_head_t more_work;
  11. /*等待隊列,其中的進程由於等待工作隊列
  12. 被刷新而處於睡眠狀態*/
  13. struct work_struct *current_work;
  14. struct workqueue_struct *wq;
  15. struct task_struct *thread;/*指向結構中工作者線程的進程描述符指針*/
  16. } ____cacheline_aligned;
  17. /*
  18. * The externally visible workqueue abstraction is an array of
  19. * per-CPU workqueues:
  20. */
  21. struct workqueue_struct {
  22. struct cpu_workqueue_struct *cpu_wq;
  23. struct list_head list;
  24. const char *name;
  25. int singlethread;
  26. int freezeable; /* Freeze threads during suspend */
  27. int rt;
  28. #ifdef CONFIG_LOCKDEP
  29. struct lockdep_map lockdep_map;
  30. #endif
  31. };

工作隊列操作

創建

最終都會調用如下函數執行

  1. struct workqueue_struct *__create_workqueue_key(const char *name,
  2. int singlethread,
  3. int freezeable,
  4. int rt,
  5. struct lock_class_key *key,
  6. const char *lock_name)
  7. {
  8. struct workqueue_struct *wq;
  9. struct cpu_workqueue_struct *cwq;
  10. int err = 0, cpu;
  11. /*分配wq結構*/
  12. wq = kzalloc(sizeof(*wq), GFP_KERNEL);
  13. if (!wq)
  14. return NULL;
  15. /*分配cwq結構*/
  16. wq->cpu_wq = alloc_percpu(struct cpu_workqueue_struct);
  17. if (!wq->cpu_wq) {
  18. kfree(wq);
  19. return NULL;
  20. }
  21. wq->name = name;
  22. lockdep_init_map(&wq->lockdep_map, lock_name, key, 0);
  23. wq->singlethread = singlethread;
  24. wq->freezeable = freezeable;
  25. wq->rt = rt;
  26. INIT_LIST_HEAD(&wq->list);
  27. if (singlethread) {/*如果設置了單線程,只創建一個*/
  28. /*初始化cwq*/
  29. cwq = init_cpu_workqueue(wq, singlethread_cpu);
  30. /*創建內核線程*/
  31. err = create_workqueue_thread(cwq, singlethread_cpu);
  32. /*喚醒剛創建的內核線程*/
  33. start_workqueue_thread(cwq, -1);
  34. } else {/*反之,每個cpu創建一個線程*/
  35. cpu_maps_update_begin();
  36. /*
  37. * We must place this wq on list even if the code below fails.
  38. * cpu_down(cpu) can remove cpu from cpu_populated_map before
  39. * destroy_workqueue() takes the lock, in that case we leak
  40. * cwq[cpu]->thread.
  41. */
  42. spin_lock(&workqueue_lock);
  43. list_add(&wq->list, &workqueues);
  44. spin_unlock(&workqueue_lock);
  45. /*
  46. * We must initialize cwqs for each possible cpu even if we
  47. * are going to call destroy_workqueue() finally. Otherwise
  48. * cpu_up() can hit the uninitialized cwq once we drop the
  49. * lock.
  50. */
  51. for_each_possible_cpu(cpu) {/*對每個cpu*/
  52. cwq = init_cpu_workqueue(wq, cpu);
  53. if (err || !cpu_online(cpu))
  54. continue;
  55. err = create_workqueue_thread(cwq, cpu);
  56. start_workqueue_thread(cwq, cpu);
  57. }
  58. cpu_maps_update_done();
  59. }
  60. if (err) {
  61. destroy_workqueue(wq);
  62. wq = NULL;
  63. }
  64. return wq;
  65. }

可見,工作隊列在創建時就喚醒創建的內核線程,下面我們看看他創建的內核線程

  1. static int worker_thread(void *__cwq)
  2. {
  3. struct cpu_workqueue_struct *cwq = __cwq;
  4. DEFINE_WAIT(wait);
  5. if (cwq->wq->freezeable)
  6. set_freezable();
  7. for (;;) {
  8. prepare_to_wait(&cwq->more_work, &wait, TASK_INTERRUPTIBLE);
  9. if (!freezing(current) &&
  10. !kthread_should_stop() &&
  11. list_empty(&cwq->worklist))
  12. schedule();
  13. finish_wait(&cwq->more_work, &wait);
  14. try_to_freeze();
  15. if (kthread_should_stop())
  16. break;
  17. /*執行工作隊列*/
  18. run_workqueue(cwq);
  19. }
  20. return 0;
  21. }
Copyright © Linux教程網 All Rights Reserved