一、用法 struct cpu_workqueue_struct { spinlock_t lock; long remove_sequence; /* Least-recently added (next to run) */ long insert_sequence; /* Next to add */ struct list_head worklist; wait_queue_head_t more_work; wait_queue_head_t work_done; struct workqueue_struct *wq; struct task_struct *thread; int run_depth; /* Detect run_workqueue() recursion depth */ } ____cacheline_aligned; /* * The externally visible workqueue abstraction is an array of * per-CPU workqueues: */ struct workqueue_struct { struct cpu_workqueue_struct *cpu_wq; const char *name; struct list_head list; /* Empty if single thread */ }; 工作隊列的使用很簡單。 1.首先是建立一個工作隊列: struct workqueue_struct *keventd_wq; keventd_wq = create_workqueue("events"); 2.然後就是在這個隊列中insert你所要做的“工作”: DECLARE_WORK(work, func, data) queue_work(keventd_wq, work); struct work_struct { unsigned long pending; struct list_head entry; void (*func)(void *); void *data; void *wq_data; struct timer_list timer; }; 初始化有兩種方法。 一種為靜態方法: #define __WORK_INITIALIZER(n, f, d) { \ .entry = { &(n).entry, &(n).entry }, \ .func = (f), \ .data = (d), \ .timer = TIMER_INITIALIZER(NULL, 0, 0), \ } #define DECLARE_WORK(n, f, d) \ struct work_struct n = __WORK_INITIALIZER(n, f, d) 另一種為動態方法: /* * initialize all of a work-struct: */ #define INIT_WORK(_work, _func, _data) \ do { \ INIT_LIST_HEAD(&(_work)->entry); \ (_work)->pending = 0; \ PREPARE_WORK((_work), (_func), (_data)); \ init_timer(&(_work)->timer); \ } while (0) 二、執行過程 create_workqueue() -> __create_workqueue() struct workqueue_struct *__create_workqueue(const char *name, int singlethread) { int cpu, destroy = 0; struct workqueue_struct *wq; struct task_struct *p; wq = kzalloc(sizeof(*wq), GFP_KERNEL); //為每個CPU建立一個結構 wq->cpu_wq = alloc_percpu(struct cpu_workqueue_struct); ... wq->name = name; mutex_lock(&workqueue_mutex); if (singlethread) { ... } else { list_add(&wq->list, &workqueues); for_each_online_cpu(cpu) { //為每個CPU建立一個線程 p = create_workqueue_thread(wq, cpu); if (p) { kthread_bind(p, cpu); //喚醒這個線程執行工作 wake_up_process(p); } else destroy = 1; } } mutex_unlock(&workqueue_mutex); ... return wq; } create_workqueue() -> __create_workqueue() -> create_workqueue_thread() static struct task_struct *create_workqueue_thread(struct workqueue_struct *wq, int cpu) { struct cpu_workqueue_struct *cwq = per_cpu_ptr(wq->cpu_wq, cpu); struct task_struct *p; spin_lock_init(&cwq->lock); cwq->wq = wq; cwq->thread = NULL; cwq->insert_sequence = 0; cwq->remove_sequence = 0; INIT_LIST_HEAD(&cwq->worklist); init_waitqueue_head(&cwq->more_work); init_waitqueue_head(&cwq->work_done); if (is_single_threaded(wq)) p = kthread_create(worker_thread, cwq, "%s", wq->name); else //建立一個線程,這個線程以cwq為資料執行worker_thread這個函數 p = kthread_create(worker_thread, cwq, "%s/%d", wq->name, cpu); if (IS_ERR(p)) return NULL; cwq->thread = p; // return p; } create_workqueue() -> __create_workqueue() -> create_workqueue_thread() -> worker_thread() //本函數在一個死迴圈等待工作的到來,這一般在睡眠狀態中,等待被喚醒執行工作 //當有工作到來時queue_work()會將這個線程喚醒 static int worker_thread(void *__cwq) { struct cpu_workqueue_struct *cwq = __cwq; DECLARE_WAITQUEUE(wait, current); ... current->flags |= PF_NOFREEZE; //設定優先權 set_user_nice(current, -5); ... set_current_state(TASK_INTERRUPTIBLE); while (!kthread_should_stop()) { //將本線程加入睡眠隊列,用於睡眠後可以被喚醒 add_wait_queue(&cwq->more_work, &wait); //如果沒用被執行的“工作”,則將自己切換出去,進入睡眠狀態 if (list_empty(&cwq->worklist)) schedule(); else //否則或是被喚醒 __set_current_state(TASK_RUNNING); remove_wait_queue(&cwq->more_work, &wait); //工作隊列非空,執行工作 if (!list_empty(&cwq->worklist)) run_workqueue(cwq); set_current_state(TASK_INTERRUPTIBLE); } __set_current_state(TASK_RUNNING); return 0; } create_workqueue() -> __create_workqueue() -> create_workqueue_thread() -> worker_thread() -> run_workqueue() //該函數執行真正的工作 static void run_workqueue(struct cpu_workqueue_struct *cwq) { unsigned long flags; spin_lock_irqsave(&cwq->lock, flags); ... //順次執行隊列中的所有工作 while (!list_empty(&cwq->worklist)) { struct work_struct *work = list_entry(cwq->worklist.next, struct work_struct, entry); void (*f) (void *) = work->func; void *data = work->data; //從隊列中刪除待執行的任務 list_del_init(cwq->worklist.next); spin_unlock_irqrestore(&cwq->lock, flags); BUG_ON(work->wq_data != cwq); clear_bit(0, &work->pending); //執行“工作” f(data); spin_lock_irqsave(&cwq->lock, flags); cwq->remove_sequence++; wake_up(&cwq->work_done); // } cwq->run_depth--; spin_unlock_irqrestore(&cwq->lock, flags); } 三、背景工作執行緒建立的詳細過程 create_workqueue() -> __create_workqueue() -> create_workqueue_thread() -> kthread_create() struct task_struct *kthread_create(int (*threadfn)(void *data), void *data, const char namefmt[], ...) { //初始化用於建立線程的輔助結構 struct kthread_create_info create; DECLARE_WORK(work, keventd_create_kthread, &create); create.threadfn = threadfn; create.data = data; init_completion(&create.started); init_completion(&create.done); if (!helper_wq) //首先建立輔助工作隊列 work.func(work.data); else { //注意,“建立一個工作隊列”這個工作本身又是屬於helper_wq工作隊列 //的一項工作,所以,將這個工作加入的輔助工作隊列中等待執行。 queue_work(helper_wq, &work); wait_for_completion(&create.done); } ... return create.result; } create_workqueue() -> __create_workqueue() -> create_workqueue_thread() -> kthread_create()-> keventd_create_kthread() //最終會調用kernel_thread為每個工作隊列建立一個線程 //這樣,被建立的線程會以create為資料執行kthread(如下),而kthread中則執行create中的threadfn(data), //即為create_workqueue_thread中的worker_thread(cwq),即為我們工作隊列要執行的函數了。 static void keventd_create_kthread(void *_create) { struct kthread_create_info *create = _create; int pid; /* We want our own signal handler (we take no signals by default). */ pid = kernel_thread(kthread, create, CLONE_FS | CLONE_FILES | SIGCHLD); if (pid < 0) { create->result = ERR_PTR(pid); } else { wait_for_completion(&create->started); read_lock(&tasklist_lock); create->result = find_task_by_pid(pid); read_unlock(&tasklist_lock); } complete(&create->done); } static int kthread(void *_create) { struct kthread_create_info *create = _create; int (*threadfn)(void *data); void *data; ... threadfn = create->threadfn; data = create->data; ... if (!kthread_should_stop()) ret = threadfn(data); ... return 0; } 四、插入“工作” /* Preempt must be disabled. */ static void __queue_work(struct cpu_workqueue_struct *cwq, struct work_struct *work) { unsigned long flags; spin_lock_irqsave(&cwq->lock, flags); work->wq_data = cwq; //將當前工作插入到工作隊列中待待執行 list_add_tail(&work->entry, &cwq->worklist); cwq->insert_sequence++; wake_up(&cwq->more_work); //喚醒相應線程 spin_unlock_irqrestore(&cwq->lock, flags); } int fastcall queue_work(struct workqueue_struct *wq, struct work_struct *work) { int ret = 0, cpu = get_cpu(); //如裡當前工作未在隊列中才插入 if (!test_and_set_bit(0, &work->pending)) { ... __queue_work(per_cpu_ptr(wq->cpu_wq, cpu), work); ret = 1; } put_cpu(); return ret; } |