目录
进程与线程
进程的概念
概念
进程的组成
PCB
程序段、数据段
进程的特征
总结
进程的状态与转换
进程的状态
创建态、就绪态
运行态
阻塞态
终止态
进程的转换
进程的组织
链接方式
索引方式
总结
进程控制
什么是进程控制
如何实现进程控制
进程控制相关的原语
进程的创建
进程的终止
进程的阻塞和唤醒
进程的切换
拓展:寄存器的组成
进程通信
什么是进程通信?
为什么进程通信需要操作系统支持
共享存储
消息传递
直接通信方式 间接通信方式
管道通信
总结
线程的概念和多线程模型
什么是线程
引入线程机制后,有什么变化?
线程的属性
线程的实现方式
用户级线程
内核级线程
多线程模型
总结
调度
基本概念
调度的三个层次
高级调度(作业调度)
低级调度(进程调度/处理机调度)
中级调度(内存调度)
拓展:进程的挂起态与七状态模型
总结
进程调度的时机切换与过程调度方式
进程调度的时机
调度方式
进程的切换与过程
总结
调度器/调度程序 (scheduler)
调度算法的评价指标
调度算法
先来先服务(FCFS)
最短作业优先(SJF)
最高响应比优先(HRRN)
时间片轮转(RR)
优先级调度
多级反馈队列
进程同步进程互斥
同步互斥概念
什么是进程同步
什么是进程互斥
总结
进程互斥的软件实现方法
单标志法
双标志先检查法
双标志后检查法
Peterson 算法
进程互斥的硬件实现方法
中断屏蔽法
TestAndSet指令(TSL)
XCHG(Exchange)指令:
互斥锁
信号量机制
整形信号量
记录型信号量
总结
信号量机制实现进程互斥、同步、前驱关系
实现进程互斥
实现进程同步、前驱关系
实现前驱关系
总结
生产者消费者问题
多生产者-多消费者
吸烟者问题
读者-写者问题
管程
管程的概念
管程的定义(一个类)
基本特征
死锁
死锁的概念
必要条件
死锁、饥饿、死循环的区别
死锁的处理策略
静态策略——预防死锁
破坏互斥条件
破坏不剥夺条件
破坏请求和保持条件
破坏循环等待条件
动态策略——避免死锁
安全序列
银行家算法
死锁的检测和解除
死锁检测
解除死锁
进程与线程
进程的概念
概念
- 引入多道程序技术之后,为了方便操作系统管理,完成程序的并发执行,引入了进程,进程实体的概念
-
内存中的PCB,程序段,数据段三部分构成了进程实体(进程印象),一般来说进程实体可以简称为进程
-
严格来说,进程实体和进程是不一样的,进程是动态的,是程序的一次执行过程,是系统进行资源分配和调度的独立单位;进程实体是静态的。
-
同一个程序多次执行会对应多个进程
进程的组成
PCB
程序段、数据段
程序段、数据段是给进程自己用的,与进程自身的运行逻辑有关
进程的特征
总结
进程的状态与转换
进程的状态
创建态、就绪态
运行态
阻塞态
终止态
进程的转换
进程的组织
链接方式
索引方式
总结
进程控制
什么是进程控制
进程控制的主要功能是对系统中的所有进程实施有效的管理,它具有创建新进程、撤销已有进程、实现进程状态转换等功能。
如何实现进程控制
- 用“原语”实现
- 原语的执行具有原子性,即执行过程只能一气呵成,期间不允许被中断。
- 原语可以用“关中断指令”和“开中断指令”这两个特权指令实现原子性
进程控制相关的原语
- 进程控制会导致进程状态的转换。无论哪个原语,要做的无非是三件事情:
- 更新PCB中的信息(如修改进程状态标志,将运行环境保存到PCB,从PCB恢复运行环境)
- 所有进程控制原语一定都会修改进程状态标志
- 剥夺当前运行进程的CPU使用权必然需要保存其运行环境
- 某进程开始运行前必然要恢复其运行环境
- 将PCB插入合适的队列
- 分配/回收资源
进程的创建
进程的终止
进程的阻塞和唤醒
进程的切换
拓展:寄存器的组成
在多线程编程中,线程之间的切换发生在同一个进程内,每个线程都有自己独立的程序计数器(Program Counter,PC)和栈帧(Stack Frame)。当线程切换时,当前线程的上下文信息,包括PC寄存器的值和栈帧的内容,会保存在线程的内部数据结构中,这些数据结构通常称为线程控制块(Thread Control Block,TCB)或线程上下文切换块(Thread Context Switch Block)。这些信息的保存是为了在切换回线程时能够继续执行。
而在进程管理中,进程之间的切换发生在不同的进程之间,每个进程都有自己独立的进程控制块(Process Control Block,PCB)。进程切换时,当前进程的上下文信息,包括程序计数器(PC)、寄存器状态、内存分配等,会保存在进程的 PCB 中。这些信息的保存是为了在切换回进程时能够继续执行。
进程通信
什么是进程通信?
- 进程通信就是指进程之间的信息交换
-
进程是分配系统资源的单位(包括内存地址空间),因此各进程拥有的内存地址空间相互独立。
为什么进程通信需要操作系统支持
为了保证安全,一个进程不能直接访问另一个进程的地址空间。
共享存储
- 两个进程对共享空间的访问必须是互斥的(互斥访问通过操作系统提供的工具实现)
-
操作系统只负责提供共享空间和同步互斥工具(如P,V操作)
-
基于数据结构的共享:比如共享空间里只能放一个长度为10的数组。这种共享方式速度慢、限制多,是一种低级通信方式
- 基于存储区的共享:在内存中画出一块共享存储区,数据的形式、存放位置都由进程控制,而不是操作系统。相比之下,这种共享方式速度更快,是一种高级通信方式。
-
基于数据结构的共享:比如共享空间里只能放一个长度为10的数组。这种共享方式速度慢、限制多,是一种低级通信方式
- 基于存储区的共享:在内存中画出一块共享存储区,数据的形式、存放位置都由进程控制,而不是操作系统。相比之下,这种共享方式速度更快,是一种高级通信方式。
消息传递
直接通信:信息直接挂到接收进程的信息缓存队列上
间接通信:消息要先发送到中间实体(信箱)中,因此也称“信箱通信方式”。
直接通信方式
间接通信方式
管道通信
- “管道“是指用于连接读写进程的一个共享文件,又名pipe文件。其实是在内存中开辟一个大小固定的缓冲区
-
管道只能采用半双工通信,某一个时间段内只能实现单向的传输。如果要实现双向同时通信,则需要设置两个管道
管道通信和直接通信的区别在于管道通信是基于队列的,他只能从队列头部开始拿数据,遵循先进先出,比如1到10个数据,我想获取7,只能也接收前6个数字才行;而直接通信就类似于一个池子一样,可以直接获取指定的数据,没有顺序要求
总结
线程的概念和多线程模型
什么是线程
可以把线程理解为“轻量级进程”
线程是一个基本的CPU执行单元,也是程序执行流的最小单位。
引入线程之后,不仅是进程之间可以并发,进程内的各线程之间也可以并发,从而进一步提升了系统的并发度,使得一个进程内也可以并发处理各种任务 (如QQ视频、文字聊天、传文件)
引入线程后,进程只作为除CPU之外的系统资源的分配单元 (如打印机、内存地址空间等都是分配给进程的)
引入线程机制后,有什么变化?
线程的属性
线程的实现方式
用户级线程
内核级线程
多线程模型
总结
调度
基本概念
调度的三个层次
高级调度(作业调度)
- 按一定的原则从外存的作业后备队列中挑选一个(或多个)作业调入内存,并创建进程。每个作业只调入一次,调出一次。作业调入时会建立PCB,调出时才撤销PCB。
低级调度(进程调度/处理机调度)
- 按照某种策略从就绪队列中选取一个进程,将处理机(CPU资源)分配给它。
- 进程调度是操作系统中最基本的一种调度,在一般的操作系统中都必须配置进程调度。
- 进程调度的频率很高,一般几十毫秒一次。
中级调度(内存调度)
- 暂时调到外存等待的进程状态为挂起状态。被挂起的进程PCB会被组织成挂起队列
-
PCB不会一起被调到外存,而是常驻内存
-
按照某种策略决定将哪个处于挂起状态的进程重新调入内存。
- 一个进程可能会被多次调出、调入内存,因此中级调度发生的频率要比高级调度更高。
拓展:进程的挂起态与七状态模型
总结
进程调度的时机切换与过程调度方式
进程调度的时机
普通临界区访问的临界资源不会直接影响操作系统内核的管理工作。因此在访问普通临界区时可以进行调度与切换
调度方式
-
非剥夺调度方式,又称非抢占方式。即,只允许进程主动放弃处理机。在运行过程中即便有更紧迫的任务到达,当前进程依然会继续使用处理机,直到该进程终止或主动要求进入阻塞态。(实现简单,系统开销小但是无法及时处理紧急任务,适合于早期的批处理系统)
-
剥夺调度方式,又称抢占方式。当一个进程正在处理机上执行时,如果有一个更重要或更紧迫的进程需要使用处理机,则立即暂停正在执行的进程,将处理机分配给更重要紧迫的那个进程。(可以优先处理更紧急的进程,也可实现让各进程按时间片轮流执行的功能 (通过时钟中断)。适合于分时操作系统、实时操作系统)
进程的切换与过程
总结
调度器/调度程序 (scheduler)
- 不支持内核级线程的操作系统,调度程序的处理对象是进程
- 支持内核级线程的操作系统,调度程序的处理对象是内核线程
调度算法的评价指标
调度算法
先来先服务(FCFS)
最短作业优先(SJF)
最高响应比优先(HRRN)
时间片轮转(RR)
优先级调度
多级反馈队列
对各类型进程相对公平(FCFS的优点);
-
每个新到达的进程都可以很快就得到响应(RR的优点);
-
短进程只用较少的时间就可完成(SPF的优点);
-
不必实现估计进程的运行时间(避免用户作假);
-
可灵活地调整对各类进程的偏好程度,比如CPU密集型进程、I/O密集型进程(拓展:可以将因I/O而阻塞的进程重新放回原队列,这样I/O型进程就可以保持较高优先级)
-
一般不说它有缺点,不过可能导致饥饿
进程同步进程互斥
同步互斥概念
什么是进程同步
什么是进程互斥
为了实现对临界资源的互斥访问,同时保证系统整体性能,需要遵循以下原则:
- 空闲让进。临界区空闲时,可以允许一个请求进入临界区的进程立即进入临界区;
- 忙则等待。当已有进程进入临界区时,其他试图进入临界区的进程必须等待;
- 有限等待。对请求访问的进程,应保证能在有限时间内进入临界区(保证不会饥饿);
- 让权等待。当进程不能进入临界区时,应立即释放处理机,防止进程忙等待。
总结
进程互斥的软件实现方法
让权等待(也称为自愿让权)是指在并发编程中,当一个进程(或线程)无法继续执行其关键部分时,主动将CPU资源让给其他进程去执行。这种等待方式是自愿的,因为进程主动选择放弃CPU执行时间,以便给其他进程更多的机
单标志法
- 算法思想:这种方法使用一个标志(flag)变量来表示是否允许访问临界资源。当一个进程想要进入临界区时,它首先检查标志变量的状态。如果标志为真,表示有其他进程正在访问临界资源,当前进程需要等待。如果标志为假,当前进程将标志置为真并进入临界区,完成后再将标志置为假,表示临界区空闲。
- 主要问题:有可能出现饥饿现象,即某些进程可能永远无法进入临界区。并且,如果忘记将标志置为假,可能导致死锁情况发生。
双标志先检查法
-
算法思想:这种方法使用两个标志变量,一个表示当前进程是否希望进入临界区(interested),另一个表示是否允许其他进程进入临界区(turn)。当一个进程想要进入临界区时,它首先将interested标志置为真,然后检查turn标志是否为自己,如果是则进入临界区,如果不是则等待。进程离开临界区后,将interested标志置为假,表示不再希望进入临界区。
-
优点:能够保证进程公平地访问临界区,避免了饥饿现象。
-
主要问题:可能存在死锁的可能性,特别是在多个进程同时希望进入临界区的情况下。此外,需要额外的开销来维护和检查两个标志变量的状态。
-
原因在于,进入区的“检查”和“上锁”两个处理不是一气呵成的。“检查”后,“上锁”前可能发生进程切换。
双标志后检查法
- 算法思想:与双标志先检查法类似,这种方法也使用两个标志变量,但检查顺序相反。当一个进程想要进入临界区时,它首先将turn标志置为自己,然后检查其他进程的interested标志。如果发现有其他进程也希望进入临界区,则进程等待。离开临界区后,进程将turn标志置为其他进程,并且修改interested标志。
- 优点:具备公平性,避免了饥饿现象。
- 主要问题:同样可能存在死锁的可能性。此外,需要维护和检查两个标志变量的状态,可能引入性能开销。
Peterson 算法
-
算法思想:这种算法由Peterson在1981年提出,用于两个进程之间的互斥。它使用两个标志变量(flag)和一个共享变量(turn)来实现。当一个进程想要进入临界区时,它首先将自己的标志置为真,并将turn标志设置为对方的进程号。然后,进程会等待对方的标志变量为假且轮到自己的进程号。最后,进程进入临界区,完成后离开。
-
优点:简单有效,适用于两个进程之间的互斥。
-
主要问题:无法扩展到多个进程的情况,只适用于两个进程之间的互斥。此外,有可能出现忙等待(busy waiting)的情况,浪费了CPU资源。
进程互斥的硬件实现方法
中断屏蔽法
-
简单解释:利用“开/关中断指令”实现(与原语的实现思想相同,即在某进程开始访问临界区到结束访问为止都不允许被中断,也就不能发生进程切换,因此也不可能发生两个同时访问临界区的情况)
- 优点:实现简单,在硬件层面上提供了对临界区的互斥保护。不需要复杂的软件代码,能够提供高效的互斥性能。
- 缺点:不适用于多处理机;只适用于操作系统内核进程,不适用于用户进程(因为开/关中断指令 只能运行在内核态,这组指令如果能让用户随意使用会很危险)
TestAndSet指令(TSL)
- 简单解释:TSL指令是用于实现原子操作的一种指令,可以实现对某个共享变量的读取和设置操作。当一个进程想要进入临界区时,它会使用TSL指令读取并设置一个特定的共享标志变量,如果该变量的值为1,表示有其他进程正在访问临界区,当前进程需要等待;如果该变量的值为0,表示临界区空闲,当前进程可以进入临界区。
- 优点:TSL指令能够在一个原子操作中实现读取和设置操作,从而提供了更高的互斥性能。它也能有效避免饥饿现象,并允许多个进程在临界区外自旋等待。
- 缺点:TSL指令通常需要硬件支持,不是所有的处理器都提供该指令。此外,在忙等待的情况下,会占用大量的CPU资源,效率可能不高。
XCHG(Exchange)指令:
- 简单解释:逻辑上来看 Swap 和 TSL 并无太大区别,都是先记录下此时临界区是否已经被上锁(记录在 old 变 量上),再将上锁标记 lock 设置为 true,最后检查 old,如果 old 为 false 则说明之前没有别的进程 对临界区上锁,则可跳出循环,进入临界区。
- 优点:XCHG指令提供了原子交换操作,保证共享变量的读取和设置操作是不可分割的。在多核系统中,XCHG指令通常具有较好的性能,能够提供高效的互斥保护。
- 缺点:和TSL指令类似,XCHG指令也可能需要特定的硬件支持。同时,在忙等待的情况下,也会占用大量的CPU资源。
互斥锁
互斥锁的主要缺点是忙等待,当有一个进程在临界区中,任何其他进程在进入临界区时必须连续循环调用 acquire()。当多个进程共享同一 CPU时,就浪费了CPU周期。因此,互斥锁通常用于多处理器系统,一个线程可以在一个处理器上等待,不影响其他线程的执行。
需要连续循环忙等的互斥锁,都可称为自旋锁 (spin ock) ,如TSL指令、swap指令、单标志法等
信号量机制
整形信号量
记录型信号量
总结
信号量机制实现进程互斥、同步、前驱关系
实现进程互斥
实现进程同步、前驱关系
实现前驱关系
总结
生产者消费者问题
- 生产者:先生产后互斥
- 消费者:先消费后互斥
思考:能否改变相邻P、V操作的顺序?
会导致:我要放东西通过了,但满了放不进去,去到消费者,但通道被生产者占住了,形成死锁
同步:查看缓存区容量和非空区
互斥:消费者和生产者不能同时使用缓存区
多生产者-多消费者
画图非常有用
吸烟者问题
读者-写者问题
写和读,写和写不能同时访问
但读和读可以同时访问
- “读优先”
第一个读者先加两把锁,都关上,再打开互斥锁,让其他读者进行访问
- ”写优先“相对公平
第一个读者先加三把锁,都关上,再打开互斥锁和写优先锁,且先打开写优先锁,使只要读者读完就给写
管程
管程的概念
- 信号量机制存在的问题:编写程序困难、易出错
管程的定义(一个类)
- 局部于管程的共享数据结构说明;
- 对该数据结构进行操作的一组过程;
- 对局部于管程的共享数据设置初始值的语句;
- 管程有一个名字。
基本特征
- 局部于管程的数据只能被局部于管程的过程所访问;(私有属性)
- 一个进程只有通过调用管程内的过程才能进入管程访问共享数据(set、get方法)
- 每次仅允许一个进程在管程内执行某个内部过程。(方法要上锁同步)
死锁
死锁的概念
在并发环境下,各进程因竞争资源而造成的一种互相等待对方手里的资源,导致各进程都阻塞,都无法向前推进的现象,就是“死锁”。发生死锁后若无外力干涉,这些进程都将无法向前推进。
必要条件
- 互斥条件:只有对必须互斥使用的资源的争抢才会导致死锁(如哲学家的筷子、打印机设备)。像内存、扬声器这样可以同时让多个进程使用的资源是不会导致死锁的(因为进程不用阻塞等待这种资源)。
- 不剥夺条件:进程所获得的资源在未使用完之前,不能由其他进程强行夺走,只能主动释放。
- 请求和保持条件:进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源又被其他进程占有,此时请求进程被阻塞,但又对自己已有的资源保持不放。
- 循环等待条件:存在一种进程资源的循环等待链,链中的每一个进程已获得的资源同时被下一个进程所请求。
死锁、饥饿、死循环的区别
死锁的处理策略
静态策略——预防死锁
破坏互斥条件
互斥条件:只有对必须互斥使用的资源的争抢才会导致死锁。
破坏不剥夺条件
不剥夺条件:进程所获得的资源在未使用完之前,不能由其他进程强行夺走,只能主动释放。
缺点:
- 实现起来比较复杂。
- 释放已获得的资源可能造成前一阶段工作的失效。因此这种方法一般只适用于易保存和恢复状态的资源,如CPU。
- 反复地申请和释放资源会增加系统开销,降低系统吞吐量。
- 若采用方案一,意味着只要暂时得不到某个资源,之前获得的那些资源就都需要放弃,以后再重新申请。如果一直发生这样的情况,就会导致进程饥饿。
破坏请求和保持条件
请求和保持条件:进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源又被其他进程占有,此时请求进程被阻塞,但又对自己已有的资源保持不放。
缺点:
- 有些资源可能只需要用很短的时间,因此如果进程的整个运行期间都一直保持着所有资源(不是用完一个就丢一个,是用完所有再一次性丢完),就会造成严重的资源浪费,资源利用率极低。
- 该策略也有可能导致某些进程饥饿。因为你得等多个元素同时空闲,这样子的时机很少
破坏循环等待条件
循环等待条件:存在一种进程资源的循环等待链,链中的每一个进程已获得的资源同时被下一个进程所请求。
可采用顺序资源分配法。首先给系统中的资源编号,规定每个进程必须按编号递增的顺序请求资源,同类资源(即编号相同的资源)一次申请完。
原理分析:一个进程只有已占有小编号的资源时,才有资格申请更大编号的资源。按此规则,已持有大编号资源的进程不可能逆向地回来申请小编号的资源,从而就不会产生循环等待的现象。
为什么这种规则可以破坏循环等待条件?
核心: 每个进程只能按递增顺序申请资源
因此每个时刻总有一个进程占据了较高序号的资源,那么它后面继续申请的资源一定是空闲的,这就保证了进程是可以一直向前推进的,举个例子:
该策略的缺点:
- 不方便增加新的设备,因为可能需要重新分配所有的编号;
- 进程实际使用资源的顺序可能和编号递增顺序不一致,会导致资源浪费;
- 必须按规定次序申请资源,用户编程麻烦。
动态策略——避免死锁
安全序列
银行家算法
死锁的检测和解除
死锁检测
死锁定理:如果某时刻系统的资源分配图是不可完全简化的,那么此时系统死锁
解除死锁
死锁的解除方法很简单,主要有几种方法:
- 资源剥夺法。将一些死锁进程暂时挂起来,并且抢占它的资源,并将这些资源分配给其他的死锁进程 ,要注意的是应该防止被挂起的进程长时间得不到资源而处于饥饿的状态
- 撤销进程法。强制撤销部分甚至全部死锁并剥夺这些进程的资源。撤销的原则可以按照进程优先级和撤销进程的代价高低进行。
- 进程回退法。让一或多个进程回退到足以回避死锁的地步,进程回退时自愿释放资源而非被剥夺。这个方法要求系统保持进程的历史信息,并设置还原点。