zoukankan      html  css  js  c++  java
  • Linux内核多线程(一)

    Linux内核可以看作一个服务进程(管理软硬件资源,响应用户进程的种种合理以及不合理的请求)。内核需要多个执行流并行,为了防止可能的阻塞,支持多线程是必要的。内核线程就是内核的分身,一个分身可以处理一件特定事情。内核线程的调度由内核负责,一个内核线程处于阻塞状态时不影响其他的内核线程,因为其是调度的基本单位。这与用户线程是不一样的。因为内核线程只运行在内核态,因此,它只能使用大于PAGE_OFFSET(3G)的地址空间。内核线程和普通的进程间的区别在于内核线程没有独立的地址空间,mm指针被设置为NULL;它只在 内核空间运行,从来不切换到用户空间去;并且和普通进程一样,可以被调度,也可以被抢占。

    内核线程(thread)或叫守护进程(daemon),在操作系统中占据相当大的比例,当Linux操作系统启动以后,你可以用”ps -ef”命令查看系统中的进程,这时会发现很多以”d”结尾的进程名,确切说名称显示里面加 "[]"的,这些进程就是内核线程。

    创建内核线程最基本的两个接口函数是:

    kthread_run(threadfn, data, namefmt, ...)    

    kernel_thread(int(* fn)(void *),void * arg,unsigned long flags)

    这里我们主要介绍kthread_run,后面会专门分析这两个函数的异同。

    kthread_run 事实上是一个宏定义:

    /**
    
     * 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_run()负责内核线程的创建,它由kthread_create()和wake_up_process()两部分组成,这样的好处是用kthread_run()创建的线程可以直接运行。外界调用kthread_run创建运行线程。kthread_run是个宏定义,首先调用kthread_create()创建线程,如果创建成功,再调用wake_up_process()唤醒新创建的线程。kthread_create()根据参数向kthread_create_list中发送一个请求,并唤醒kthreadd,之后会调用wait_for_completion(&create.done)等待线程创建完成。新创建的线程开始运行后,入口在kthread(),kthread()调用complete(&create->done)唤醒阻塞的模块进程,并使用schedule()调度出去。kthread_create()被唤醒后,设置新线程的名称,并返回到kthread_run中。kthread_run调用wake_up_process()重新唤醒新创建线程,此时新线程才开始运行kthread_run参数中的入口函数。

    在介绍完如何创建线程之后,下面来介绍另外两个基本的函数:

    int kthread_stop(struct task_struct *k);

    int kthread_should_stop(void);

     kthread_stop()负责结束创建的线程,参数是创建时返回的task_struct指针。kthread设置标志should_stop,并等待线程主动结束,返回线程的返回值。在调用 kthread_stop()结束线程之前一定要检查该线程是否还在运行(通过 kthread_run 返回的 task_stuct 是否有效),否则会造成灾难性的后果。kthread_run的返回值tsk。不能用tsk是否为NULL进行检查,而要用IS_ERR()宏定义检查,这是因为返回的是错误码,大致从0xfffff000~0xffffffff。

     kthread_should_stop()返回should_stop标志(参见 struct kthread )。它用于创建的线程检查结束标志,并决定是否退出。

    kthread() (注:原型为:static int kthread(void *_create) )的实现在kernel/kthread.c中,头文件是include/linux/kthread.h。内核中一直运行一个线程kthreadd,它运行kthread.c中的kthreadd函数。在kthreadd()中,不断检查一个kthread_create_list链表。kthread_create_list中的每个节点都是一个创建内核线程的请求,kthreadd()发现链表不为空,就将其第一个节点退出链表,并调用create_kthread()创建相应的线程。create_kthread()则进一步调用更深层的kernel_thread()创建线程,入口函数设在kthread()中。

          外界调用kthread_stop()删除线程。kthread_stop首先设置结束标志should_stop,然后调用wake_for_completion(&kthread->exited)上,这个其实是新线程task_struct上的vfork_done,会在线程结束调用do_exit()时设置。

    附:

    struct kthread {
    
           int should_stop;
    
           struct completion exited;
    
    };
    
    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_HIGH_MEMORY]);
    
    
           current->flags |= PF_NOFREEZE | PF_FREEZER_NOSIG;
    
     
           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_stop - stop a thread created by kthread_create().
    
     * @k: thread created by kthread_create().
    
     *
    
     * Sets kthread_should_stop() for @k to return true, wakes it, and
    
     * waits for it to exit. This can also be called after kthread_create()
    
     * instead of calling wake_up_process(): the thread will exit without
    
     * calling threadfn().
    
     *
    
     * If threadfn() may call do_exit() itself, the caller must ensure
    
     * task_struct can't go away.
    
     *
    
     * Returns the result of threadfn(), or %-EINTR if wake_up_process()
    
     * was never called.
    
     */
    
    int kthread_stop(struct task_struct *k)
    {
    
           struct kthread *kthread;
    
           int ret;
    
     
           trace_sched_kthread_stop(k);
    
           get_task_struct(k);
    
     
           kthread = to_kthread(k);
    
           barrier(); /* it might have exited */
    
           if (k->vfork_done != NULL) {
    
                  kthread->should_stop = 1;
    
                  wake_up_process(k);
    
                  wait_for_completion(&kthread->exited);
    
           }
    
           ret = k->exit_code;
    
           put_task_struct(k);
    
           trace_sched_kthread_stop_ret(ret);
    
           return ret;
    
    }
    
  • 相关阅读:
    javascript获取本地机器信息
    js中获取日期格式
    C#.NET 对HashTable数组进行按值排序
    c# 修饰符
    Android浏览器软键盘中的回车(确认)会触发表单提交的问题解决办法
    excel 选择一个单元格,高亮一行
    Berkeley Language Model
    [转]sqlserver2005 【用户 'sa' 登录失败。该用户与可信 SQL Server 连接无关联】解决方案
    Berkeley Parser
    自然语言处理(NLP)网上资源整理 (转)
  • 原文地址:https://www.cnblogs.com/zhuyp1015/p/2545624.html
Copyright © 2011-2022 走看看