线程
线程是操作系统调度的最小单位,一般分为两类,内核线程和用户线程。
内核线程
内核线程是由内核负责调度的线程,只能运行在内核态。
用户线程
用户线程是由用户程序负责调度,内核无法感知,只能运行在用户态,只能访问用户空间。
内核态和用户态
cpu为了实现保护机制设立的权限等级,常用的intel有4个等级,但liunx系统只用到了2个。内核态下cpu可以访问所有设备和数据。用户态下只能访问内存。
线程的实现方式
1对1,内核线程和用户线程1对1映射,调度由系统负责。
1对多,内核线程和用户线程1对多映射,一个内核线程下的用户线程由用户维护。
多对多,多个内核线程与多个用户线程映射.
lwp,因为1对1模式开销大,如果不一一对应,系统又无法感知用户线程,所以又提供了轻量级进程的方案。lwp相对于内核线程更轻量,且能被系统感知,linux2.0版本后支持了lwp。
线程的实现
LinuxThreads,是linux针对线程的早期实现版本,linuxthreads就是通过lwp方式实现的pthread库。但linux不完全符合posix规范,且有诸多问题,所以在2.4版本后被nptl替换。
NPTL,Native POSIX Thread Library,nptl是2.4以后版本linux的线程库。
抢占式和非抢占式
非抢占式内核:由消息队列管理顺序执行,直到任务主动让出内核资源,才能切换下一个任务。
抢占式内核:由系统时钟管理,时间片结束后,由最高优先级任务抢占内核资源。
非抢占式相比抢占式内核,优点是共享资源竞争小,天然支持类似不可重入函数,缺点是响应慢,依赖任务主动释放资源。
linux到2.6版本才升级为抢占式内核。
进程调度队列
运行队列
每个在运行中的进程都会被加入到运行队列。
就绪队列
就绪队列是一组队列,根据进程优先级分组,运行的进程让出cpu后,内核会从优先级最高的就绪队列中找出一个进程运行,并从就绪队列移除,加入运行队列,刚才让出cpu的队列重新加会就绪队列。
等待队列
需要等待依赖的外部资源完成时的进程,会被加入到等待队列,主动让出资源sleep、wait也会被加入到等待队列。等待结束后进入就绪队列等待调度。
管程
管程是由程序自己实现的维护线程调度的管理器,主要有三种模式。
hoare
由运行中的线程指定一个线程唤醒,被唤醒线程立即抢占cpu资源。
hasen
由运行中的线程指定一个线程唤醒,被唤醒的线程等待当前线程让出cpu后,优先于其它线程执行。
mesa
由运行中的线程唤醒的线程被加入到准备队列,等当前线程让出资源后,准备队列里的线程竞争。这是java采用的模式。
java对多线程的实现
java在早期也尝试过1对多的线程实现方式,后来因为各种问题放弃了,现在oracle jdk采用1对1的模式,有的版本也支持1对多映射lwp。