线程

线程的概念

引入线程

  • 线程是进程的一部分,具有一段执行流
  • 线程在同一个进程的地址空间内,可以共享变量
  • 线程是CPU调度的基本单位

进程 VS 线程

进程:

  • 运行时:代码、寄存器、堆栈、数据段
  • 资源:地址空间、文件描述符、权限等

最简单的进程只有一个线程

地址空间

  • 进程之间不共享内存
  • 进程切换会切换页表
  • 进程中的线程共享整个地址空间

权限

  • 进程拥有自己的权限(如文件访问权限)
  • 进程中的线程共享所有的权限

过程 VS 线程

过程调用

  • s0/fp/指向栈底,sp指向最新的栈顶
  • 被调用者将函数参数压栈
  • 被调用者将局部变量压栈

image-20221020104530721

线程调用

  • 多线程并发执行
    • 多线程可以并行地在多个CPU上运行
    • 过程调用是顺序的
  • 线程可能会乱序地执行
    • 不能利用栈恢复过程
    • 每一个线程都有自己的栈
  • 线程不能频繁的切换
    • 每一个线程通常绑定一个CPU的核

线程并发性/并行性

  • 人们希望同时完成多件事情(服务器需要同时服务多个请求)
  • 计算交叠、I/O交叠(不同的线程使用不同的资源)
  • 多个线程共享内存,但会带来共享资源访问问题

线程表示与操作API

线程控制块(TCB)

  • 状态
    • 就绪态:准备运行
    • 运行态:正在运行
    • 阻塞态:等待资源
  • 寄存器
  • 程序计数器(PC)
  • 代码

典型的线程API

创建

createjoin

互斥

acquirerelease

条件变量

waitsignalbroadcast

警报

alertalertwaittestalert

线程上下文切换

  • 保存上下文
    • 所有的寄存器(通用寄存器和浮点寄存器)
    • 所有协处理器的状态
  • 开始新的上下文
    • 做保存上下文的逆操作
  • 可能触发进程的上下文切换
    • 单线程操作
    • 执行I/O操作

保存上下文

  • 在线程的栈上保存上下文
    • 可以利用处理器专用的指令进行高效的保存
    • 保存前需要确保栈上没有溢出的问题
  • TCB中保存上下文
    • 不存在溢出问题,但效率可能不是很高

线程模型

分类

  • 用户级线程
  • 内核级线程
  • 轻量级线程

分类依据(核内调度 VS 核外调度)

  • 核外调度:减少上下文切换开销
  • 核内调度:充分利用SMP结构

用户级线程(协程)

由一组用户级的线程库函数来完成线程的管理, 包括线程的创建、终止、同步和调度等

image-20221020104627285

内核级线程

直接由内核本身启动的工作线程,执行内核函数

特点

  • 在CPU特权级中运行
  • 访问内存的内核地址空间

轻量级线程(LWP)

特点

  • 共享某些资源的进程,例如地址空间、打开文件等资源
  • 实现内核支持的线程机制

image-20221020104706995

模式一

用户空间线程映射到一个LWP上(Linux)

image-20221020104727441

模式二

多个用户空间线程映射到多个LWP上(Solaris,Unix,System V)

image-20221020104752700

用户级线程 VS LWP

  • 用户级线程
    • 用户级线程库实现线程上下文切换
    • 时间中断会引入抢占
    • 当用户级线程被I/O事件阻塞时,整个进程都会被阻塞
  • LWP
    • LWP被内核调度器调度
    • 由于跨越了用户态和内核态,LWP的上下文切换开销大于用户级线程

image-20221020104822873

不同映射关系的对比

  • 一对一:每一个线程都拥有自己的内核栈
  • 多对一:一个进程的所有线程共享同一个内核栈
  • 多对多:多个线程共享一个内核栈
一对一
私有的内核栈
多对多
共享的内核栈
多对一
共享的内核栈
内存消耗
系统服务 并发 部分并发 串行访问
多处理器 部分利用 无法利用
内核复杂性