linux 3.10中完成量的经典使用

完成量是基于等待队列设计的,所以显然不能在中断上下文使用完成量。

struct completion {
    unsigned int done;
    wait_queue_head_t wait;
};

我们来看一个使用完成量的经典例子:

struct kthread_create_info
{
    /* Information passed to kthread() from kthreadd. */
    int (*threadfn)(void *data);
    void *data;
    int node;

    /* Result passed back to kthread_create() from kthreadd. */
    struct task_struct *result;
    struct completion done;

    struct list_head list;
};

在创建内核线程的例子中,我们使用了一个kthread_create_info结构来封装了一个完成量:

struct task_struct *kthread_create_on_node(int (*threadfn)(void *data),
                       void *data, int node,
                       const char namefmt[],
                       ...)
{
    struct kthread_create_info create;

    create.threadfn = threadfn;---------------要创建的线程的主函数
    create.data = data;
    create.node = node;
    init_completion(&create.done);------------初始化完成量

    spin_lock(&kthread_create_lock);
    list_add_tail(&create.list, &kthread_create_list);-------------加入链表,相当于把请求挂在一个双向循环链表中
    spin_unlock(&kthread_create_lock);

    wake_up_process(kthreadd_task);-----------唤醒处理完成量的内核线程,来处理我们发送的请求
    wait_for_completion(&create.done);--------等待完成,这个在等待完成量的期间,会导致本进程睡眠
。。。。。。。

如上代码是提交请求的一侧,那么,处理请求的一侧是怎么完成该任务,并通知到请求方呢?

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;

    for (;;) {
        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;

            create = list_entry(kthread_create_list.next,
                        struct kthread_create_info, list);--------------取出请求
            list_del_init(&create->list);------------将请求从链表隔离
            spin_unlock(&kthread_create_lock);-------解锁,这个锁保证加入请求和解除请求的串行化

            create_kthread(create);------------------创建线程

            spin_lock(&kthread_create_lock);
        }
        spin_unlock(&kthread_create_lock);
    }

    return 0;
}

简单地看,没看到怎么通知请求方,代码其实是在create_kthread中实现的:

static void create_kthread(struct kthread_create_info *create)
{
    int pid;

#ifdef CONFIG_NUMA
    current->pref_node_fork = create->node;
#endif
    /* 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) {
        create->result = ERR_PTR(pid);
        complete(&create->done);-------------------通知请求方,一般就是唤醒了
    }
}

以上就是使用完成量的经典例子,两个互不干扰的执行流,一个通过wait_for_completion来等待请求完成,一个通过complete,还有complete_all等来通知请求方,完成交互。

这个例子比较有意思的地方是,我们可以看到,内核线程的创建接口,是由 kthreadd 来完成fork的,

 ps -ef |grep -i kthreadd
root         2     0  0 9月15 ?       00:00:00 [kthreadd]

这个内核线程的pid是2,其他所有的内核线程都是它fork出来的,因为init进程占据了pid 1,所以它的pid是2。

我们假设一下,如果pid 为1的init进程,最终不去执行

if (!run_init_process("/sbin/init") ||
        !run_init_process("/etc/init") ||
        !run_init_process("/bin/init") ||
        !run_init_process("/bin/sh"))

那么它这个时候纯粹还是内核线程,它全部工作在内核态,没有用户态进程的os有没有用呢?

我觉得是有的,没有交互罢了,全部在内核态。恩,如果你把一些任务放在内核里面完成,完成可以不要用户态进程嘛。

猜你喜欢

转载自www.cnblogs.com/10087622blog/p/9666929.html