内核线程、轻量级进程、用户线程、用户进程

来源:互联网 发布:日本对华援助知乎 编辑:程序博客网 时间:2024/05/21 06:52

内核线程

内核线程只运行在内核态,不受用户态上下文的拖累。

  • 处理器竞争:可以在全系统范围内竞争处理器资源;
  • 使用资源:唯一使用的资源是内核栈和上下文切换时保持寄存器的空间
  • 调度:调度的开销可能和进程自身差不多昂贵
  • 同步效率:资源的同步和数据共享比整个进程的数据同步和共享要低一些。

轻量级进程(vfork创建)

轻量级进程(LWP)是建立在内核之上并由内核支持的用户线程,它是内核线程的高度抽象,每一个轻量级进程都与一个特定的内核线程关联。内核线程只能由内核管理并像普通进程一样被调度。

轻量级进程由clone()系统调用创建,参数是CLONE_VM,即与父进程是共享进程地址空间和系统资源。

与普通进程区别:LWP只有一个最小的执行上下文和调度程序所需的统计信息。

  • 处理器竞争:因与特定内核线程关联,因此可以在全系统范围内竞争处理器资源
  • 使用资源:与父进程共享进程地址空间
  • 调度:像普通进程一样调度

用户线程

用户线程是完全建立在用户空间的线程库,用户线程的创建、调度、同步和销毁全又库函数在用户空间完成,不需要内核的帮助。因此这种线程是极其低消耗和高效的。

  • 处理器竞争:单纯的用户线程是建立在用户空间,其对内核是透明的,因此其所属进程单独参与处理器的竞争,而进程的所有线程参与竞争该进程的资源。
  • 使用资源:与所属进程共享进程地址空间和系统资源。
  • 调度:由在用户空间实现的线程库,在所属进程内进行调度

Linux使用的线程库

LinuxThreads是用户空间的线程库,所采用的是线程-进程1对1模型(即一个用户线程对应一个轻量级进程,而一个轻量级进程对应一个特定 的内核线程),将线程的调度等同于进程的调度,调度交由内核完成,而线程的创建、同步、销毁由核外线程库完成(LinuxThtreads已绑定到 GLIBC中发行)。

在LinuxThreads中,由专门的一个管理线程处理所有的线程管理工作。当进程第一次调用pthread_create()创建线程时就会先 创建(clone())并启动管理线程。后续进程pthread_create()创建线程时,都是管理线程作为pthread_create()的调用 者的子线程,通过调用clone()来创建用户线程,并记录轻量级进程号和线程id的映射关系,因此,用户线程其实是管理线程的子线程。

LinuxThreads只支持调度范围为PTHREAD_SCOPE_SYSTEM的调度,默认的调度策略是SCHED_OTHER。

用户线程调度策略也可修改成SCHED_FIFO或SCHED_RR方式,这两种方式支持优先级为0-99,而SCHED_OTHER只支持0。

  • SCHED_OTHER 分时调度策略,
  • SCHED_FIFO   实时调度策略,先到先服务
  • SCHED_RR     实时调度策略,时间片轮转

SCHED_OTHER是普通进程的,后两个是实时进程的(一般的进程都是普通进程,系统中出现实时进程的机会很少)。SCHED_FIFO、 SCHED_RR优先级高于所有SCHED_OTHER的进程,所以只要他们能够运行,在他们运行完之前,所有SCHED_OTHER的进程的都没有得到 执行的机会。

附: 基础知识(线程和进程)

按照教科书上的定义,进程是资源管理的最小单位,线程是程序执行的最小单位。在操作系统设计上,从进程演化出线程,最主要的目的就是更好的支持SMP以及减小(进程/线程)上下文切换开销。

无论按照怎样的分法,一个进程至少需要一个线程作为它的指令执行体,进程管理着资源(比如cpu、内存、文件等等),而将线程分配到某个cpu上执 行。一个进程当然可以拥有多个线程,此时,如果进程运行在SMP机器上,它就可以同时使用多个cpu来执行各个线程,达到最大程度的并行,以提高效率;同 时,即使是在单cpu的机器上,采用多线程模型来设计程序,正如当年采用多进程模型代替单进程模型一样,使设计更简洁、功能更完备,程序的执行效率也更 高,例如采用多个线程响应多个输入,而此时多线程模型所实现的功能实际上也可以用多进程模型来实现,而与后者相比,线程的上下文切换开销就比进程要小多 了,从语义上来说,同时响应多个输入这样的功能,实际上就是共享了除cpu以外的所有资源的。

针对线程模型的两大意义,分别开发出了核心级线程和用户级线程两种线程模型,分类的标准主要是线程的调度者在核内还是在核外。前者更利于并发使用多 处理器的资源,而后者则更多考虑的是上下文切换开销。在目前的商用系统中,通常都将两者结合起来使用,既提供核心线程以满足smp系统的需要,也支持用线 程库的方式在用户态实现另一套线程机制,此时一个核心线程同时成为多个用户态线程的调度者。正如很多技术一样,”混合”通常都能带来更高的效率,但同时也 带来更大的实现难度,出于”简单”的设计思路,Linux从一开始就没有实现混合模型的计划,但它在实现上采用了另一种思路的”混合”。

在线程机制的具体实现上,可以在操作系统内核上实现线程,也可以在核外实现,后者显然要求核内至少实现了进程,而前者则一般要求在核内同时也支持进 程。核心级线程模型显然要求前者的支持,而用户级线程模型则不一定基于后者实现。这种差异,正如前所述,是两种分类方式的标准不同带来的。

当核内既支持进程也支持线程时,就可以实现线程-进程的”多对多”模型,即一个进程的某个线程由核内调度,而同时它也可以作为用户级线程池的调度 者,选择合适的用户级线程在其空间中运行。这就是前面提到的”混合”线程模型,既可满足多处理机系统的需要,也可以最大限度的减小调度开销。绝大多数商业 操作系统(如Digital Unix、Solaris、Irix)都采用的这种能够完全实现POSIX1003.1c标准的线程模型。在核外实现的线程又可以分为”一对一”、”多对 一”两种模型,前者用一个核心进程(也许是轻量进程)对应一个线程,将线程调度等同于进程调度,交给核心完成,而后者则完全在核外实现多线程,调度也在用 户态完成。后者就是前面提到的单纯的用户级线程模型的实现方式,显然,这种核外的线程调度器实际上只需要完成线程运行栈的切换,调度开销非常小,但同时因 为核心信号(无论是同步的还是异步的)都是以进程为单位的,因而无法定位到线程,所以这种实现方式不能用于多处理器系统,而这个需求正变得越来越大,因 此,在现实中,纯用户级线程的实现,除算法研究目的以外,几乎已经消失了。

    Linux内核只提供了轻量进程的支持,限制了更高效的线程模型的实现,但Linux着重优化了进程的调度开销,一定程度上也弥补了这一缺陷。目前 最流行的线程机制LinuxThreads所采用的就是线程-进程”一对一”模型,调度交给核心,而在用户级实现一个包括信号处理在内的线程管理机制。 Linux-LinuxThreads的运行机制正是本文的描述重点。


1.kthread_create kernel_thread产生的是进程还是线程,如果是线程那么进程如何产生,如果说内核线程和进程是一个东西,那么到底是进程还是线程,我在一个.C里声明个全局变量,其它的线程可以共享吗?


2.产生内核进程用哪个函数?


1.kthread_create kernel_thread产生的是进程还是线程,如果是线程那么进程如何产生,如果说内核线程和进程是一个东西,那么到底是进程还是线程,我在一个.C里声明个全局变量,其它的线程可以共享吗?
答: 创建内核线程。内核线程和进程不同。
进程,一般是应用程序运行时产生的。
据我所知,没有内核进程这个术语。
全局变量,其它线程可以共享。既然进程可以共享,因为一个进程可以有1个或1个以上的线程组成。不过,要注意,访问全局变量时要加锁,防止形成竞争条件。

 2.产生内核进程用哪个函数?
答:据我所知,没有内核进程这个术语。



没有“内核进程”。“内核线程”本身就是一种特殊的进程,它只在内核空间中运行,因此没有与之相关联的“虚拟地址空间”,也就永远不会被切换到用户空间中执行。但跟一般的进程一样,它们也是可调度的、可抢占的。这一点跟中断处理程序不一样。
Linux一般用内核线程来执行一些特殊的操作。比如负责page cache回写的pdflush内核线程。
另外,在Linux内核中,可调度的东西都对应一个thread_info以及一个task_struct,同一个进程中的线程,跟进程的区别仅仅是它们共享了一些资源,比如地址空间(mm_struct成员指向同一位置)。所以,如果非要觉得内核线程应该被称为“内核进程”,那也没啥不可以,只是这样说的话,就成了文字游戏了。毕竟官方的叫法就是“内核线程”。  

对于Linux系统已经实现的“内核线程”来说,它本身就跑在内核空间中,内核中所有export出来的符号——不管是函数还是变量——它都能访问,“共享”自然是很直接的一件事。