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);
    }
}
复制代码

 

posted on   Hello-World3  阅读(1152)  评论(0编辑  收藏  举报

编辑推荐:
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!

导航

< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5
点击右上角即可分享
微信分享提示