TODO

实现

“受限直接执行”机制

  • 多个进程共享 CPU 需要解决的问题
    • 性能:不增加系统开销
    • 安全:系统应该可以接管
  • 实现
    • 用户模式和内核模式分离
    • 用户模式通过 Trap 指令陷入内核模式
    • 系统启动时注册 Trap 表,指定发生各种中断/系统调用时执行什么命令

并发与并行

  • 并发:一个核心上频繁切换不同任务
  • 并行:多个核心上同时执行不同任务

进程的状态

  • 挂起:可能由进程调度、Ctrl-Z (SIGTSTP) 或者内存过低导致,但是挂起状态的进程未必被换出到硬盘上!

PCB 进程控制块

  • 定义在 <include/linux/sched.h>
  • 组成(还需学习:task_struct 中的具体内容
    • 进程状态(RUNNING/STOPPED/ZOMBIE 等)
    • 进程标识符 PID
    • 进程内核栈(不是用户空间栈,包括用户空间栈指针,系统调用参数等)
    • 进程调度信息(动态优先级,调度策略等)
    • Program Counter
    • CPU 寄存器
    • 内存管理信息(页表信息)什么是页表信息?
    • 已执行的时间,时间限制等
    • 进程相关的 IO 设备
  • PCB 通过链表组织起来
    • 就绪状态的进程链在一起,叫做就绪队列
    • 等待状态的进程链在一起,叫做阻塞队列

进程控制

  • 创建进程
    • 写入 PCB 信息
    • 分配资源
    • 插入就绪队列
  • 终止进程
    • 找到 PCB
    • 停止运行
    • 将子进程交给 1 号进程
    • 回收资源
    • 从队列(不管是哪个)中删除
  • 阻塞进程
    • 找到 PCB
    • 状态改为阻塞
    • 插入阻塞队列
  • 唤醒进程
    • 找到 PCB
    • 状态改为就绪
    • 插入就绪队列

线程

  • 解决的问题
    • 进程上下文切换开销较大
    • 进程之间通信开销较大(怎么通信?)
  • 同一个进程的不同线程之间共享地址空间和文件,但是寄存器和栈是独立的
  • 线程与进程的比较(可能是常见八股)
    • 进程是资源分配的单位,线程是 CPU 调度的单位
    • 进程的所有资源都是独享的,但线程只独享必需的资源(寄存器和栈)
    • 线程和进程都有不同状态(就绪、执行、阻塞)和状态之间的转换
    • 线程可以减少并发执行的时空开销(resource + overhead)
      • 线程的创建比进程快,因为很多东西(页表,文件)都不需要分配,不需要加载程序
      • 线程的终止比进程快,因为需要释放的资源少(同上一点)
      • 同一个进程的线程之间切换比进程之间切换快,因为不需要切换页表
      • ITC 比 IPC 快(例如,同步操作不需要经过内核)

线程实现

  • 用户线程
    • 一个内核线程对应多个用户线程
    • 线程的管理和调度在用户态完成,内核不直接参与
    • 优点
      • TCB 由用户态线程库维护,就算内核不支持线程,也可以实现线程
      • 用户线程的切换不经过内核,速度快
    • 缺点
      • 如果某个线程发起系统调用,则会阻塞所有线程
      • 线程无法打断正在执行的线程,只能等待它自己交出 CPU 使用权
      • 时间片是按进程为单位分配的,这样每个线程得到的运行时间就更少,少中少
  • 内核线程
    • 一个内核线程对应一个用户线程
    • 线程的管理和调度完全由内核完成
    • 优点
      • 如果某个线程发起系统调用,并不会阻塞其他线程
      • 多线程的进程得到的运行时间较多
    • 缺点
      • 用户线程的切换必须经过内核,速度慢
  • 轻量级进程
    • 多个内核线程对应多个用户线程
    • 结合用户线程和内核线程的优点

调度

调度的原因

  • 上下文切换的原因
    • 多任务:在多个进程之间切换
    • 中断处理
    • 用户和内核态之间切换

IMPORTANT

当出现高优先级进程时,打断当前进程执行的行为依然需要依靠中断,例如 Round Robin 策略下通过时钟中断实现。

调度时机

  • 调度算法的分类
    • 非抢占式:让一个进程一直运行到阻塞/退出,才进行调度
    • 抢占式:让一个进程运行一段时间,然后挂起,然后进行调度。通过时钟中断实现
  • 每次进程的运行状态发生改变时,都会触发调度

调度原则

  • CPU 利用率:越高越好
  • 系统吞吐量:单位时间内 CPU 完成进程的数量,越高越好
  • 周转时间:运行时间 + 阻塞时间 + 等待时间,越小越好
  • 等待时间:就绪时间,越小越好
  • 响应时间:从用户提交请求到系统产生第一次响应的时间(?)

调度算法

  • FCFS 先来先服务:非抢占式,最简单,字面意思
  • SJF 最短作业有限:非抢占式,最简单,字面意思
  • HRRN 高响应比优先:非抢占式,优先执行 最大的进程
  • RR 时间片轮转:抢占式,每个进程执行时间为固定的时间片,应当权衡设置的时间片大小
  • HPF 最高优先级:抢占式或非抢占式,先执行优先级高的进程
  • MFQ 多级反馈队列
    • 不同的队列优先级从高到低,优先级越高时间片越短
    • 第一级队列中没执行完的放到第二级队列中
    • 永远优先执行高优先级队列中的进程

IPC

  • 管道
    • 读/写会阻塞
    • 虽然说具名管道是文件,但是也不存储在磁盘上,而是内核缓冲区
  • 消息队列
    • 不会阻塞,写完就行了,不用等着另一端读
    • 内核中的消息链表
  • 共享内存
    • mmap
    • 需要通过信号量实现同步/互斥
      • 信号量的值如果为负,则表示等待的线程个数
  • 信号
    • 例如 SIGINT、SIGTSTP、SIGKILL
    • 来源可以是硬件或软件
    • SIGKILL 和 SIGSTOP 无法忽略
  • 套接字 Socket
    • 协议族 AF_LOCAL 表示本机内部通信
    • UNIX domain socket 绑定到文件(例如“/tmp/mysocket.sock”)而不是 IP 地址

线程同步

  • 互斥:不能有两个线程操作同一个资源
  • 同步:不同功能的线程之间(例如生产者消费者)操作同一个资源时,需要有固定的先后顺序
    • 自旋锁:占满 CPU 不断查询是否无人占用锁
    • 无等待锁:没获取到锁时,把线程加入等待队列,当其他人释放锁时,唤醒一个正在等待的线程
  • 信号量
    • P 操作把信号量减 ,然后当信号量 时线程进入等待状态;
    • V 操作把信号量加 ,然后当信号量 时唤醒一个正在等待的线程(就绪状态)
    • 信号量可以实现互斥和同步(锁可不可以实现同步?)
      • 初值为 实现互斥(基本用法)
      • 两个初值为 的信号量,可以实现同步(等待对方,保证执行顺序)
  • 常见问题
    • 生产者-消费者模型,用两个信号量分别表示空缓冲区和满缓冲区的个数
    • 哲学家就餐问题
      • 用信号量表示每个哲学家的资源: 表示左右两人都不在吃东西, 表示至少有一人在吃东西,即当前哲学家不能吃东西
      • 每次有人放下餐具时,都会重新检查左右的人是否需要吃东西,如果是,则唤醒对应进程
    • 读写锁问题:可以同时有多个人读,或者一个人写
      • 读者会获取写信号量(互斥信号量),并且有新读者时会加入计数器,只有在计数器为 会释放写信号量
      • 写者会获取写信号量,同时获取“读者加入计数器”信号量(互斥信号量),阻止更多的读者加入计数器,直到当前写操作完成