一、完成量的使用步骤
1. 完成量的基本使用流程
/* 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; };
二、完成量使用的经典例子——创建内核线程
1. 相关的kthreadd内核线程启动流程
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结构来封装了一个完成量:
//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); } }