kthread_work和kthread_worker机制
生活随笔
收集整理的這篇文章主要介紹了
kthread_work和kthread_worker机制
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
Kernel中提供的kthread_work和kthread_worker機制和經典的work_struct和workqueue_struct的關系有點類似.通過一個kthread_worker可以處理多個kthread_work,其實就是利用了工人在流水線上工作的形式.先來看看這兩個數據結構的定義,它們定義在頭文件include/linux/kthread.h(3.1版本)
struct kthread_worker {
?? ?spinlock_t?? ??? ?lock;//保護work_list鏈表的自旋鎖
?? ?struct list_head?? ?work_list;//kthread_work鏈表,相當于流水線
?? ?struct task_struct?? ?*task;//為該kthread_worker執行任務的線程對應的task_struct結構
?? ?struct kthread_work?? ?*current_work;//當前正在處理的kthread_work
};
struct kthread_work {
?? ?struct list_head?? ?node;//kthread_work鏈表的鏈表元素
?? ?kthread_work_func_t?? ?func;//執行函數,該kthread_work所要做的事情
?? ?wait_queue_head_t?? ?done;//沒有找到相關用法
?? ?struct kthread_worker?? ?*worker;//處理該kthread_work的kthread_worker
};
?? ?kthread_worker處理kthread_work是通過創建一個內核線程來執行kthread_worker_fn函數來實現的.下面給出一段例子代碼:
========================================================
struct kthread_worker worker;//聲明一個kthread_worker
init_kthread_worker(&worker);//初始化kthread_worker
struct task_struct *kworker_task = kthread_run(kthread_worker_fn, &worker, "nvme%d", dev->instance);//為kthread_worker創建一個內核線程來處理work.
struct kthread_work work;//聲明一個kthread_work
init_kthread_work(&work, work_fn);初始化kthread_work,設置work執行函數.
queue_kthread_work(&worker, &work);//將kthread_work添加到kthread_worker的work_list.
========================================================
?? ?下面來依次介紹一下這些函數的實現.
?? ?
?? ?init_kthread_worker和init_kthread_work其實是兩個宏.
#define init_kthread_worker(worker)?? ??? ??? ??? ??? ?\
?? ?do {?? ??? ??? ??? ??? ??? ??? ??? ?\
?? ??? ?static struct lock_class_key __key;?? ??? ??? ?\
?? ??? ?__init_kthread_worker((worker), "("#worker")->lock", &__key); \
?? ?} while (0)
?? ?該宏會調用函數__init_kthread_worker,該函數基本上就是初始化work_list鏈表
void __init_kthread_worker(struct kthread_worker *worker,
?? ??? ??? ??? ?const char *name,
?? ??? ??? ??? ?struct lock_class_key *key)
{
?? ?spin_lock_init(&worker->lock);
?? ?lockdep_set_class_and_name(&worker->lock, key, name);
?? ?INIT_LIST_HEAD(&worker->work_list);
?? ?worker->task = NULL;
}
?? ?宏init_kthread_work用來初始化kthread_work,主要任務是初始化鏈表元素,設置執行函數.
#define init_kthread_work(work, fn)?? ??? ??? ??? ??? ?\
?? ?do {?? ??? ??? ??? ??? ??? ??? ??? ?\
?? ??? ?memset((work), 0, sizeof(struct kthread_work));?? ??? ?\
?? ??? ?INIT_LIST_HEAD(&(work)->node);?? ??? ??? ??? ?\
?? ??? ?(work)->func = (fn);?? ??? ??? ??? ??? ?\
?? ??? ?init_waitqueue_head(&(work)->done);?? ??? ??? ?\
?? ?} while (0)
?? ?下面看下函數queue_kthread_work的實現,該函數獲取了保護work_list的自旋鎖之后,將剩下的任務委托給函數insert_kthread_work去執行.
bool queue_kthread_work(struct kthread_worker *worker,
?? ??? ??? ?struct kthread_work *work)
{
?? ?bool ret = false;
?? ?unsigned long flags;
?? ?spin_lock_irqsave(&worker->lock, flags);
?? ?if (list_empty(&work->node)) {
?? ??? ?insert_kthread_work(worker, work, &worker->work_list);
?? ??? ?ret = true;
?? ?}
?? ?spin_unlock_irqrestore(&worker->lock, flags);
?? ?return ret;
}
static void insert_kthread_work(struct kthread_worker *worker,
?? ??? ??? ??????? struct kthread_work *work,
?? ??? ??? ??????? struct list_head *pos)
{
?? ?lockdep_assert_held(&worker->lock);
?? ?list_add_tail(&work->node, pos);//pos = &worker->work_list,這里就是將kthread_work添加到kthread_worker的work_list鏈表末尾.
?? ?work->worker = worker;//設置處理該work的worker
?? ?
?? ?//如果此時指定了worker->task,將會喚醒worker->task表示的內核線程.在初始化kthread_worker的時候worker->task = NULL,但是在上面創建內核線程時所指定的線程的執行函數kthread_worker_fn中會將worker->task賦值,詳情見下文kthread_worker_fn的介紹.
?? ?if (likely(worker->task))
?? ??? ?wake_up_process(worker->task);
}
?? ?kthread_worker_fn無疑是最重要的函數,在該函數中將實現worker對work的處理.
?? ?
int kthread_worker_fn(void *worker_ptr)
{
?? ?struct kthread_worker *worker = worker_ptr;
?? ?struct kthread_work *work;
?? ?WARN_ON(worker->task);
?? ?worker->task = current;//將worker->task設置為當前線程
repeat:
?? ?set_current_state(TASK_INTERRUPTIBLE);?? ?/* mb paired w/ kthread_stop */
?? ?if (kthread_should_stop()) {//如果該work處理線程在別的地方被停止了,則該線程會退出.
?? ??? ?__set_current_state(TASK_RUNNING);
?? ??? ?spin_lock_irq(&worker->lock);
?? ??? ?worker->task = NULL;
?? ??? ?spin_unlock_irq(&worker->lock);
?? ??? ?return 0;
?? ?}
?? ?work = NULL;
?? ?spin_lock_irq(&worker->lock);
?? ?if (!list_empty(&worker->work_list)) {
?? ??? ?//如果worker的work_list上有work,則獲取其上的第一個work,然后將其從鏈表中刪除.
?? ??? ?work = list_first_entry(&worker->work_list, struct kthread_work, node);
?? ??? ?list_del_init(&work->node);
?? ?}
?? ?worker->current_work = work;//將剛才獲取的work設置為worker當前處理的work
?? ?spin_unlock_irq(&worker->lock);
?? ?if (work) {//如果剛才獲取到了work,則修改當前線程的狀態為TASK_RUNNING,這樣在發生調度的時候,該線程就不會被從就緒隊列中移除.然后執行該work的執行函數.
?? ??? ?__set_current_state(TASK_RUNNING);
?? ??? ?work->func(work);
?? ?} else if (!freezing(current))
?? ??? ?schedule();//如果沒有獲取到work,則說明該worker當前無事可做,則調用schedule交出cpu,該線程會被從就緒隊列中移除,而進入睡眠狀態,直到有work被添加到worker的work_list時,被喚醒.
?? ?try_to_freeze();
?? ?goto repeat;//繼續處理work_list中的work或者剛被喚醒,去檢查work_list上是否有work可處理.
}
?? ?還有一個比較重要的函數是flush_kthread_worker,該函數會等待當前work_list上的所有的work都被處理了,才會返回.可用來在最后要停止worker的處理線程之前,保證所有的work都已經處理了.
void flush_kthread_worker(struct kthread_worker *worker)
{
?? ?struct kthread_flush_work fwork = {
?? ??? ?KTHREAD_WORK_INIT(fwork.work, kthread_flush_work_fn),
?? ??? ?COMPLETION_INITIALIZER_ONSTACK(fwork.done),
?? ?};
?? ?queue_kthread_work(worker, &fwork.work);
?? ?wait_for_completion(&fwork.done);
}
?? ?該函數會首先創建并初始化一個kthread_flush_work,該結構體包含了一個kthread_work和一個completion,然后將kthread_work添加到worker的work_list上,最后調用wait_for_completion等待該work被處理,這個最后一個提交的work被稱作flash_work,的執行函數為kthread_flush_work_fn,如下所示,該函數僅僅是調用complete喚醒調用flush_kthread_worker的線程而已,該線程被喚醒之后,它就知道work_list上所有的work都已經處理完了,flush函數也就返回了.
static void kthread_flush_work_fn(struct kthread_work *work)
{
?? ?struct kthread_flush_work *fwork =
?? ??? ?container_of(work, struct kthread_flush_work, work);
?? ?complete(&fwork->done);
}
struct kthread_worker {
?? ?spinlock_t?? ??? ?lock;//保護work_list鏈表的自旋鎖
?? ?struct list_head?? ?work_list;//kthread_work鏈表,相當于流水線
?? ?struct task_struct?? ?*task;//為該kthread_worker執行任務的線程對應的task_struct結構
?? ?struct kthread_work?? ?*current_work;//當前正在處理的kthread_work
};
struct kthread_work {
?? ?struct list_head?? ?node;//kthread_work鏈表的鏈表元素
?? ?kthread_work_func_t?? ?func;//執行函數,該kthread_work所要做的事情
?? ?wait_queue_head_t?? ?done;//沒有找到相關用法
?? ?struct kthread_worker?? ?*worker;//處理該kthread_work的kthread_worker
};
?? ?kthread_worker處理kthread_work是通過創建一個內核線程來執行kthread_worker_fn函數來實現的.下面給出一段例子代碼:
========================================================
struct kthread_worker worker;//聲明一個kthread_worker
init_kthread_worker(&worker);//初始化kthread_worker
struct task_struct *kworker_task = kthread_run(kthread_worker_fn, &worker, "nvme%d", dev->instance);//為kthread_worker創建一個內核線程來處理work.
struct kthread_work work;//聲明一個kthread_work
init_kthread_work(&work, work_fn);初始化kthread_work,設置work執行函數.
queue_kthread_work(&worker, &work);//將kthread_work添加到kthread_worker的work_list.
========================================================
?? ?下面來依次介紹一下這些函數的實現.
?? ?
?? ?init_kthread_worker和init_kthread_work其實是兩個宏.
#define init_kthread_worker(worker)?? ??? ??? ??? ??? ?\
?? ?do {?? ??? ??? ??? ??? ??? ??? ??? ?\
?? ??? ?static struct lock_class_key __key;?? ??? ??? ?\
?? ??? ?__init_kthread_worker((worker), "("#worker")->lock", &__key); \
?? ?} while (0)
?? ?該宏會調用函數__init_kthread_worker,該函數基本上就是初始化work_list鏈表
void __init_kthread_worker(struct kthread_worker *worker,
?? ??? ??? ??? ?const char *name,
?? ??? ??? ??? ?struct lock_class_key *key)
{
?? ?spin_lock_init(&worker->lock);
?? ?lockdep_set_class_and_name(&worker->lock, key, name);
?? ?INIT_LIST_HEAD(&worker->work_list);
?? ?worker->task = NULL;
}
?? ?宏init_kthread_work用來初始化kthread_work,主要任務是初始化鏈表元素,設置執行函數.
#define init_kthread_work(work, fn)?? ??? ??? ??? ??? ?\
?? ?do {?? ??? ??? ??? ??? ??? ??? ??? ?\
?? ??? ?memset((work), 0, sizeof(struct kthread_work));?? ??? ?\
?? ??? ?INIT_LIST_HEAD(&(work)->node);?? ??? ??? ??? ?\
?? ??? ?(work)->func = (fn);?? ??? ??? ??? ??? ?\
?? ??? ?init_waitqueue_head(&(work)->done);?? ??? ??? ?\
?? ?} while (0)
?? ?下面看下函數queue_kthread_work的實現,該函數獲取了保護work_list的自旋鎖之后,將剩下的任務委托給函數insert_kthread_work去執行.
bool queue_kthread_work(struct kthread_worker *worker,
?? ??? ??? ?struct kthread_work *work)
{
?? ?bool ret = false;
?? ?unsigned long flags;
?? ?spin_lock_irqsave(&worker->lock, flags);
?? ?if (list_empty(&work->node)) {
?? ??? ?insert_kthread_work(worker, work, &worker->work_list);
?? ??? ?ret = true;
?? ?}
?? ?spin_unlock_irqrestore(&worker->lock, flags);
?? ?return ret;
}
static void insert_kthread_work(struct kthread_worker *worker,
?? ??? ??? ??????? struct kthread_work *work,
?? ??? ??? ??????? struct list_head *pos)
{
?? ?lockdep_assert_held(&worker->lock);
?? ?list_add_tail(&work->node, pos);//pos = &worker->work_list,這里就是將kthread_work添加到kthread_worker的work_list鏈表末尾.
?? ?work->worker = worker;//設置處理該work的worker
?? ?
?? ?//如果此時指定了worker->task,將會喚醒worker->task表示的內核線程.在初始化kthread_worker的時候worker->task = NULL,但是在上面創建內核線程時所指定的線程的執行函數kthread_worker_fn中會將worker->task賦值,詳情見下文kthread_worker_fn的介紹.
?? ?if (likely(worker->task))
?? ??? ?wake_up_process(worker->task);
}
?? ?kthread_worker_fn無疑是最重要的函數,在該函數中將實現worker對work的處理.
?? ?
int kthread_worker_fn(void *worker_ptr)
{
?? ?struct kthread_worker *worker = worker_ptr;
?? ?struct kthread_work *work;
?? ?WARN_ON(worker->task);
?? ?worker->task = current;//將worker->task設置為當前線程
repeat:
?? ?set_current_state(TASK_INTERRUPTIBLE);?? ?/* mb paired w/ kthread_stop */
?? ?if (kthread_should_stop()) {//如果該work處理線程在別的地方被停止了,則該線程會退出.
?? ??? ?__set_current_state(TASK_RUNNING);
?? ??? ?spin_lock_irq(&worker->lock);
?? ??? ?worker->task = NULL;
?? ??? ?spin_unlock_irq(&worker->lock);
?? ??? ?return 0;
?? ?}
?? ?work = NULL;
?? ?spin_lock_irq(&worker->lock);
?? ?if (!list_empty(&worker->work_list)) {
?? ??? ?//如果worker的work_list上有work,則獲取其上的第一個work,然后將其從鏈表中刪除.
?? ??? ?work = list_first_entry(&worker->work_list, struct kthread_work, node);
?? ??? ?list_del_init(&work->node);
?? ?}
?? ?worker->current_work = work;//將剛才獲取的work設置為worker當前處理的work
?? ?spin_unlock_irq(&worker->lock);
?? ?if (work) {//如果剛才獲取到了work,則修改當前線程的狀態為TASK_RUNNING,這樣在發生調度的時候,該線程就不會被從就緒隊列中移除.然后執行該work的執行函數.
?? ??? ?__set_current_state(TASK_RUNNING);
?? ??? ?work->func(work);
?? ?} else if (!freezing(current))
?? ??? ?schedule();//如果沒有獲取到work,則說明該worker當前無事可做,則調用schedule交出cpu,該線程會被從就緒隊列中移除,而進入睡眠狀態,直到有work被添加到worker的work_list時,被喚醒.
?? ?try_to_freeze();
?? ?goto repeat;//繼續處理work_list中的work或者剛被喚醒,去檢查work_list上是否有work可處理.
}
?? ?還有一個比較重要的函數是flush_kthread_worker,該函數會等待當前work_list上的所有的work都被處理了,才會返回.可用來在最后要停止worker的處理線程之前,保證所有的work都已經處理了.
void flush_kthread_worker(struct kthread_worker *worker)
{
?? ?struct kthread_flush_work fwork = {
?? ??? ?KTHREAD_WORK_INIT(fwork.work, kthread_flush_work_fn),
?? ??? ?COMPLETION_INITIALIZER_ONSTACK(fwork.done),
?? ?};
?? ?queue_kthread_work(worker, &fwork.work);
?? ?wait_for_completion(&fwork.done);
}
?? ?該函數會首先創建并初始化一個kthread_flush_work,該結構體包含了一個kthread_work和一個completion,然后將kthread_work添加到worker的work_list上,最后調用wait_for_completion等待該work被處理,這個最后一個提交的work被稱作flash_work,的執行函數為kthread_flush_work_fn,如下所示,該函數僅僅是調用complete喚醒調用flush_kthread_worker的線程而已,該線程被喚醒之后,它就知道work_list上所有的work都已經處理完了,flush函數也就返回了.
static void kthread_flush_work_fn(struct kthread_work *work)
{
?? ?struct kthread_flush_work *fwork =
?? ??? ?container_of(work, struct kthread_flush_work, work);
?? ?complete(&fwork->done);
}
總結
以上是生活随笔為你收集整理的kthread_work和kthread_worker机制的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: kthread_run创建内核线程的原理
- 下一篇: Linux workqueue工作原理