Linux 的线程模型
自 Linux 诞生以来有三种
- LinuxThreads, 最早的模型, 只部分实现 POSIX Threads 标准
- NGPT, Next Generation POSIX Threads, 已终止
- NPTL, Native POSIX Thread Library, 从 2.6 内核以来到现在所使用的模型
在 NPTL 中, Linux 使用的用户线程与内核线程 1:1 内应的线程模型, 这也是众多其它系统, win32, Solaris, NetBSD, FreeBSD, macOS, iOS 等所采用的模型, 1:1 模型的实现相对 N:1 和 M:N 要简单些.
当调用 pthread 库创建的一个线程时, pthread_create
会调用 clone
系统调用, 最终会创建一个 LWP(轻量级进程) 和一个内核线程. NPTL 的 1:1 模型,即 1 个轻量级线程对应 1 个内核线程. 此时该 LWP 在内核态有对应的 task_struct(进程描述符), 是一个独立的线程调度单元
Linux 的进程与线程
对于系统而言并不区分进程与线程, 创建进程与创建线程都是使用 fork
系统调用, 只是传参上的区别, 比如子进程会有自己的独立地址空间, 而线程则会共享空间. 内核进行调度的单位是 task
.
可认为 Linux 里的线程本质上是共享资源的一系列进程.
LWP/ 用户线程 / 内核线程
LWP, Light-weight process, 即轻量级线程, 本质上来讲一种由内核支持的用户线程.
内核线程 , 只能访问内核空间, 共享相同的内核页表, 所有内核线程的内核空间是相同的, 而用户线程则是拥有自己的地址空间(与非同一进程的其它线程的空间不同).
用户线程, 标准意义上的用户线程应当是完全建立在用户空间的线程,用户线程的创建, 同步, 销毁, 调度完全在用户空间完成, 不需要内核的协助. 这个定义与 LWP 存在一定的交叉.
上下文切换
被抢占的 LWP, 需要在用户态保存当前寄存器, 然后切换到内核态, 保存对应内核线程的寄存器, 恢复调度时, 分别恢复这两个现场, 涉及至少两次的栈切换和一定程度的缓存失效.
Green threads
绿色线程, 由运行时库或虚拟机进行调度的 ” 线程 ”, 完全在用户空间进行管理.
Jdk1.2 之前, 在 Solaris 等平台上实现的线程模型, 类型上属于绿色线程. 早期的 Linux 线程模型也属于绿色线程.
另外常见属于此类型的实现主要是协程, 像 Java 中的 Quasar, Python 中的 Greenlet 等.
Coroutine
协程, 是为解决非抢占式多任务处理的泛化子例程, 本质上是一种程序控制机制. 协程可以有多个入口点,可以在指定位置挂起和恢复执行.
协程分为两类
- 非对称式协程, 协程间有调用链关系, 有两种传递程序控制权的方式(resume/yield), 一个非对称协程可看做从属于它的调用者
- 对称式协程, 只有一种传递程序控制权的操作(coroutine.transfer)
Fiber
基本上可认为是协程的一种实现
Java 创建的线程是什么线程
Java 1.2 以前有些是 Green threads.
1.2 以后, 以 Linux 平台为例, 线程的创建最终是调用到pthread_create
, 是内核级线程