深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上鸿蒙开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
同步机制
- 空闲让进
- 忙则等待
- 有限等待:在有限时间内能进入自己的临界区,以免陷入“死等”状态
- 让权等待:进程不能进入临界区时应立即释放处理机
硬件同步机制
- 关中断
在进入锁测试之前关闭中断,直到完成锁测试并上锁之后才能打开中断。
- Test-and-Set指令实现互斥
boolean TS(boolean *lock)
{
boolean old;
old = *lock;
*lock = TRUE;
return old;
}
do{
…
while TS(&lock)
critical section;
lock = FALSE;
remainder section;
}while(TRUE)
- 利用Swap指令实现进程互斥
void swap(boolean *a, boolean *b)
{
boolean temp;
temp = *a;
*a = *b;
*b = temp;
}
do{
key = TRUE;
do{
swap(&lock,&key);
}while(key!=FALSE)
临界区操作;
lock = FALSE;
…
}while(TRUE)
信号量机制
- 整型信号量
wait(S)和signal(S)被分别称为P、V操作。
wait(S){
while(S<=0)
S–;
}
signal(S){
S++;
}
- 记录型信号量
代表资源数目的整型变量value,一个进程链表指针list,用于链接上述所有等待进程。
typedef struct{
int value;
struct process_control_block *list;
}
wait(semaphore *S)
{
S->value–;
if(S->value<0) block(S->list);
}
signal(semaphore *S)
{
S->value++;
if(S->value<=0) wakeup(S->list);
}
S->value的初值表示系统某类资源的数目,它的每次wait操作,意味着进程请求一个单位的该类资源,使系统中可供分配的该类资源数减少一个;当S->value<0时,表示该类资源已分配完毕,因此进程应调用block原语进行自我阻塞,放弃处理机,并插入到信号量链表S->list中。此时S->value的绝对值表示在该信号量链表中已阻塞的数目。
若S->value++后S->value≤0,则表示该信号量链表中仍有等待该资源的进程被阻塞,故调用wakeup原语,唤醒链表中的第一个等待进程。
- AND型信号量
将进程在整个运行过程中需要的所有资源,一次性全部分配给进程,待进程使用完后再一起释放,只要尚有一个资源未能分配给进程,其它所有可能为之分配的资源也不分配给它。
- 信号量集
进程对信号量Si是该资源的分配下限值ti,要求Si≥ti,否则不予分配。一旦允许分配,进程对该资源的需求值为di,即表示资源占用量,进行Si=Si-di操作。
特殊情况
- Swait(S,d,d):只有一个信号量S,允许每次申请d个资源,当少于d时,不予分配
- Swait(S,1,1):一般的记录型信号量(S>1)或互斥信号量(S=1)
- Swait(S,1,0):S≥1,允许多个进程进入某特定区;S=0,将阻止任何进程进入特定区
利用信号量实现进程互斥
两个信号量互斥
- 设mutex为互斥信号量,其初值为1,取值范围为(-1,0,1)。当mutex=1时,表示两个进程皆未进入需要互斥的临界区;当mutex=0时,表示有一个进程进入临界区运行,另外一个必须等待,挂入阻塞队列;当mutex=-1时,表示有一个进程正在临界区运行,另外一个进程因等待而阻塞在信号量队列中,需要被当前已在临界区运行的进程退出时唤醒。
- 代码描述
semaphore mutex=1;
PA(){
while(1){
wait(mutex);
临界区
signal(mutex);
剩余区
}
}
利用信号量实现前趋关系
Var a,b,c,d,e,f,g:semaphore: =0,0,0,0,0,0,0;
p1(){S1;signal(a);signal(b);}
p2(){wait(a);S2;signal©;signal(d);}
p3(){wait(b);S3;signal(e);}
p4(){wait©;S4;signal(f);}
p5(){wait(d);S5;signal(g);}
p6(){wait(e);wait(f);wait(g);S6;}
管程机制
代表共享资源的数据结构以及由对该共享数据结构实施操作的一组过程所组成的资源管理程序共同构成了一个操作系统的资源管理模块,称之为管程。
- 管程的名称
- 局部于管程的共享数据结构说明
- 对该数据结构进行操作的一组过程
- 对局部于管程的共享数据设置初始值的语句
管程和进程的不同
- 进程定义的是私有数据结构PCB,管程定义的是公共数据结构
- 进程是由顺序程序执行有关操作,管程进行同步操作和初始化操作
- 进程的设置目的在于实现系统的并发性,管程的设置目的则是解决共享资源互斥使用问题
- 进程为主动工作方式,管程为被动工作方式
- 进程具有动态性,管程是操作系统中的一个资源管理模块,供进程调用
2.5 经典进程的同步问题
设信号量→赋初值→编程(每一进程)
生产者-消费者问题
哲学家进餐问题
读者-写者问题
2.6 进程通信
低级进程通信(信号量)
- 效率低
- 通信对用户不透明
高级进程通信
- 使用方便
- 高效地传送大量数据
进程通信的类型
- 共享存储器系统
相互通信的进程共享某些数据结构或共享存储区,进程之间能够通过这些空间进行通信。
- 基于共享数据结构的通信方式
- 基于共享存储区的通信方式
- 管道通信系统
所谓“管道”,指用于连接一个读进程和一个写进程以实现它们之间通信的一个共享文件
- 消息传递系统
以格式化消息为单位,将通信的数据封装在消息中,并利用操作系统提供的一组通信命令,在进程间进行消息传递,完成进程间的数据转换
- 直接通信方式
发送进程利用OS所提供的发送原语,直接把消息发送给目标进程
- 间接通信方式
发送和接收进程,都通过共享中间实体的方式进行消息的发送和接收,完成进程间的通信。
- 客户机-服务器系统
- 套接字
- 基于文件型
- 基于网络型
- 远程过程调用
- 远程方法调用
消息传递通信的实现方式
- 直接消息传递系统
发送进程利用OS所提供的发送命令(原语),直接把消息发送给目标进程。
- 直接通信原语
- 对称寻址方式
send(receiver,message);
receive(sender,message);
- 非对称寻址方式
send(P,message);
receive(id,message);
2. 消息的格式
3. 进程的同步方式
4. 通信链路
- 信箱通信
- 信箱的结构
- 信箱头:用于存放有关信箱的描述信息
- 信箱体:由若干个可以存放消息(或消息头)的信箱格组成,信箱格的数目已经每格的大小是在创建信箱时确定的
- 信箱通信原语
- 邮箱的创建和撤销
- 消息的发送和接收
- 信箱的类型
- 私用邮箱
- 公用邮箱
- 共享邮箱
直接消息传递系统实例
2.7 线程的基本概念
引入线程是为了减少程序在并发执行时所付出的时空开销,使OS具有更好的并发性
线程与进程的比较
- 调度的基本单位
进程是能独立运行的基本单位,在每次被调度时,都需要进行上下文切换,开销较大;
线程切换仅需保存和设置少量寄存器内容,切换代价远低于进程
- 并发性
都可以并发执行
- 拥有资源
进程可以拥有资源,并作为系统中拥有资源的一个基本单位;
线程并不拥有系统资源,而是仅有一点必不可少的、能保证独立运行的资源
- 独立性
进程间地址空间和资源互相独立;
线程间共享地址空间和资源
- 系统开销
进程需要切换上下文;线程没什么资源,开销小
- 支持多处理机系统
进程必须在单处理机上运行;
单进程中的多线程可分配到不同处理机上执行
第三章 处理机调度与死锁
3.1 处理机调度的层次和调度算法的目标
处理机调度的层次
- 高级调度(作业调度)
调度对象是作业,主要用于多道批处理系统中。
- 低级调度(进程调度)
调度对象是进程,基本调度,在多道批处理、分时和实时三种类型的OS中,都必须配置这级调度。
- 中级调度(内存调度)
主要目的是提高内存利用率和系统吞吐量。
处理机调度算法的共同目标
- 资源利用率
C
P
U
的
利
用
率
=
C
P
U
有
效
工
作
时
间
C
P
U
有
效
工
作
时
间
C
P
U
空
闲
等
待
时
间
CPU的利用率=\frac{CPU有效工作时间}{CPU有效工作时间+CPU空闲等待时间}
CPU的利用率=CPU有效工作时间+CPU空闲等待时间CPU有效工作时间
- 公平性
诸进程都获得合理的CPU时间,不会发生进程饥饿现象。
- 平衡性
CPU都能经常处于忙碌状态;系统资源的平衡性。
- 策略强制执行
如安全策略。
批处理系统的目标
- 平均周期时间短
周转时间:从作业被提交给系统开始,到作业完成为止的这段时间间隔,包括作业在外存后备队列上等待调度的时间,进程在就绪队列上等待进程调度的时间,进程在CPU上执行的时间,以及进程等待I/O操作完成的时间。
平均周转时间最短,有效提高系统资源利用率,可使大部分用户满意。
平均周转时间:
T
=
1
n
[
∑
i
=
1
n
T
i
]
T=\frac{1}{n}[\sum_{i=1}^{n}{T_i}]
T=n1[i=1∑nTi]
平均带权周转时间:
W
=
1
n
∑
i
=
1
n
T
i
T
s
W=\frac{1}{n}\sum_{i=1}^{n}{\frac{T_i}{T_s}}
W=n1i=1∑nTsTi
作业周转时间
T
T
T,系统为其服务时间
T
s
T_s
Ts
- 系统吞吐量高
吞吐量:在单位时间内系统所完成的作业数
- 处理机利用率高
分时系统的目标
- 响应时间快
响应时间:从用户通过键盘提交一个请求开始,直到屏幕上显示出处理结果为止的一段时间间隔。
- 均衡性
均衡性:系统响应时间的快慢应与用户所请求服务的复杂性相适应。
实时系统的目标
- 截止时间的保证
截止时间:某任务必须开始执行的最迟时间,或必须完成的最迟时间
- 可预测性
3.2 作业与作业调度
作业:包含程序、数据、作业说明书。
作业步:完成作业的每一个加工步骤。
作业控制块JCB:是作业在系统中存在的标志。
作业运行的三个阶段和三种状态
- 收容阶段。 后备状态
- 运行阶段。 运行状态
- 完成阶段。 完成状态
作业调度的主要任务
- 接纳多少个作业(允许多少个作业同时在内存中运行)
- 接纳哪些作业(取决于所采用的调度算法)
先来先服务FCFS调度算法
作业调度√,进程调度√
系统按照作业到达的先后顺序来进行调度,优先考虑等待时间最长的作业。
有利于长作业(进程),不利于短作业(进程)
有利于CPU繁忙型作业,不利于I/O繁忙型作业
短作业优先SJF调度算法
作业调度√,进程调度√
以作业长短来计算优先级,作业越短,优先级越高。作业的长短是以作业所要求的运行时间来衡量的。
缺点:
- 必须预知作业的运行时间
- 对长作业非常不利
- 人-机无法交互
- 未考虑作业的紧迫程度
优先级调度PSA算法
作业调度√,进程调度√
基于作业的紧迫度,由外部赋予作业相应的优先级。
高响应比优先调度HRRN算法
进程都到之后开始计算优先权。
动态优先级
优
先
权
=
等
待
时
间
要
求
服
务
时
间
要
求
服
务
时
间
=
响
应
时
间
要
求
服
务
时
间
=
响
应
比
R
p
优先权=\frac{等待时间+要求服务时间}{要求服务时间}=\frac{响应时间}{要求服务时间}=响应比R_p
优先权=要求服务时间等待时间+要求服务时间=要求服务时间响应时间=响应比Rp
- 作业等待时间相同,服务时间越短,优先级越高
- 要求服务时间相同,等待时间越长,优先级越高
- 厂作业优先级可以随等待时间的增加而提高
缺点:每次调度都需计算
3.3 进程调度
进程调度的任务
- 保存处理机的现场信息
- 按某种算法选取进程
- 把处理器分配给进程
进程调度机制
- 排队器
- 分派器
- 上下文切换器
进程调度方式
- 非抢占方式:一旦分配给某进程,就一直让它运行下去。
- 正在执行的进程运行完毕,或因发生某时间而使其无法再继续运行
- 正在执行中的进程因提出I/O请求而暂停执行
- 进程通信或同步过程中,执行了某种原语操作
- 抢占方式:允许调度程序根据某种原则,去暂停某个正在执行的进程,将已分配给该进程的处理机重新分配给另一进程
- 优先权原则
- 短进程优先原则
- 时间片原则
轮转调度RR算法
- 一个时间片尚未用完,正在允许的进程便已完成
- 在一个时间片用完时,计时器中断处理程序被激活,若进程尚未运行完毕,调度程序将把它送往就绪队列的末尾。
多级反馈队列调度算法
- 设置多个就绪队列(优先级逐个降低,时间片逐个增大)
- 每个队列都采用FCFS算法(新进程进入,加入第一队列末尾,若未在时间片内完成,则到第二队列末尾)
- 按队列优先级调度
调度算法的性能
- 终端型用户
- 短批处理作业用户
- 长批处理作业用户
3.5 死锁概述
死锁产生原因:多个进程对资源的争夺
- 竞争不可抢占性资源引起死锁
- 竞争可消耗资源引起死锁
- 进程推进顺序不当引起死锁
如果一组进程中的每一个进程都在等待仅由该组进程中的其它进程才能引发的事件,那么该组进程是死锁的。
产生死锁的必要条件:
- 互斥条件
- 请求和保持条件
- 不可抢占条件
- 循环等待条件
处理死锁的方法:
- 预防死锁
- 避免死锁
- 检测死锁
- 解除死锁
预防死锁
破坏四个必要条件之一,其中互斥不能破坏。
- 破坏“请求和保持”条件:进程在中间不会请求新的资源
- 破坏“不可抢占”条件:不可抢占→可抢占,影响进程执行效率
- 破坏“循环等待”条件:规定每个进程必须按序号递增的顺序请求资源
避免死锁
安全状态:系统能按某种进程推进顺序(
P
1
,
P
2
,
…
,
P
n
P_1,P_2,…,P_n
P1,P2,…,Pn)为每个进程
P
i
P_i
Pi分配其所需资源,直至满足每个进程对资源的最大需求,使每个进程都可顺利完成。
银行家算法避免死锁
四个数据结构:
- 可利用资源向量Available
- 最大需求矩阵Max
- 分配矩阵Allocation
- 需求矩阵Need
N
e
e
d
[
i
,
j
]
=
M
a
x
[
i
,
j
]
−
A
l
l
o
c
a
t
i
o
n
[
i
,
j
]
Need[i,j]=Max[i,j]-Allocation[i,j]
Need[i,j]=Max[i,j]−Allocation[i,j]
银行家算法:
- R
e
q
u
e
s
t
i
[
j
]
≤
N
e
e
d
[
i
,
j
]
Request_i[j]≤Need[i,j]
Requesti[j]≤Need[i,j],便转向步骤2;否则认为出错。
2. R
e
q
u
e
s
t
i
[
j
]
≤
A
v
a
i
l
a
b
l
e
[
j
]
Request_i[j]≤Available[j]
Requesti[j]≤Available[j],便转向步骤3;否则表示尚无足够资源,
P
i
P_i
Pi须等待。
3. 系统试探着把资源分配给进程
P
i
P_i
Pi,并修改下面数据结构中的数值:
A
v
a
i
l
a
b
l
e
[
j
]
=
A
v
a
i
l
a
b
l
e
[
j
]
−
R
e
q
u
e
s
t
i
[
j
]
;
Available[j]=Available[j]-Request_i[j];
Available[j]=Available[j]−Requesti[j];
A
l
l
o
c
a
t
i
o
n
[
i
,
j
]
=
A
l
l
o
c
a
t
i
o
n
[
i
,
j
]
R
e
q
u
e
s
t
i
[
j
]
;
Allocation[i,j]=Allocation[i,j]+Request_i[j];
Allocation[i,j]=Allocation[i,j]+Requesti[j];
N
e
e
d
[
i
,
j
]
=
N
e
e
d
[
i
,
j
]
−
R
e
q
u
e
s
t
i
[
j
]
;
Need[i,j]=Need[i,j]-Request_i[j];
Need[i,j]=Need[i,j]−Requesti[j];
4. 系统执行安全性算法,检查此次资源分配后系统是否处于安全状态,若安全才正式将资源分配 给进程
P
i
P_i
Pi,否则作废,恢复原来的资源分配状态。
安全性算法:
- 设置两个向量:
- 工作向量Work,表示系统可提供给进程继续运行所需的各类资源数目,
W
o
r
k
=
A
v
a
i
l
a
b
l
e
Work=Available
Work=Available
- Finish,表示系统是否有足够的资源分配给进程,使之运行完成。开始时
F
i
n
i
s
h
[
i
]
=
f
a
l
s
e
Finish[i]=false
Finish[i]=false;当有足够资源分配给进程时,
F
i
n
i
s
h
[
i
]
=
t
r
u
深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上鸿蒙开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
esti[j];
A
l
l
o
c
a
t
i
o
n
[
i
,
j
]
=
A
l
l
o
c
a
t
i
o
n
[
i
,
j
]
R
e
q
u
e
s
t
i
[
j
]
;
Allocation[i,j]=Allocation[i,j]+Request_i[j];
Allocation[i,j]=Allocation[i,j]+Requesti[j];
N
e
e
d
[
i
,
j
]
=
N
e
e
d
[
i
,
j
]
−
R
e
q
u
e
s
t
i
[
j
]
;
Need[i,j]=Need[i,j]-Request_i[j];
Need[i,j]=Need[i,j]−Requesti[j];
4. 系统执行安全性算法,检查此次资源分配后系统是否处于安全状态,若安全才正式将资源分配 给进程
P
i
P_i
Pi,否则作废,恢复原来的资源分配状态。
安全性算法:
- 设置两个向量:
- 工作向量Work,表示系统可提供给进程继续运行所需的各类资源数目,
W
o
r
k
=
A
v
a
i
l
a
b
l
e
Work=Available
Work=Available
- Finish,表示系统是否有足够的资源分配给进程,使之运行完成。开始时
F
i
n
i
s
h
[
i
]
=
f
a
l
s
e
Finish[i]=false
Finish[i]=false;当有足够资源分配给进程时,
F
i
n
i
s
h
[
i
]
=
t
r
u
深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
[外链图片转存中…(img-X5ygAf57-1715731515516)]
[外链图片转存中…(img-MJgXTdYF-1715731515516)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上鸿蒙开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新