作多线程应用开发,对于线程的理解是很是重要的,咱们要为咱们建立的每个线程负责。这篇文章主要聊聊操做系统线程相关的主题,在了解线程定义、用户态与内核态、模态切换、线程上下文切换的基础之上再对常见的三种线程模型进行进一步介绍,但愿对你们可以有所帮助。java
什么是线程?《POSIX Threads Programming》中有一段话对线程的定义进行描述:git
A thread is defined as an independent stream of instructions that can be scheduled to run as such by the operating system.
线程能够被认为是一个能够被独立调度的实体,这个实体共享进程的地址空间、文件描述符、代码和数据,且拥有本身私有的栈、寄存器上下文、和程序计数器。github
咱们在github上面给开源项目提交代码的时候,按照comment格式都要写Motivation这部分,咱们今天讨论线程这个存在,也要讨论线程为何存在。安全
在不少应用中须要同时执行多个任务,这些任务大部分甚至所有均可以相互独立的并行的执行。好比一个网络代理,传统的实现是用一个进程做为监听器来监听网络端口,当有客户端链接进来的时候,当前进程将会fork一个新的进程来处理客户端的请求。这种体系结构很差的地方以下:网络
线程的出现就是为了解决这些问题,线程之间拥有共享的进程空间用于共享数据、也有本身独立的运行空间相似一个轻量级的进程。多线程
在理解用户线程与内核线程以前、咱们有必要了解一下用户空间与内核空间。现代操做系统的地址空间主要基于虚拟地址空间机制设计,和实际物理内存大小不要紧,好比对于32位操做系统,它的寻址空间为2的32次方也就是4G,这里的寻址空间被称为虚拟存储空间。操做系统的核心是内核,独立于普通应用程序,具备最高权限,能够访问底层硬件设备以及受保护的空间,所以这部分包括驱动程序和操做系统。操做系统的设计者为了保证内核的安全,将用户进程设计为只有必定权限的程序,它不可以操做内核以及硬件。操做系统将虚拟存储空间划分为两部分,一部分是内核空间,一部分是用户空间。针对Linux操做系统而言,最高的1G字节供内核使用,称为内核空间,较低的3G字节供给各个进程使用,被称为用户空间。进程能够经过系统调用进入内核,Linux内核由全部进程共享。用户空间和内核空间示意图以下:并发
用户空间及内核空间jvm
每一个进程都拥有全部的虚拟地址空间,当进程运行用户代码的时候是运行在用户地址空间的,这时候CPU运行所须要的指令和数据都保存在用户空间,进程能够认为是指令+数据+CPU,所以这个时候咱们把这个状态的进程叫作用户进程。当用户执行系统调用而陷入内核代码中执行的时候,当前进程运行的指令和数据都在内核空间,所以咱们把这个状态的进程叫内核进程。用户进程和内核进程不是独立的两个进程的意思,而是进程运行的不一样状态。值得注意的是,用户进程不能访问内核虚拟地址空间,内核进程能够访问所有的虚拟地址空间,所以用户进程和内核进程进行数据交换只能经过内核进程从用户地址空间取数据,而后放入用户地址空间。性能
系统调用涉及到进程从用户态到内核态的切换(mode switch),这个时候涉及到的切换主要是寄存器上下文的切换,和一般所说的进程上下文切换不一样,mode switch的消耗相对要小不少。spa
上面能够看出,用户线程与内核线程的区别主要在于指令与数据运行于不一样虚拟地址空间,用户线程和内核线程也能够叫作用户空间线程和内核空间线程。用户线程由用户代码支持,内核线程由操做系统内核支持。
线程上下文切换和线程模态切换不是一个维度的东东,线程上下文切换讲的是多线程之间由于调度器的调度,而从一个线程正在被调度切换到另一个线程被调度的事情。线程上下文切换必需要保存线程执行的寄存器状态、栈信息、线程正文、数据等,所以相对模态切换是比较重的操做。
线程模型在不一样的操做系统下的实现一般有三种,每种模型都有其优势与缺点,下面咱们来看看这三种线程模型。
一个多线程子系统有可能所有由用户代码实现,这些线程的调度与切换所有发生在用户地址空间,这种模型一般是由一个内核线程和多个用户线程组成。典型的实现是基于POSIX线程draft 4,OSF'DCE是其中一种具体实现。一个用户空间库负责线程的建立、终止、调度与同步。这些线程对于操做系统内核是透明的。
这种模型的好处是线程上下文切换都发生在用户空间,避免的模态切换(mode switch),从而对于性能有积极的影响。然而很差的地方是全部的线程基于一个内核调度实体即内核线程,这意味着只有一个处理器能够被利用,在多处理环境下这是不可以被接受的,本质上,用户线程只解决了并发问题,可是没有解决并行问题。
还有一点,若是线程由于I/O操做陷入了内核态,内核态线程阻塞等待I/O数据,则全部的线程都将会被阻塞,用户空间也可使用非阻塞而I/O,可是仍是有性能及复杂度问题。
用户空间线程模型
对于用户空间线程模型,全部的用户线程都和特定的内核线程进行交互,而内核空间线程模型是每一个用户线程都和一个特定的内核线程进行交互,用户线程和内核线程是1:1的关系。典型的实现是将每一个用户线程映射到一个内核线程上。
每一个线程由内核调度器独立的调度,因此若是一个线程阻塞则不影响其余的线程。然而,建立、终止和同步线程都会发生在内核地址空间,这可能会带来较大的性能问题。在建立线程的时候内核必需要进行内存锁的申请,并负责调度线程,并且每一个线程都要消耗有限的内核资源,当大量的线程被建立的时候,体现的尤其明显。值得夸奖的是,在多核处理器的硬件的支持下,内核空间线程模型支持了真正的并行,下面是内核空间模型示意图:
内核空间线程模型
内核用户空间线程模型中,内核线程和用户线程的数量比为M : N,所以也一般被叫作M : N线程模型,内核用户空间综合了前两种的优势。
这种模型须要内核线程调度器和用户空间线程调度器相互操做,本质上是多个线程被绑定到了多个内核线程上,这使得大部分的线程上下文切换都发生在用户空间,而多个内核线程又能够充分利用处理器资源,模型图以下:
内核用户空间线程模型
最近在作jvm线程实现相关研究,发现java线程的问题实际上是基于C++和pthread线程库的问题,往下深刻,也就是操做系统对于线程实现的问题,这篇文章记录了我对操做系统线程技术的认识和理解。