【推荐阅读】
一、完成量的使用步骤
完成量的基本使用流程
/* 1.定义一个completion结构并初始化 */
struct completion done;
init_completion(&create.done);
/* 2.一个进程进行等待 */
wait_for_completion(&kthreadd_done);
/* 2.另一个进程执行唤醒 */
complete(&done);
完成量是基于等待队列实现的,因此可能会阻塞,不能用于原子上下文
struct completion {
unsigned int done;
wait_queue_head_t wait;
};
二、完成量使用的经典例子——创建内核线程
相关的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);
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);
}
}