complete完成量——实例分析

一、完成量的使用步骤

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, &param);
        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);
    }
}

 

上一篇:【最短路】CF715B Complete The Graph


下一篇:cf1182E Complete Mirror