操作系统
本讲主要介绍操作系统的相关知识,帮助大家快速建立操作系统的宏观认识。需要注意的是,操作系统涉及处理机管理、存储管理、文件系统、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)是指系统中多个进程无限制地等待永远不会发生的条件。
死锁发生的必要条件:
- 互斥
- 请求和保持
- 非剥夺
- 环路等待
处理死锁问题的四种方法:
- 鸵鸟算法
- 死锁预防
- 预先静态分配法
- 有序资源使用法
- 死锁检测
- 资源分配图算法
- 死锁避免
- 银行家算法