1、完成量的使用步驟node
1. 完成量的基本使用流程linux
/* 1.定義一個completion結構並初始化 */ struct completion done; init_completion(&create.done); /* 2.一個進程進行等待 */ wait_for_completion(&kthreadd_done); /* 2.另外一個進程執行喚醒 */ complete(&done);
2. 完成量是基於等待隊列實現的,所以可能會阻塞,不能用於原子上下文函數
struct completion { unsigned int done; wait_queue_head_t wait; };
2、完成量使用的經典例子——建立內核線程this
1. 相關的kthreadd內核線程啓動流程spa
start_kernel rest_init /*start_kernel的最後調用的是rest_init*/ pid = kernel_thread(kernel_init, NULL, CLONE_FS); /*這裏面啓動init進程*/ free_initmem();/*這個線程裏釋放了__init段的內存*/ try_to_run_init_process("/sbin/init") /*調用execve函數簇將pid=1的內核線程替換爲init進程(pid=1)*/ pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES); for(;;) /*用來循環建立內核線程(pid=2)*/ create_kthread(create);
2. complete的使用流程線程
struct kthread_create_info { int (*threadfn)(void *data); void *data; int node; struct task_struct *result; /* 完成量 */ struct completion *done; struct list_head list; };
在建立內核線程的例子中,使用了一個kthread_create_info結構來封裝了一個完成量:rest
//linux4.14.39/kernel/kthread.c struct task_struct *__kthread_create_on_node(int (*threadfn)(void *data), void *data, int node, const char namefmt[], va_list args) { /* 1.靜態定義並初始化一個完成量 */ DECLARE_COMPLETION_ONSTACK(done); struct task_struct *task; struct kthread_create_info *create = kmalloc(sizeof(*create), GFP_KERNEL); if (!create) return ERR_PTR(-ENOMEM); create->threadfn = threadfn; create->data = data; create->node = node; create->done = &done; spin_lock(&kthread_create_lock); /* 2.將完成量添加到鏈表中 */ list_add_tail(&create->list, &kthread_create_list); spin_unlock(&kthread_create_lock); /* 3.喚醒kthreadd內核線程,是由它負責建立全部的內核線程 */ wake_up_process(kthreadd_task); /* * Wait for completion in killable state, for I might be chosen by * the OOM killer while kthreadd is trying to allocate memory for * new kernel thread. */ /* 4.等待kthreadd建立完成這個內核線程 */ if (unlikely(wait_for_completion_killable(&done))) { /* * If I was SIGKILLed before kthreadd (or new kernel thread) * calls complete(), leave the cleanup of this structure to * that thread. */ if (xchg(&create->done, NULL)) return ERR_PTR(-EINTR); /* * kthreadd (or new kernel thread) will call complete() * shortly. */ wait_for_completion(&done); } /* 5.獲取完成量的執行結果 */ task = create->result; if (!IS_ERR(task)) { static const struct sched_param param = { .sched_priority = 0 }; vsnprintf(task->comm, sizeof(task->comm), namefmt, args); /* * root may have changed our (kthreadd's) priority or CPU mask. * The kernel thread should not inherit these properties. */ sched_setscheduler_nocheck(task, SCHED_NORMAL, ¶m); set_cpus_allowed_ptr(task, cpu_all_mask); } /* 6. 釋放完成量 */ kfree(create); return task; }
int kthreadd(void *unused) { struct task_struct *tsk = current; /* Setup a clean context for our children to inherit. */ set_task_comm(tsk, "kthreadd"); ignore_signals(tsk); set_cpus_allowed_ptr(tsk, cpu_all_mask); set_mems_allowed(node_states[N_MEMORY]); current->flags |= PF_NOFREEZE; cgroup_init_kthreadd(); for (;;) { /* 1.沒有任務要建立的時候就休眠,要建立內核線程時會把它喚醒 */ set_current_state(TASK_INTERRUPTIBLE); if (list_empty(&kthread_create_list)) schedule(); __set_current_state(TASK_RUNNING); spin_lock(&kthread_create_lock); while (!list_empty(&kthread_create_list)) { struct kthread_create_info *create; /* 2.從鏈表中取出這個kthread_create_info結構 */ create = list_entry(kthread_create_list.next, struct kthread_create_info, list); list_del_init(&create->list); spin_unlock(&kthread_create_lock); /* 3. 建立內核線程 */ create_kthread(create); spin_lock(&kthread_create_lock); } spin_unlock(&kthread_create_lock); } return 0; }
static void create_kthread(struct kthread_create_info *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) { /* If user was SIGKILLed, I release the structure. */ struct completion *done = xchg(&create->done, NULL); if (!done) { kfree(create); return; } /* 1.給完成量的結果賦值 */ create->result = ERR_PTR(pid); /* 2.喚醒這個完成量上等待的線程 */ complete(done); } }