• 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);
        }
    }
  • 相关阅读:
    JZOJ Contest2633 总结
    6813. 【2020.10.05提高组模拟】送信
    HDU 1506 最大子矩形
    2020.10.07【NOIP提高A组】模拟 总结
    6815. 【2020.10.06提高组模拟】树的重心
    2020.10.06【NOIP提高A组】模拟 总结
    2020.10.05【NOIP提高A组】模拟 总结
    gmoj 3976. 【NOI2015模拟1.17】⑨
    2020.09.26【省选组】模拟 总结
    2020.09.19【省选组】模拟 总结
  • 原文地址:https://www.cnblogs.com/hellokitty2/p/10982397.html
Copyright © 2020-2023  润新知