操作系统

本讲主要介绍操作系统的相关知识,帮助大家快速建立操作系统的宏观认识。需要注意的是,操作系统涉及处理机管理、存储管理、文件系统、IO设备等多个方面,而本讲主要涉及处理机管理中的进程间通信、进程的同步与互斥、死锁等内容,较少涉及其它部分。以下为本讲大纲,详细课件参见仓库

进程与线程

进程

进程(Process)是具有独立功能的程序在某个数据集合上的一次运行活动,可以理解为操作系统执行的一个程序的实例。

三状态模型:运行、就绪、阻塞。

线程

线程(Thread)进一步减小了程序并发执行的时空开销,是轻量级进程。进程是资源分配的单位,线程是处理器调度的单位,同一个进程的多个线程共享资源。

用户级线程 vs 内核级线程

处理器

处理器状态:核心态(Kernel mode)与用户态(User mode),通过陷入(Trap)与修改程序状态字(PSW)互相切换。

CPU的一个核心上,同一时间片只能执行一个线程。操作系统在单核上实现并行的方式是通过处理器调度,使得各线程轮流执行,实现宏观并行。操作系统的调度需要陷入核心态,保存寄存器、堆栈,同时cache需要更新,因此有一定时间开销。

调度的时刻(抢先vs非抢先):

  • 创建一个新进程后
  • 一个进程运行完毕后
  • 中断
  • 一个进程被阻塞
  • 时间片轮转

对于计算密集型任务,频繁调度不划算;而对于IO密集型任务,调度是有较大收益的。

原语(primitive):由若干条指令构成的 “原子操作(atomic operation)” 过程 ,作为一个整体而不可分割——要么全都完成 ,要么全都不做 要么全都不做 要么全都不做 。许多系统调用都是原语。原语可以避免因调度而被打断。

需要注意的是,C或其它编程语言的一条指令可能由多个汇编语句构成,而处理器执行汇编指令中都可能发生调度,因此同一条指令可能被打断,发生各种情况。

Linux / Windows / POSIX 中的进程与线程

Linux无线程概念,但可共享资源的进程可看作线程,相当于内核级线程。

Windows进程是惰性的,无实际作用,线程是调度单位,内核级线程。

POSIX未限定用户级/内核级线程。多线程编程接口标准pthread。

进程同步与互斥

  • 禁止中断

    进入临界区前执行关闭中断指令,离开临界区后执行开中断。

    简单但可靠性差,不适合多处理器。

  • 严格轮转法 进程严格轮流进入临界区。 忙等待,效率较低,且可能在临界区外阻塞别的进程。

  • Petersen算法 可以正常工作的解决互斥问题的算法,仍使用锁。 忙等待,效率较低。

  • 硬件指令方法 使用硬件提供不被打断的单条指令读写共享变量。 适用于任意数目进程,且较简单,但仍有忙等待。

  • 信号量 使用原语访问信号量管理资源。 不必忙等,效率较高,但信号量的控制分布在整个程序中,正确性难以保证。

  • 管程 信号量及其操作的高级语言封装。 效率同信号量一样,易于管理开发。

  • 消息传递 用以实现分布式系统的同步、互斥。 支持分布式系统,但消息传递本身效率较低且不完全可靠。

忙等待不但浪费CPU时间,还会出现优先级反转问题(priority inversion problem)。

进程间通信

Linux进程间通信

  • 信号
  • 管道
  • 消息队列
  • 共享内存
  • 套接字

Windows进程间通信

  • 管道
  • 共享内存
  • 邮件槽
  • 套接字

经典IPC问题

  • 生产者-消费者问题
  • 读者-写者问题
  • 睡眠理发师问题
  • 哲学家进餐问题

死锁

死锁(Deadlock)是指系统中多个进程无限制地等待永远不会发生的条件。

死锁发生的必要条件:

  • 互斥
  • 请求和保持
  • 非剥夺
  • 环路等待

处理死锁问题的四种方法:

  • 鸵鸟算法
  • 死锁预防
    • 预先静态分配法
    • 有序资源使用法
  • 死锁检测
    • 资源分配图算法
  • 死锁避免
    • 银行家算法