进程和线程
进程具有两个相互独立的特点:
- 资源所有权:进程包括存放进程映像的虚拟地址空间,具有对资源的控制权。
- 调度/执行:进程具有运行状态和优先级,是可被 OS 调度和分派的实体。
通常将分派的单位称为线程(thread)或者轻量级进程(lightweight process),将拥有资源所有权的单位称为进程(process)或者任务(task)。
多线程是指 OS 在单个进程内支持多个并发执行路径的能力。
在多线程环境中,进程定义为资源分配单元和保护单元,和进程相关的有:
- 容纳进程映像的虚拟地址空间。
- 对处理器、其他进程(进程间通信)、文件和 I/O 资源的受保护访问。
一个进程可能有一或多个线程,和线程相关的有:
- 执行状态。
- 未运行时已保存的线程上下文。
- 执行栈。
- 用于局部变量的静态存储空间。
- 与进程内其他线程共享的内存和资源访问。
线程的优点:
- 在已有进程中创建新线程花费的时间远少于创建一个新进程。
- 终止线程花费的时间少于终止进程。
- 同一进程内线程之间的切换花费的时间少于进程间的切换。
- 提高了执行程序间的通信效率,线程间通信不需要调用内核。
在多处理器环境中,同一进程中的多个线程可以同时在不同处理器上运行。
支持线程的 OS 中,调度和分派是在线程基础上完成的,大多数与执行相关的信息可以保存在线程级数据结构中。当进程的某些状态改变时,进程中的所有线程的状态都会改变。比如进程终止时,所有线程也会终止。
线程状态:运行态、就绪态和阻塞态。
线程状态改变的操作:
- 派生:一般情况下,当派生一个新进程时,同时也会为该进程派生一个线程。
- 阻塞:等待一个事件时。
- 解除阻塞:等待的事件发生。
- 结束:线程执行完成。
线程同步:同一个进程中的所有线程共享地址空间和其他资源。一个线程对资源的修改会影响到其他线程的环境,为了防止它们产生干扰和破坏数据结构,需要同步各线程的活动。
线程分类
用户级线程:管理线程的所有工作由应用程序完成,内核不知道线程的存在。
任何应用程序都可以使用线程库设计成多线程程序。线程库是一个和线程操作相关的例程库。
当进程处于运行态时,进程中的某一个线程正在运行。此时,若线程发出了阻塞的系统调用,则进程状态将转为阻塞态,而运行中的线程状态不变,仍然是运行态;若发生时钟中断,进程状态转为就绪态,运行态的线程状态不变;若运行中的线程需要同一进程中的另一个线程执行,则该线程转为阻塞态,另一个线程转为运行态。
用户级线程优点:
- 线程切换不需要转换到内核模式,节省了模式转换的开销。
- 调度算法可以根据应用程序的不同而在应用程序中设计,不干扰 OS 底层的调度策略。
- 可以在任何 OS 中运行。
缺点:
- 系统调用会阻塞进程,即阻塞进程中所有线程。
- 多线程程序不能应用多处理技术,每次只有一个进程被分配给处理器,每个进程每次只有一个线程在运行。
解决线程阻塞问题的方法
使用称为“套管”(jacketing)的技术,目标是将产生阻塞的系统调用转化为非阻塞的系统调用。
内核级线程:内核为进程和进程中每个线程维护上下文信息。调度由内核基于线程完成。
优点:
- 可以同时把一个进程中的多个线程调度到多个处理器上执行。
- 进程中一个线程被阻塞时,可以调度同一进程中另外一个线程。
缺点:
在同一个进程内不同线程间传递处理器控制权时,需要切换到内核模式。
混合方式:线程创建完全在用户空间中完成,线程调度和同步在应用程序中进行。一个应用程序中的多个线程会被映射到一些(数量少于应用程序中的线程)内核级线程上。
进程和线程之间多对多的关系
当一个程序需要使用 I/O 子程序时,将主程序和子程序当作由单线程实现的单个活动,就是两者作为同一个线程实现,但是为主程序和子程序分别创建各自的地址空间,线程在执行过程中在这两个地址空间中移动。此时是一个线程对多个进程,也可以在同一地址空间中执行多个线程。
参考
[1] William Stallings, 操作系统——精髓与设计原理(8th), 2017.