内核线程创建: kthread

    技术2025-10-27  4

    内核线程的创建,最好使用kthread_create。 kthread_create vs kernel_thread的区别就是,前者是在后者的基础上封装的内核创建接口。如果用kernel_thread创建线程,需要创建者去实现complete和wakeup。kthread_create是在内核启动时创建了一个kthreadd的线程来创建的,该线程会wait在kthread_create_list的queue上,当queue非空,kthreadd醒来用kernel_thread创建kthread_create_list中添加的线程。用kthread_create创建线程也就是往kthread_create_list中添加要创建的线程的信息“struct kthread_create_info create”

     

    下面是转来的一片文章,其中的代码和2.6.36的不完全相同,但是原理和流程相同,最新的代码做了优化。

     

    kthread.c/*** kthread_create - create a kthread.* @threadfn: the function to run until signal_pending(current).* @data: data ptr for @threadfn.* @namefmt: printf-style name for the thread.** Description: This helper function creates and names a kernel* thread. The thread will be stopped: use wake_up_process() to start* it. See also kthread_run(), kthread_create_on_cpu().** When woken, the thread will run @threadfn() with @data as its* argument. @threadfn() can either call do_exit() directly if it is a* standalone thread for which noone will call kthread_stop(), or* return when 'kthread_should_stop()' is true (which means* kthread_stop() has been called). The return value should be zero* or a negative error number; it will be passed to kthread_stop().** Returns a task_struct or ERR_PTR(-ENOMEM).*/struct task_struct *kthread_create(int (*threadfn)(void *data),                   void *data,                   const char namefmt[],                   ...){    struct kthread_create_info create;

        create.threadfn = threadfn;    create.data = data;    init_completion(&create.started);    init_completion(&create.done);

        spin_lock(&kthread_create_lock);    list_add_tail(&create.list, &kthread_create_list);    wake_up_process(kthreadd_task);    spin_unlock(&kthread_create_lock);

        wait_for_completion(&create.done);

        if (!IS_ERR(create.result)) {        va_list args;        va_start(args, namefmt);        vsnprintf(create.result->comm, sizeof(create.result->comm),            namefmt, args);        va_end(args);    }    return create.result;}首先,声明了一个struct kthread_create_info结构,这个结构包含了针对内核线程操作的所要用到的字段:struct kthread_create_info{    /* Information passed to kthread() from kthreadd. */    int (*threadfn)(void *data);    void *data;    struct completion started;

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

        struct list_head list;};其中包含了线程要干的活,同步用到的结构,以及线程的描述符,比如说函数最后返回的create.result,就是创建的内核"线程"的进程描述符.然后初始化threadfn以及他的函数参数,将这个kthread_create_info结构挂入全局的内核线程队列,并唤醒进程kthreadd_task,开始等待,等待......kthreadd_task是个什么玩意?这是在本文件头部声明的一个全局进程描述符,在main.c中的rest_init里初始化:static void noinline __init_refok rest_init(void)    __releases(kernel_lock){    int pid;

        kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);    numa_default_policy();   pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);     kthreadd_task = find_task_by_pid(pid);     unlock_kernel();

        /*    * The boot idle thread must execute schedule()    * at least one to get things moving:    */    preempt_enable_no_resched();    schedule();    preempt_disable();

        /* Call into cpu_idle with preempt disabled */    cpu_idle();} 我们看到里面kernel_thread函数返回一个pid,然后我们通过这个pid得到了这个kthreadd_task的描述符.其实我们的kthread_create这个函数并没有直接创建什么内核线程,这里的kernel_thread这里才是真正创建内核线程的所在:/** Create a kernel thread*/int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags){    struct pt_regs regs;

        memset(&regs, 0, sizeof(regs));

        regs.ebx = (unsigned long) fn;    regs.edx = (unsigned long) arg;

        regs.xds = __USER_DS;    regs.xes = __USER_DS;    regs.xfs = __KERNEL_PERCPU;    regs.orig_eax = -1;    regs.eip = (unsigned long) kernel_thread_helper;    regs.xcs = __KERNEL_CS | get_kernel_rpl();    regs.eflags = X86_EFLAGS_IF | X86_EFLAGS_SF | X86_EFLAGS_PF | 0x2;

        /* Ok, create the new process.. */    return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, &regs, 0, NULL, NULL); }do_fork(),一个伟大的函数,经过他的流程,一个新的"内核线程"诞生了...我们来看看kthreadd_task到底都干了些什么:int kthreadd(void *unused){    /* Setup a clean context for our children to inherit. */    kthreadd_setup();

        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;}写过驱动的哥们都知道,这个套路不陌生.整个一大循环,如果全局的kthread_create_list为空,我们这个内核线程就会一直沉睡,如果醒来发现还是空的怎么办?废话,当然是继续睡了!当有一天这个线程醒了发现有事可做了,那么他就会从这个队列中取下一个他渴望的猎物,然后,然后,然后你说呢?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) {        create->result = ERR_PTR(pid);    } else {        wait_for_completion(&create->started);        read_lock(&tasklist_lock);        create->result = find_task_by_pid(pid);        read_unlock(&tasklist_lock);    }    complete(&create->done);}我们的内核线程是在这里创建的,要做什么呢?答案在传进来的struct kthread_create_info *create里.然后我们对返回的pid检查,如果正常,那么我们就等待内核线程的对create->started的同步,来告诉父"线程",我们已经OK了.同样我们通过另一个同步信号create->done来告诉调用kthread_create的进程,你想要的东西已经准备好了!static int kthread(void *_create){    struct kthread_create_info *create = _create;    int (*threadfn)(void *data);    void *data;    int ret = -EINTR;

        /* Copy data: it's on kthread's stack */    threadfn = create->threadfn;    data = create->data;

        /* OK, tell user we're spawned, wait for stop or wakeup */    __set_current_state(TASK_UNINTERRUPTIBLE);    complete(&create->started);    schedule();

        if (!kthread_should_stop())        ret = threadfn(data);

        /* It might have exited on its own, w/o kthread_stop. Check. */    if (kthread_should_stop()) {        kthread_stop_info.err = ret;        complete(&kthread_stop_info.done);    }    return 0;}我们在同步create->started后就让出了CPU,并等待唤醒.在我们的kthread_run里,我们不需要等待很长的时间,因为很快,我们就有   if (!IS_ERR(__k))                             wake_up_process(__k); 当然,我们写驱动的时候可以根据自己的情况,比如创建一个内核线程以后我们要做一些别的操作比如增加计数什么的,我们就不用这个宏,然后自己来唤醒内核线程.

     

    本文来自博客,转载请标明出处:http://blog.csdn.net/unbutun/archive/2009/12/07/4957312.aspx

    最新回复(0)