Linux操作系统的体系架构分为用户态和内核态(或者用户空间和内核)。
内核从本质上看是一种软件——控制计算机的硬件资源,并提供上层应用程序运行的环境。用户态即上层应用程序的活动空间,应用程序的执行必须依托于内核提供的资源,包括CPU资源、存储资源、I/O资源等。
注:对操作系统来说,用户态线程具有不可见性,也称透明性。
用户态的应用程序可以通过三种方式来访问内核态的资源:
在 CPU 的所有指令中,有些指令是非常危险的,如果错用,将导致系统崩溃,比如清内存、设置时钟等。如果允许所有的程序都可以使用这些指令,那么系统崩溃的概率将大大增加。
所以,CPU将指令分为特权指令和非特权指令,对于那些危险的指令,只允许操作系统及其相关模块使用,普通应用程序只能使用那些不会造成灾难的指令。
比如Intel的CPU将特权等级分为4个级别:Ring0~Ring3;Linux 系统只使用了Ring0和Ring3两个运行级别。
当进程运行在Ring3级别时被称为运行在用户态,而运行在 Ring0 级别时被称为运行在内核态。
很多程序开始时运行于用户态,但在执行的过程中,一些操作需要在内核权限下才能执行,这就涉及到一个从用户态切换到内核态的过程。
注意:系统调用的本质其实也是中断,相对于外围设备的硬中断,这种中断称为软中断,这是操作系统为用户特别开放的一种中断。所以,从触发方式和效果上来看,这三种切换方式是完全一样的,都相当于是执行了一个中断响应的过程。但是从触发的对象来看,系统调用是进程主动请求切换的,而异常和硬中断则是被动的。
理解了这里,推荐看下《NIO效率高的原理之零拷贝与直接内存映射》中有关NIO零拷贝优化的知识。
用户线程一般不会直接去使用内核线程,而是去使用内核线程的一种高级接口——轻量级进程(Light Weight Process,LWP)。因此对于用户线程来说,用户程序必须让它的调度器采用用户线程,然后在内核线程上运行它。
用户线程与内核线程的映射关系有三种模型:一对一模型、多对一模型、多对多模型。
有了内核线程,每个用户线程被映射到一个内核线程。用户线程在其生命期内都会映射到该内核线程。一旦用户线程终止,两个线程都将离开系统。这被称作"一对一"线程映射。
缺点:
多对一模型将多个用户线程映射到一个内核线程上,线程之间的切换由用户态的代码来进行,因此相对一对一模型,多对一模型的线程切换速度要快许多。此外,多对一模型对用户线程的数量几乎无限制。
缺点:
多对多模型(又称为M对N模型)结合了一对一模型和多对一模型的优点,将多个用户线程映射到多个内核线程上。
优点:
java线程在jdk1.2之前,是基于名为“绿色线程”的用户线程实现的,这导致绿色线程只能同主线程共享CPU分片,从而无法利用多核CPU的优势。
由于绿色线程和原生线程比起来在使用时有一些限制, jdk1.2中放弃绿色线程,转而使用原生线程。
在目前的jdk版本中,操作系统支持怎样的线程模型,很大程度上决定了java虚拟机的线程是怎样映射的,这点在不同的平台上都没有办法达成一致。
总的来说就是,虚拟机规范中并没有限定java线程需要使用哪种线程模型,要根据不同的平台来说,但是无论使用哪种线程模型,java程序的编码和运行都是没有差异的。
例如,Java SE最常用的JVM是Oracle/Sun研发的HotSpot VM。在这个JVM所支持的所有平台上都是采用一对一的线程模型的,除了Solaris平台。
参考文档: