complete完成量实例分析
【推荐阅读】
浅谈linux 内核网络 sk_buff 之克隆与复制
深入linux内核架构--进程&线程
了解Docker 依赖的linux内核技术
一、完成量的使用步骤
完成量的基本使用流程
/* 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_kernelrest_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);}
}
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
