Skip to main content
 首页 » 操作系统

Linux complete完成量——实例分析

2022年07月19日151grandyang

一、完成量的使用步骤

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); 
    } 
}

本文参考链接:https://www.cnblogs.com/hellokitty2/p/10982397.html