zoukankan      html  css  js  c++  java
  • linux内核 kthread_run函数 理解学习

    linux内核 kthread_run函数 理解学习
    2010-08-09 20:34

         最近发现在内核创建线程的时候经常会用到kthread_run()这样的一个调用。于是准备拿出来学习一下。首先看看它的定义之处才发现它是一个宏函数,而不是一个真正意义上的函数。在include/linux/Kthread.h里有/**
    * kthread_run - create and wake a thread.
    * @threadfn: the function to run until signal_pending(current).
    * @data: data ptr for @threadfn.
    * @namefmt: printf-style name for the thread.
    *
    * Description: Convenient wrapper for kthread_create() followed by
    * wake_up_process(). Returns the kthread or ERR_PTR(-ENOMEM).
    */
    #define kthread_run(threadfn, data, namefmt, ...)      \
    ({            \
    struct task_struct *__k         \
       = kthread_create(threadfn, data, namefmt, ## __VA_ARGS__); \
    if (!IS_ERR(__k))         \
       wake_up_process(__k);        \
    __k;           \
    })

    这个函数的英文注释里很明确的说明: 创建并启动一个内核线程。可见这里的函数kthread_create()只是创建了内核线程,而最后启动是怎么启动的呢,我们看到了后面的wake_up_process()函数,没错就是这个函数启动了这个线程,让它在一开始就一直运行下去。知道遇见kthread_should_stop函数或者kthread_stop()函数。那我们具体看看前一个函数到底做了什么吧。

    在这个宏里面主要是调用了函数:kthread_create()

    这个函数是干什么的呢?在Kernel/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;
    DECLARE_WORK(work, keventd_create_kthread, &create);

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

    /*
    * The workqueue needs to start up first:
    */
    if (!helper_wq)
       work.func(work.data);
    else {
       queue_work(helper_wq, &work);
       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;
    }
    EXPORT_SYMBOL(kthread_create);

    注意到上面的这段英文解释:说这个函数会创建一个名为namefmt的内核线程,这个线程刚创建时不会马上执行,要等到它将kthread_create() 返回的task_struct指针传给wake_up_process(),然后通过此函数运行线程。我们看到creat结构体,我们将传入的参数付给了它,而threadfn这个函数就是创建的运行函数。在使用中我们可以在此函数中调用kthread_should_stop()或者kthread_stop()函数来结束线程。这里我们看到创建线程函数中使用工作队列DECLARE_WORK,我们跟踪一下发现这只是将函数#define DECLARE_WORK(n, f, d)      \
    struct work_struct n = __WORK_INITIALIZER(n, f, d)
    然后再跟进:

    #define __WORK_INITIALIZER(n, f, d) {     \
    .entry = { &(n).entry, &(n).entry },    \
    .func = (f),       \
    .data = (d),       \
    .timer = TIMER_INITIALIZER(NULL, 0, 0),    \
    }

    反正目的是创建一个工作组队列,而其中keventd_create_kthread()函数主要是起到创建线程的功能

    /* We are keventd: create a thread. */
    static void keventd_create_kthread(void *_create)
    {
    struct kthread_create_info *create = _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);
    }
    再看看kernel_thread()函数最后调用到了哪里:

    /*
    * Create a kernel thread.
    */
    pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
    {
    struct pt_regs regs;
    long pid;

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

    regs.ARM_r1 = (unsigned long)arg;
    regs.ARM_r2 = (unsigned long)fn;
    regs.ARM_r3 = (unsigned long)do_exit;
    regs.ARM_pc = (unsigned long)kernel_thread_helper;
    regs.ARM_cpsr = SVC_MODE;

    pid = do_fork(flags|CLONE_VM|CLONE_UNTRACED, 0, &regs, 0, NULL, NULL);

    MARK(kernel_thread_create, "%ld %p", pid, fn);
    return pid;
    }
    EXPORT_SYMBOL(kernel_thread);

    好,最后我们看到了线程通过申请进程的pid号来被创建,关键是我们要知道如何使用这个宏函数,也就是如何应用它。要注意的是它调用了创建线程函数,同时也激活了线程。所以代码中调用了它的话就隐含着已经启动一个线程。

  • 相关阅读:
    架构思维—软件架构—系统架构—系统—大局观、系统观(结构与秩序)、还原论(分与合)
    微核架构的本质是微核掌握了更多的上下文-----微核架构 = 整体上下文 + 配置组成
    spring mvc的工作流程
    @getMapping和@postMapping,@RestController
    springTransaction Management
    Architecture of Spring Framework
    Tomcat 6 —— Realm域管理
    Tomcat模型结构
    spring的启动过程
    ServletContext、webApplicationContext、DispatcherServlet与容器
  • 原文地址:https://www.cnblogs.com/yuzaipiaofei/p/4124304.html
Copyright © 2011-2022 走看看