进程的引入,从CPU开始
管理CPU,先要使用CPU,那么CPU上电之后发生了什么?
工作原理 : 自动的取址执行
从PC处获取指令的地址,然后取出指令并且执行
现在的指令执行是顺序执行,如果遇到需要与外界设备交互的指令,那么就会等待较长的时间,CPU资源明显浪费,怎么提高效率?
解决 : 在需要外界设备工作时,通知外界设备工作,然后执行其他程序,等待外界设备工作完成之后,再执行本程序
一个CPU上交替执行多个程序: 并发
多个CPU同时执行多个程序 : 并行
切换程序之后,需要切回去时怎么办?
引入PCB(Process Control Block) :每一个程序的PCB中存放PC地址,来源程序地址和去向程序地址,保存程序的信息
引入进程
进程 :执行中的程序或进入内存的程序、运行中的程序
进程有开始,有结束,程序没有
进程会走走停停,走停对程序无意义
进程需要记录,ax(来源进程),bx(去向进程),pc ,程序不用
多进程图像
1 如何使用CPU?
让程序执行起来
2 如何利用CPU
启动多个程序,交替执行
启动了的程序就是进程,所以是多个进程推进
操作系统只需要把这些进程记录好、按照合理的次序推进(分配资源、进程调度)
多进程如何组织?
PCB关联成队列,依次或关联执行
PCB + 状态 + 队列
多进程如何交替?
pNew = getNext(ReadyQueue) //从就绪队列中获取下一个进程(算法调度)
switch_to(pCur,pNew) //切换进程的PCB,达到切换进程的效果
队列操作+调度+切换
调度策略
1 FIFO
FIFO是公平的策略
FIFO没有考虑进程执行的任务的区别
2 Priority
优先级的设定条件 , 可能产生饥饿进程
多进程如何影响?
多进程同时存在内存中时,会出现下面的问题,一个物理内存地址,会被多个进程引用
每个进程都有独有的内部映射表,在程序中相同的内存地址通过映射之后,会分配到不同的物理地址
用户级线程
多进程是操作系统的基本图像
代码 : 进入内存的程序语句
栈 : 保存PC的数据结构(保存下一个指令的地址)
PCB : 进程切换的信息(对应的PC , ax(来源PCB信息) , bx(去向PCB信息))
现场 : 用于保留一个进程在运行时存放在处理器现场中的各种信息,任何一个进程在让出处理器时必须把此时的处理器现场信息保存到进程控制块中,而当该进程重新恢复运行时也应恢复处理器现场。常用的现场信息包括通用寄存器的内容、控制寄存器(如PSW寄存器)的内容、用户堆栈指针、系统堆栈指针等
映射表 : 内存映射
进程和线程实质
进程 = 资源 + 指令执行序列
多指令执行序列 + 一个地址空间(一个资源)
一个网页浏览器就是一个进程 , 进程是一系列指令执行序列,线程是一系列指令执行序列中的具有特殊作用的指令执行序列
线程切换
TCB : 保存线程的来源TCB信息、去向TCB信息、对应的PC
栈 : 保存PC的数据结构(下一个执行指令的地址)
用户级线程
Yield是用户级线程
用户级线程只能在用户态进程切换 (一对多的关系)
如果进入内核的线程阻塞,用户态进程无法感知内核进程状态,所以用户线程无法进行切换,只能内核态切换进程,切换进程之后,CPU资源就不在当前进程了
核心级线程
核心级线程可以在核心态切换 (一对一的关系)
如果用户级线程进入内核态之后承接的是核心级线程,那么核心线程之间可以进行切换,并且不会切换进程导致丢失CPU资源
切换五段论
1 内核栈关联用户栈
2 内核栈关联TCB1
3 切换TCB
4 TCB关联到内核栈
5 内核栈关联到用户栈
CPU调度策略
FIFO
先入先出、简单有效,但是CPU利用率不高
Priority(优先级)
优先级标准选取 : 任务时间短,重要性
调度策略的选取原则
尽快结束任务 : 周转时间短(从任务开始到任务结束)
用户操作尽快响应 : 响应时间短(从操作发生到响应)
系统内耗时间少 : 吞吐量(完成的任务量)
总原则 :系统专注于任务执行,又能合理调配任务
选取原则之间的矛盾性
响应时间小 =》切换次数多 =》系统内耗大 =》吞吐量小
前台任务关注于响应时间,后台任务关注于周转时间
调度算法
FCFS(First Come , First Served)
按顺序先到先得
SJF : 短作业优先
周转时间短的优先
时间片轮转调度
每一个任务都执行相同的时间片之后切出去
时间片轮转+优先级调度
c(t) = c(t-1)/2 + p t : 阻塞次数
counter : 表示时间片 , 也表示优先级
每一个进程都进行时间片调度,如果某一个进程阻塞,此进程的时间片就会累加获得更高的优先级,当CPU资源空闲时,优先级更高的进程也会更容易获得CPU资源,不会出现饥饿进程
进程同步和信号量
进程合作:多进程共同完成一个任务
信号量是在信号的基础上增加了一些参数,信号量是记录了一些信息(量),并且根据信息决定睡眠还是唤醒
信号量的定义举例
class Sigle{
int value ; //记录资源个数
Thread[] arr; //记录等待在该信号量量上的进程
}
Sigle s = new sigle();
p.product(s)
p.consume(s)
信号量临界区保护
信号量作用 : 通过对信号量的访问和修改,让多个进程有序推进
竞争条件
概念 : 和调度有关的共享数据语义错误,由多个进程对并发操作的共享数据引起
P1的修改共享数据的操作还没有全部完成,执行进程就切换到P2执行了,导致共享数据混乱、
解决竞争条件的办法
临界区
概念 : 一次只允许一个进程进入的该进程的一段代码
临界区代码的保护原则
基本原则 : 互斥进入,如果一个进程在临界区中执行,则其他进程不允许进入
好的临界区原则
1 有空让进 : 若干进程要求进入空闲临界区时,应尽快使一个进程进入
2 有限等待 : 从进程发出进入请求到允许进入,不能无限等待
解决算法实例
开关中断
因为进程的切换是由于中断,可以在临界区的前后关中断和开中断
多核CPU不好使 , 为什么?
因为每一个CPU都对应一个INTR(中断寄存器),对一个CPU的中断关闭之后,确实无法切换出去了,但是别的CPU的中断没有关闭,中断之后可以切进来
硬件原子指令
锁的本质还是一个信号量,用来标志是否可进入
只需要对标志位(资源)的修改的多条指令做成一条指令,就不会出现标志位修改混乱
使用硬件来做,将标志位(资源)的多条指令用硬件做成一条指令
死锁处理
我们将这种多个进程由于互相等待对方持有的资源而造成的谁也无法执行的情况叫死锁
死锁的成因
1 互斥使用
2 不可抢占
3 请求和保持,进程必须占有资源,再去申请
4 循环等待,在资源分配图中存在环路
死锁处理的方法
1 死锁预防 : 破坏死锁出现的条件
2 死锁避免 : 检测每一个资源请求,如果造成死锁就拒绝(银行家算法)
3 死锁检测 + 恢复 : 检测死锁出现时,让一些进程回滚,让出资源
4 死锁忽略 : 忽略死锁(一般的PC机就是)