spi driver: kthread_worker 和 kthread_work

kthread_worker 和 kthread_work

轉載自:

https://blog.csdn.net/qqliyunpeng/article/details/53931350

作者: 李雲鵬([email protected])

1. 簡介:

        在spi驅動中用到了內核的線程,用的函數就是跟 kthread_worker 和 kthread_work 相關的函數,對於這兩個名詞的翻譯,在網上暫時沒有找到合適的,先翻譯成線程內核線程相關的:工人和工作,這麼直白的翻譯是根據其工作原理相關的,本來想翻譯成別的,一想到他的實現方式,直白的翻譯,更能讓人理解。

        此部分介紹的函數主要在 include/linux/kthread.h 文件,這裏可以推測,也許是內核爲了方便我們使用內核的線程,而設計的kthread_work和kthread_worker。


2. 函數:


2.1 先來看這兩個結構體:


kthread_worke

kthread_worker

  1. struct kthread_worker {  
  2.     spinlock_t      lock;  
  3.     struct list_head    work_list;  
  4.     struct task_struct  *task;  
  5.     struct kthread_work *current_work;  
  6. };  
  7.   
  8. struct kthread_work {  
  9.     struct list_head    node;  
  10.     kthread_work_func_t func;  
  11.     wait_queue_head_t   done; //  等待隊列,內部成員是一個鎖和一個鏈表節點  
  12.     struct kthread_worker   *worker;  
  13. };  

【1】其中的 wait_queue_head_t 結構體需要解析一下:

  1. struct __wait_queue_head { // 是一個帶鎖的鏈表節點  
  2.     spinlock_t lock;  
  3.     struct list_head task_list;  
  4. };  
  5. typedef struct __wait_queue_head wait_queue_head_t;  

2.2 聲明:

DEFINE_KTHREAD_WORK宏

DEFINE_KTHREAD_WORKER宏

  1. #define KTHREAD_WORKER_INIT(worker) {               \  
  2.     .lock = __SPIN_LOCK_UNLOCKED((worker).lock),            \ // 初始化worker中lock  
  3.     .work_list = LIST_HEAD_INIT((worker).work_list),        \ // 初始化worker中的鏈表節點 work_list  
  4.     }  
  5.   
  6. #define KTHREAD_WORK_INIT(work, fn) {               \  
  7.     .node = LIST_HEAD_INIT((work).node),                \ // 初始化work中的鏈表節點 node (next和pre指針指向自己的首地址)  
  8.     .func = (fn),                           \ // func成員賦值  
  9.     .done = __WAIT_QUEUE_HEAD_INITIALIZER((work).done),     \ // 初始化 done 成員 (初始化等待隊列中的鎖和鏈表節點,  
  10.     }                                                                 // 鏈表節點的初始化就是next和pre指針指向節點的首地址)  
  11.   
  12. #define __WAIT_QUEUE_HEAD_INITIALIZER(name) {               \  
  13.     .lock       = __SPIN_LOCK_UNLOCKED(name.lock),      \  
  14.     .task_list  = { &(name).task_list, &(name).task_list } }  
  15.   
  16. #define DEFINE_KTHREAD_WORKER(worker)                   \  
  17.     struct kthread_worker worker = KTHREAD_WORKER_INIT(worker)  
  18.   
  19. #define DEFINE_KTHREAD_WORK(work, fn)                   \  
  20.     struct kthread_work work = KTHREAD_WORK_INIT(work, fn)  

2.3 初始化


init_kthread_work宏

init_kthread_worker宏

  1. #define init_kthread_worker(worker)                 \ // 初始化 kthread_worker  
  2.     do {                                \  
  3.         static struct lock_class_key __key;         \  
  4.         __init_kthread_worker((worker), "("#worker")->lock", &__key); \  
  5.     } while (0)  
  6.   
  7. #define init_kthread_work(work, fn)                 \ // 初始化 kthread_work  
  8.     do {                                \  
  9.         memset((work), 0, sizeof(struct kthread_work));     \  
  10.         INIT_LIST_HEAD(&(work)->node);               \ // 初始化成員 node 鏈表節點  
  11.         (work)->func = (fn);                 \ // 將回調函數的指針指向fn函數,內核線程將會一直執行的函數  
  12.         init_waitqueue_head(&(work)->done);          \ // 初始化成員 done (等待隊列)  
  13.     } while (0)  
  14.   
  15. void __init_kthread_worker(struct kthread_worker *worker,  
  16.                 const char *name,  
  17.                 struct lock_class_key *key)  
  18. {  
  19.     spin_lock_init(&worker->lock);  
  20.     lockdep_set_class_and_name(&worker->lock, key, name); // 跟防止死鎖有關,此處不深究  
  21.     INIT_LIST_HEAD(&worker->work_list); // 初始化 work_list 鏈表節點  
  22.     worker->task = NULL;  
  23. }  

2.4 內核線程一直執行的函數


kthread_worker_fn函數

  1. /** 
  2.  * kthread_worker_fn - kthread 函數目的是執行 kthread_worker中work_list下的work,此函數是作爲內核線程中一直執行的函數 
  3.  * @worker_ptr: 指向初始化了的 kthread_worker 
  4.  */  
  5. int kthread_worker_fn(void *worker_ptr)  
  6. {  
  7.     struct kthread_worker *worker = worker_ptr;  
  8.     struct kthread_work *work;  
  9.   
  10.     WARN_ON(worker->task);  
  11.     worker->task = current;  
  12. repeat:  
  13.     set_current_state(TASK_INTERRUPTIBLE);  /* mb paired w/ kthread_stop */  
  14.   
  15.     if (kthread_should_stop()) { // 如果接收到線程停止的信號  
  16.         __set_current_state(TASK_RUNNING);  
  17.         spin_lock_irq(&worker->lock);  
  18.         worker->task = NULL;  
  19.         spin_unlock_irq(&worker->lock);  
  20.         return 0;  
  21.     }  
  22.   
  23.     work = NULL;  
  24.     spin_lock_irq(&worker->lock);  
  25.     if (!list_empty(&worker->work_list)) { // 如果 worker中的work_list鏈表不是空的  
  26.         work = list_first_entry(&worker->work_list, // 取出頭結點後邊的第一個結構體kthread_work  
  27.                     struct kthread_work, node);  
  28.         list_del_init(&work->node); // 刪除鏈表中的入口  
  29.     }  
  30.     worker->current_work = work; // 將正在處理的work地址賦給 worker中的current_work成員  
  31.     spin_unlock_irq(&worker->lock);  
  32.   
  33.     if (work) { // 如果有 work  
  34.         __set_current_state(TASK_RUNNING); // 啓動內核線程  
  35.         work->func(work); // 運行work中的func函數  
  36.     } else if (!freezing(current)) // 如果沒有work要做,並且沒有freeze,則主動請求調度,主動放棄cpu時間片  
  37.         schedule();  
  38.   
  39.     try_to_freeze();  
  40.     goto repeat; // 無限循環  
  41. }  
【1】可以看到,這個函數的關鍵就是重複的執行kthread_worker結構體中的work_list鏈表鎖掛接的kthread_work中的func函數,直到work_list變爲空爲止。

【2】要知道的是kthread是內核線程,是一直運行在內核態的線程

【3】這個函數一般是作爲回調函數使用,比如spi.c中的如下程序

  1. master->kworker_task = kthread_run(kthread_worker_fn,  
  2.                        &master->kworker,  
  3.                        dev_name(&master->dev));  
  4.   
  5. /** 
  6.  * kthread_run - 創建並喚醒一個內核線程 
  7.  * @threadfn: the function to run until signal_pending(current). 
  8.  * @data: data ptr for @threadfn. 
  9.  * @namefmt: printf-style name for the thread. 
  10.  * 
  11.  * Description: Convenient wrapper for kthread_create() followed by 
  12.  * wake_up_process().  Returns the kthread or ERR_PTR(-ENOMEM). 
  13.  */  
  14. #define kthread_run(threadfn, data, namefmt, ...) ...(此處省略)  


2.5 隊列化kthread_work


queue_kthread_work 函數

  1. /** 
  2.  * queue_kthread_work - 隊列化一個 kthread_work,實質是將work中的node節點掛接到worker中的work_list後邊,並嘗試喚醒worker中的任務 
  3.  * @worker: 目標 kthread_worker 
  4.  * @work: 要隊列化的 kthread_work 
  5.  * 
  6.  * 隊列化 @work 目的是爲了讓任務異步執行.  @task 
  7.  * 必須已經被 kthread_worker_create() 創建了.   
  8.  * 隊列化成功,返回true,不成功返回false 
  9.  */  
  10. bool queue_kthread_work(struct kthread_worker *worker,  
  11.             struct kthread_work *work)  
  12. {  
  13.     bool ret = false;  
  14.     unsigned long flags;  
  15.   
  16.     spin_lock_irqsave(&worker->lock, flags);  
  17.     if (list_empty(&work->node)) {   // 這裏保證要插入到worker中鏈表節點的work的node節點一定要是一個獨立的,不能是一串  
  18.         insert_kthread_work(worker, work, &worker->work_list);  
  19.         ret = true;  
  20.     }  
  21.     spin_unlock_irqrestore(&worker->lock, flags);  
  22.     return ret;  
  23. }  
  24.   
  25. /* 在@worker中的work_list鏈表中的@pos位置的後邊插入@work中的鏈表節點 */  
  26. static void insert_kthread_work(struct kthread_worker *worker,  
  27.                    struct kthread_work *work,  
  28.                    struct list_head *pos)  
  29. {  
  30.     lockdep_assert_held(&worker->lock);  
  31.   
  32.     list_add_tail(&work->node, pos);  
  33.     work->worker = worker; // work中的worker指針指向worker  
  34.     if (likely(worker->task))  
  35.         wake_up_process(worker->task); // 嘗試喚醒一下 worker 中的task指向的線程來處理work  
  36. }  

2.6 執行完worker中的work


flush_kthread_worker 函數

  1. struct kthread_flush_work {  
  2.     struct kthread_work work;  
  3.     struct completion   done;  
  4. };  
  5.   
  6. static void kthread_flush_work_fn(struct kthread_work *work)  
  7. {  
  8.     struct kthread_flush_work *fwork =  
  9.         container_of(work, struct kthread_flush_work, work);  
  10.     complete(&fwork->done); // 喚醒完成量  
  11. }  
  12.   
  13. /** 
  14.  * flush_kthread_worker - flush all current works on a kthread_worker 
  15.  * @worker: worker to flush 
  16.  * 
  17.  * Wait until all currently executing or pending works on @worker are 
  18.  * finished. 
  19.  */  
  20. void flush_kthread_worker(struct kthread_worker *worker)  
  21. {  
  22.     struct kthread_flush_work fwork = {  
  23.         KTHREAD_WORK_INIT(fwork.work, kthread_flush_work_fn),  
  24.         COMPLETION_INITIALIZER_ONSTACK(fwork.done), // ON_STACK後綴相當於加了static  
  25.     };  
  26.   
  27.     queue_kthread_work(worker, &fwork.work); // 將 fwork中的work成員的node節點過接到worker_list下,並嘗試喚醒線程進行kthread_flush_work_fn函數的執行  
  28.     wait_for_completion(&fwork.done); // 調用這個函數的線程睡眠等待在這裏,等待執行worker中work_list下的fulsh_kthread_work完kthread_flush_work_fn函數  
  29. }  
【1】如何就flush了呢?看2.7的總結


2.7 總結的一張圖:


        說了半天,其實woker和work的關係還是很難理解的,當我們經過一段時間,再次看的時候,難免還要花很長時間,因此,我畫了一張圖:

【1】worker中的task執行的是各自work中的func指定的函數,此規則同樣適用於kthread_flush_work

【2】kthread_flush_work中的函數是kthread.c文件指定的函數,而kthread_work中的函數是用戶自己定義的函數

【3】每次喚醒線程執行的work都是worker中的work_list下掛載的正常順序的第一個

【4】如何實現等待全部的work都執行完呢?調用的是flush_kthread_worker函數中的wait_for_completion(&fwork.done);語句,只有當前邊的work都執行完,才能輪到kthread_flush_work中的kthread_flush_work_fn的執行,此函數就是喚醒kthread_flush_work中的done。從而確定了前邊的work都執行完了

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章