工作隊列分析 queue_work

 一、用法 
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; 
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章