complete完成量——實例分析

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, &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);
    }
}
相關文章
相關標籤/搜索