计算机操作系统
文章目录
- 计算机操作系统
核心大题梳理
引言 操作系统基本概念
老师画重点:
操作系统的定义:
计算机系统的组成:
操作系统的作用:
沙漏模型:向上提供类似虚拟机环境 向下提供
抽象:
组成:Windows、MacOS、Linux
操作系统在计算机中的位置
- OS与各层之间的关系
- 与硬件的关系:
- 控制CPU的工作
- 访问存储器
- 设备驱动、中断处理
- 与用户及应用程序的关系
- 提供便捷的用户界面
- 提供优质的服务
- 各层对操作系统的制约
- 下层硬件环境
- 提供OS运行基础
- 限制OS的功能实现
- 上层软件
- 对OS提出要求
- 良好的用户界面
第一章 引论
目标和作用
目标:
- 方便性
- 有效性
- 可扩充性
- 开放性
作用:
- OS作为用户与计算机硬件系统之间的接口
- OS作为计算机系统资源的管理者
- OS实现了对计算机资源的抽象
操作系统发展历程
单道批处理系统
缺点:
- 资源利用率低下
- 程序难以调试、除错
存在问题:
内存中只有一个作业在运行,作业本身串行执行,这就导致CPU必须等待IO操作(如 读数据)完成,才能进行下一步计算。
补一张示例图:
多道批处理系统
解决上述现象
即可以多个作业同时驻留在内存中,这样就保证在某个作业等待IO操作完成时CPU仍然在执行其他作业
宏观上同时运行:都处在运行状态,但是都未运行完毕
微观上交替运行:各作业交替使用CPU
通过这个图我们可以发现,在不同作业调度之间也是需要占用一小部分CPU的
该系统的优点:
- 提高CPU利用率
- 提高内存和IO设备利用率
- 提高系统吞吐量
缺点:
- 平均周转时间长
特征:
- 多道性
- 调度性:作业调度、进程调度
分时系统
区别于批处理系统,分时系统指一台主机上连接多个带有显示器和键盘的终端,同时允许多个用户共享主机的资源,每个用户都可以通过自己的终端以交互方式使用计算机
思想:
- 采用时间片轮转法,为许多终端用户服务,对每个用户能保证足够快的响应时间,并提供交互会话的功能
- 时间片:将CPU的时间划分为若干片段,称为时间片,操作系统以时间片为单位,轮流为每个终端用户服务
特点:
- 多路性
- 独立性
- 及时性 : 用户请求能在很短时间内获得响应
- 交互性
实时系统
特点在于对时间有更严格的控制,如银行、订票系统之类,对系统的响应时间提出更高的要求
所有任务都在规定时间内完成的操作系统,满足时序可预测性
类型:
-
任务执行
- 周期性
- 非周期性
-
截止时间要求
- 硬实时任务(严格)
- 软实时任务(不严格)
基本特征
并发
区分:
- 并行:两个或多个事件在同一时刻发生
- 并发:两个或多个事件在同一时间间隔内发送
- 在多道程序环境下,并发性指宏观上一段时间内有多道程序在同时运行
- 在单处理机系统,微观上这些程序在交替执行
- 进程:能独立运行并作为资源分配的基本单位
- 线程:独立运行和独立调度的基本单位
共享
含义:系统中资源可供内存中多个并发执行的进程共同使用,此外共享以程序的并发为条件
具体:
- 互斥共享:一段时间只允许一个进程访问该资源 => 临界资源
- 同时访问:宏观上同时访问,微观上仍互斥交替访问
虚拟
通过某种技术把一个物理实体变为若干个逻辑上的对应物。前者是实际存在的,而后者是虚构的
时分复用技术:虚拟处理机、虚拟设备
空分复用技术:虚拟磁盘、存储器管理
异步性(不确定性)
操作系统主要功能
处理机管理
-
主要任务:
- 对处理机进行分配
- 对处理机运行进行有效的控制和管理
- 上面两个以进程为基本单位,因此对处理机的管理可归结为对进程的管理
-
主要功能:
- 进程控制(进程管理)
为作业创建进程,撤销已结束进程,控制进程在运行过程中的状态转换。
-
进程同步
为多个进程(含线程)的运行进行协调
- 信号量
-
进程通信
用来实现在相互合作的进程之间的信息交换
- 直接通信
- 间接通信
-
处理机调度
- 作业调度
- 进程调度
- 作业调度
- 进程调度
- 调度算法
内存管理
- 内存分配和回收
- 内存保护:每道用户程序都只在自己的内存空间运行,彼此互不干扰
- 地址映射:将地址空间的逻辑地址转化为内存空间与对应的物理地址
- 内存扩充(虚拟存储技术):用于实现请求调用功能,置换功能等
设备管理
主要功能:
- 缓冲管理
- 设备分配
- 设别处理
- 虚拟设备
主要任务:
完成用户提出的IO请求,为用户分配IO设备,提高CPU和IO设备的利用率,提高IO速度以及方便用户使用IO设备
文件管理
主要功能:
- 文件存储空间的管理
- 目录管理
- 文件的读写管理和保护
主要任务:
管理用户文件和系统文件,方便用户使用,保证文件安全性
第二章
进程
进程和程序的区别
进程是分配资源的基本单位,而线程则是系统调度的基本单位
- 调度性
线程作为调度和分派的基本单位,而进程只作为资源拥有的基本单位
- 并发性
进程可以并发执行,一个进程的多个线程也可并发执行
- 拥有资源
进程始终是拥有资源的基本单位,线程只拥有 运行时必不可少的资源,本身基本不拥有系统资源,但可以访问隶属进程的资源
- 系统开销
进程显著大于线程
线程
多线程模型
多对一模型、一对一模型和多对多模型。
多对一模型的主要缺点在于,如果一个线程在访问内核时发生阻塞,则整个进程都会被阻塞;此外,在任一时刻,只有一个线程能够访问内核,多个线程不能同时在多个处理机上运行。
用户级线程
仅存在于用户空间中的线程,无须内核支持。这种线程的创建、撤销、线程间的同步与通信等功能,都无需利用系统调用实现。用户级线程的切换通常发生在一个应用进程的诸多线程之间,同样无需内核支持。
实现方法:
用户级线程是在用户空间中的实现的,运行在“运行时系统”与“内核控制线程”的中间系统上。运行时系统用于管理和控制线程的函数的集合。内核控制线程或轻型进程LWP可通过系统调用获得内核提供服务,利用LWP进程作为中间系统。
内核支持线程
在内核支持下运行的线程。无论是用户进程中的线程,还是系统线程中的线程,其创建、撤销和切换等都是依靠内核,在内核空间中实现的。
在内核空间里还为每个内核支持线程设置了线程控制块,内核根据该控制块感知某线程的存在并实施控制。
实现方法:
系统在创建新进程时,分配一个任务数据区PTDA,其中包括若干个线程控制块TCB空间。创建一个线程分配一个TCB,有关信息写入TCB,为之分配必要的资源。
当PTDA中的TCB用完,而进程又有新线程时,只要所创建的线程数目未超过系统允许值,系统可在为之分配新的TCB;在撤销一个线程时,也应回收线程的所有资源和TCB。
创建进程
- OS发现请求创建进程事件后,调用进程创建原语Create()
- 申请空白PCB
- 为新进程分配资源
- 初始化进程控制块
- 将新进程插入就绪队列
撤销进程
- 根据被终止进程标识符,从PCB集中检索出进程PCB,读出该进程状态
- 若被终止进程处于执行状态,立即终止该进程的执行,置调度标志 为真,指示该进程被终止后重新调度
- 若该进程还有子进程,应将所有子孙进程终止,以防成为不可控进程
- 将被终止进程拥有的全部资源,归还给父进程,或归还给系统
- 将终止进程PCB从所在队列或列表移出,等待其他程序搜集信息
PV操作
2.3课后练习:
semaphore mutexX = 1; //设置信号量 后面数值表示可用资源量为1
int x;
void P1(){
int y, z;
P(mutexX); //即将要使用x变量 则声明 然后锁住 其他进程无法访问x
x = 1;
y = 0;
if(x >= 1){
y = y + 1;
}
V(mutexX);
z = y;
}
void P2(){
int t, u;
P(mutex);
x = 0;
t = 0;
if(x < 1){
t = t + 2;
}
V(mutex);
u = t;
}
#include <iostream>
using namespace std;
semaphore semBuffer = 9; //资源数 九个进程共享缓冲区
semaphore semProduct = 0; //当前产品的数量 初始为0
semaphore mutexBuffer = 1; //缓冲区互斥访问
semaphore mutexProducerTriAccess = 1; //控制一次性写入3个
void producer(){
do{
//限定一次性写入三个缓冲区
P(mutexProducerTriAccess);
P(semBuffer); //获取一个资源单位
P(semBuffer);
P(semBuffer);
V(mutexProducerTriAccess);
P(mutexBuffer); // 开始访问
... //写入
V(mutexBuffer); //访问结束
//释放一次性写入的三个缓冲区
V(semBuffer);
V(semBuffer);
V(semBuffer);
}while(TRUE); //不断执行
}
void consumer(){
do{
P(semBuffer); //访问缓冲区 获取一个资源单位
P(mutexBuffer);
... //开始执行
V(mutexBuffer);
V(semBuffer);
}while(TRUE);
}
semaphore mutexHall = 50; //最大资源量为50 超时可容纳50人
//下面这两个信号量都是只能单人操作
semaphore mutexEntry = 1;
semaphore mutexExit = 1;
void shopping(){
do{
P(mutexHall);
P(mutexEntry); //这不是执行 只是预告 所以锁住之后 其他代码会执行对应操作
... //进入商场
V(mutexEntry);
... //shopping
P(mutexExit);
... //退出
V(mutexExit);
V(mutexHall);
}while(TRUE);
}
原题练习:
Semaphore empty = 1000 //容量1000人
Semaphore mutex_In = 1 //进口资源控制
Semaphore mutex_Out = 1 //出口资源控制
cobegin
参观者进程i;
{
P(empty) //先声明总数 加括号表示是个函数
P(mutex_In) //请求入
进门;
V(mutex_In) //进完了
参观;
P(mutex_Out)
出门;
V(mutex_Out)
}coend
类似这个关系,在互斥中,我们对两边的队头设置互斥就好了 这个是用mutex 而对内部也需要设置互斥访问,也就是下面的mutexA 和 mutexB
Semaphore mutex = 1; //独木桥同一边方向有人 资源量为1个方向 互斥
Semaphore mutexA = 1; //对countA互斥访问 只能一个
Semaphore mutexB = 1; //对countB互斥访问
int countA = 0, countB = 0;
PA(){
P(mutexA); //countA没被访问 同一组进程 对countA计数器的写操作做保护
if(countA == 0){ //A方向没人 此时如果b有人呢 如果B有人 下面的P就申请不到呗 这里之所以写==0 作用就是判断是不是队首 只有队首需要去互斥 相当于简化了
P(mutex); //开始占用独木桥方向
}
countA ++; //A向放人
V(mutexA);
过桥;
P(mutexA); //再次需要对countA操作
countA --;
if(countA == 0){ //如果没人了 结束占用
V(mutex); //释放A方向对独木桥的占用
}
V(mutexA);
}
PB(){
P(mutexB)
if(countB == 0){
P(mutex)
}
countB ++;
V(mutexB);
过桥;
P(mutexB)
countB--;
//回到队首了
if(countB == 0){
V(mutex)
}
V(mutexB);
}
类似题:
和上面那个逻辑一模一样
semaphore mutexA = 1; //对队内部
semaphore mutexB = 1; //对队内部
semaphore mutex = 1; //对两个队
int countA = 0, countB = 0;
void PA(){
do{
P(mutexA);
if(countA == 0){
P(mutex);
}
countA++;
V(mutexA);
//访问
P(mutexA);
countA--; //按照逻辑思考 读完先减一个
if(countA == 0){
V(mutex);
}
V(mutexA);
}while(TRUE);
}
void PB(){
do{
P(mutexB);
if(countB == 0){
P(mutex);
}
countB++;
V(mutexB);
//访问
P(mutexB);
countB--; //按照逻辑思考 读完先减一个
if(countB == 0){
V(mutex);
}
V(mutexB);
}while(TRUE);
}
例:
考前练习:
P Q R共享一个缓冲区 P为生产者 Q为消费者 R即为生产者又为消费者 若缓冲区为空 可以写入 若缓冲区不空可以读出,实现同步
semaphore mutex = 1; //互斥使用缓冲区
semaphore empty = 1; //缓冲区空位数 如果为1 表示为空 为0 表示非空
semaphore full = 0; //缓冲区产品数
P(){
//写个死循环放着
do{
P(empty);
P(mutex);
...Product one
V(mutex);
V(full); //释放 加1的功效
}while(1);
}
Q(){
do{
P(full);
P(mutex);
...Consume one
V(mutex);
V(empty); //给empty+1 为1 表示空 因为消耗了一个产品
}while(1);
}
Summary:
仔细了解下这个信号量怎么使用设置,直到P和V放置的地方,看下上面这几个题目,PV操作还是比较清晰的!
第三章 处理机调度与死锁
调度
类型:
- 按OS的类型分类
- 批处理调度
- 分时调度
- 实时调度
- 多处理机调度
- 按调度的层次划分
- 高级调度
- 中级调度
- 低级调度
一个作业从提交开始,往往要经历三级调度
- 高级调度
主要任务:决定把外存上处于后备队列中的哪些作业调入内存,创建进程 分配资源,排在就绪队列上。
即允许多少作业同时在内存中运行
而将哪些作业从外存调入内存,将取决于调度算法,先来先服务、短作业优先等
- 低级调度
主要功能:
- 保存处理机的现场信息
- 按某种算法选取进程
- 把处理机分配给进程
从就绪队列中选择一个进程来执行并分配处理机
是OS中最基本的调度
常采用非抢占方式和抢占方式两种
- 非抢占方式:
一旦把处理机分配给某进程后,该进程一直执行,直到进程完成或因某事件阻塞
- 抢占方式:
允许调度程序根据某种原则(时间片、优先权、短进程优先)停止当前正在执行的进程
- 中级调度
内存和外存对换区之间按照给定的原则和策略选择进程对换
引入中级调度的主要目的:为了提高内存利用率和系统吞吐量,使那些暂时不能运行的进程不再占用内存资源,将他们调至外存等待,把进程状态改为就绪驻外存状态或挂起状态。
进程调度算法
处理机调度算法的共同目标
资源利用率、公平性、平衡性、策略强制执行
批处理系统的调度目标
平均周转时间短、系统吞吐量高、处理机利用率高
准则
面向用户:
- 周转时间短
- 响应时间快
- 截止时间的保证
- 优先权准则
面向系统:
- 系统吞吐量
- 处理机利用率好
- 各类资源平衡利用
最优准则:
- 最大的CPU利用率
- 最大的吞吐量
- 最短的周转时间
- 最短的等待时间
- 最短的响应时间
计算:
CPU利用率 = CPU有效运行时间 /(运行时间+空闲时间)
响应时间:交互式进程从提交一个请求到系统响应之间的时间间隔
吞吐量:单位时间内处理的作业··数
周转时间 = 作业等待时间 + 作业运行时间 即从作业提交给系统开始,到作业完成为止的时间间隔
平均周转时间 =
其中Ti 为 服务结束时间 - 到达时间
带权周转时间 = 周转时间 / 进程要求运行时间
平均带权周转时间 =
Ti 为 服务结束时间 - 到达时间
Ts 为 需要服务时间
平均等待时间:(开始服务时间 - 到达时间)和 / 个数
需要考虑的因素:
- 系统利用率:充分使用系统中的各类资源 尽可能使多个设备并行工作
- 调度开销:调度算法不能太复杂 因为调度本身需要很大开销
先来先服务调度算法 (FCFS)
按照进程进入队列的先后次序分配处理机
一般采用非剥夺的调度方式
使用甘特图
考虑周转时间(结束时间-达到时间)
平均周转时间:加起来 / 个数 会考喔!
等待时间:开始时间 - 达到时间 一句话给你点透什么是等待时间:就是从到了开始,只要不在执行那就是等待时间,加起来就完事,顾名思义!
平均等待时间:加起来 / 个数
平均带权周转时间:基于周转时间 / 需要服务时间 求和除个数 (带小数 保留两位小数即可)
短作业(中级调度)/进程(低级调度)优先调度算法
短作业SJF
- 用于作业调度
- 从后背队列中选择一个或若干个估计运行时间最短的作业,调到内存
短进程SPF
- 用于进程调度
- 把CPU进行分配
- 继续分类
- 可剥夺(抢占)
- 不可剥夺
Summary:
都是在后备队列中选择时间短的进行优先处理
应对论述题:
优点:
- 有效降低作业的平均等待时间
- 提高吞吐量
- 有效缩短进程的周转时间
缺点:
- 对长作业不利
- 不考虑作业的紧迫程度
- 作业执行时间、剩余时间仅为估计时间
区分疑惑(作业调度和进程调度的区别):
概述:
对于作业调度
对于进程调度(注意 只有进程调度才会涉及到抢占式和非抢占式)
参考文章:https://blog.csdn.net/qq_51694696/article/details/118270795
高响应比优先调度算法(HRRN)
综合考虑进程等待时间和执行时间
(等待时间+服务时间) / 服务时间
对作业进行调度 不存在抢占或者非抢占 都是非抢占
解释一下引出这个算法的原因:
- FCFS算法只考虑作业:只考虑作业等候时间而忽视了作业的计算时间
- SJF算法只考虑用户估计的作业时间计算而忽视了作业的等待时间
所以产生了这么一个既考虑作业等待时间,又考虑作业运行时间的 根据这两个计算出每一个进程的优先权
直接上例题
注意:上面的可以写成 1 + 等待/要求 计算的结果是该作业的响应比 所以越高的越优先
上面是一个组合 从两个不完美到一个合并 下面这个也一样 从两个结合发展到一个
时间片轮转(RR)调度算法
个人理解引入:
基于时间片轮转,简单来说就是雨露均沾,根据FCFS的调度原则,先对所有已经到达的进入的进程进行排列,然后从队首开始,分配一个时间片, 如果进程的执行时间少于时间片,则自愿释放CPU,对下一个分配。如果一个时间片用完之后该进程还没有结束,则调度程序将该进程插入到就绪队列末尾,将CPU分配给新的队首进程,同时让它执行一个时间片
来个例题:
优先权调度算法
一般数字越小,优先权越高,看题目
分为抢占式和非抢占式
同时关于优先权也有分类:
- 静态优先权:优先权在创建进程时确定,在程序的整个运行期间保持不变,一般用一个整数表示,越小优先级越高
- 动态优先权:在创建进程时确定,但在进程的运行期间发送改变
- 根据进程占有CPU时间决定
- 根据进程等待CPU时间决定
多级反馈队列调度算法
结合了上面的时间片轮转算法和优先级调度算法的综合发展,动态调整进程优先级和时间片大小,目前较不错的进程调度算法
特点:
- 长短作业兼顾,又较好的响应时间
- 短一次完成
- 中作业周转时间不长
- 大作业不会长期不处理
- 向短时间作业,I/O繁忙和交互式进程倾斜
- 具有**“运行时间越长,则等待时间越长”**特点
解释执行流程:
- 设置多个就绪队列,每个队列的优先级不同,其中队列1优先级最高,优先执行,且优先级高的队列中的进程,获得CPU的时间片越短
- 当新进程进入系统,放到队列1的末尾,按照FCFS等待调度。如果进程执行完成,则撤离系统,否则转移到下一个程序的尾部,依次类推,如果到最后还没完成,在就绪队列n中执行RR时间片轮转算法,直到执行完成
- 上一队列为空才能下一队列中的进程才能开始运行。
- 抢占方式:当新进程到比当前优先级高的队列,则被抢占,原进程转移到其所在队列的尾部
综合例题练习:
讲解:非常脆爽 https://blog.csdn.net/scarificed/article/details/115215962
首先要理解:该题其实要分两个阶段:
- 一个是作业到内存中,这是作业调度,采用短时间优先调度
- 另一个是内存请求CPU进行执行,这是进程调度,采用优先权调度算法
逻辑:
10:10 J1进入, 此时内存中只有J1 开始执行J1
10:20 J2进入, 因为内存可以装两个,所以J2进入内存, 同时因为2的优先数为3 抢占 开始执行J2
10:30 J3进入, 因为此时内存有两个,J3等待进入内存,待会如果内存有空位,则等待中的短时间的优先进入
10:50 J4进入, 此时内存有两个,J4去等待进入内存
死锁
概述
P:Process
R:Resource
资源指向进程:资源已经分配给进程
进程指向资源:进程请求该资源
注意:
- 至少两个进程,每个进程需要多个资源才会有可能产生死锁
- 参与死锁的所有进程均等待资源
- 参与死锁的进程至少有两个占有资源
定义:多个进程在运行过程中因抢夺资源而造成的一种僵局,如果没有外力作用,所有进程都无法向前推进
资源分类:
根据资源性质本身:
- 可剥夺资源:CPU、主存
- 不可:驱动器、打印机
根据资源使用期限:
- 永久
- 临时
而竞争的资源就是上面所说的非剥夺性资源、临时性资源
产生死锁的原因:
- 进程推进顺序不当
产生死锁的必要条件:
- 互斥条件(资源独占条件)
- 请求和保持条件(部分分配条件) 占一部分资源 但是需要新的资源得不到 这时占的资源也不释放
- 不剥夺条件(不可抢占) 优先级高的剥夺优先级低的可以避免死锁
- 循环等待条件(环路条件):P1等待P2的资源 … Pn等待P1的资源
处理死锁的四种方法
1. 预防(防范程度最高,往下依次递减)
通过设置某些限制条件,去破坏产生死锁的四个必要条件中的一个或多个
常针对条件2、3、4
- 针对2:
预先静态分配方法,即要求在进程运行之前一次申请它所需要的全部资源,在资源未满足前,不投入使用。人话就是要么全占,要么一点别占。
虽然简单安全,但是降低了资源利用率,同时必须预知进程所需要的全部资源
- 针对3:
一个已经获得某些资源的进程,若又请求新的资源不能满足,必须释放出占有的资源,以后重新申请
- 针对4:
采用有序资源分配法,所有资源都按照类型赋予一个编号,要求每一个进程均严格按照编号递增的次序请求资源,同类资源一次申请完。
好处是一定不会产生死锁
坏处:
加重了进程负担
因使用资源顺序与申请顺序不同而造成资源浪费
2. 避免(银行家算法)
在资源的动态分配过程中,用某种方法去防止系统进入不安全状态
如果分配给你,系统仍然处于安全状态,则分配 一定不会产生死锁
什么是安全状态:
系统
银行家算法:必考
一图打通:
该算法之所以叫银行家算法,顾名思义就是模仿银行与客户之间进行贷款的流程,所以不要把他想的太复杂,就是一个简单的贷款还款再贷款的问题,在Claim中 相当于有四个客户 P1-4 每个客户都有ABC三个工程去贷款 只有这三个工程都有资金,客户才能启动整个项目
例题理解:
分析:
:happy: 资源总数量:上图中的Resource 银行可分配给贷款方的钱
:happy: 分配矩阵:Allocation 银行已经分配给贷款方的钱
:happy: 最大需求矩阵:Claim 贷款方总共需要的钱的情况
系统是否安全就看能否形成一个均满足的贷款顺序 该顺序称为安全序列,但是注意安全序列不是唯一的,只要存在安全序列,则系统处于安全状态
这个检测的顺序,一般从当前往下循环,比如第一个选中了D 下面我要检测E 而不是从头检测A 测完E再循环到头部
考点计算:
:happy: 资源总数 = Allocation + Available
:happy: New Available = Allocation + Available
翻译成人话,每次Need(最大需求-已分配)只是用来判断的,只要满足可以选中,则直接回收已分配的资源就好!
答题:
依据上表演示流程,存在一个安全序列P1 … 所以系统是安全的
若P1申请资源(1, 0, 2),是否可以分配
系统按银行家算法进行检查:
- Request(1,0,2) <= Need(1,2,2) 相当于提前做一个预估 不能超过Need
- Request(1,0,2) <= Available(3,3,2)
- 先假定为P1分配资源,并修改Available,Allocation,Need向量 进行推演
3. 检测
允许系统在运行过程中发生死锁,但可设置检测机构及时检测死锁的发生,并采取适当措施加以清楚
4. 解除
描述死锁
资源分配图
参考:https://blog.csdn.net/qq_39328436/article/details/111123779
死锁定理:
如果分配图不可完全简化,说明发生了死锁
当且仅当该状态的资源分配图是不可完全简化的(死锁的充分条件)
例题:以一个简单的分配图进行学习
只针对I进行简化
对图进行解释:
P1和P2代表进程
R1和R2代表资源
其中R中的小圆圈代表资源总量,已分配的也包括
从R指向P的箭头代表已经分配出去的资源
从P指向R的箭头代表进程请求的资源,还需要
针对P1
可以发现R2只剩一个资源 R1没有资源 所以P1无法请求 不能消边
针对P2
正好请求一个R2的资源 请求成功 然后释放P2
此时P1也可以请求成功
到此结束 不会发生死锁!
题目练习:
第四章 存储器管理
存储其管理的主要功能
- 内存的分配和回收
- 地址变换
- 内存扩充
- 内存共享
- 存储保护
基本概念
主存储器:简称内存或主存,用于保存进程运行时的程序和数据
寄存器:在CPU内部,具有与处理机相同的速度
硬盘:就是我们电脑里面的C盘 D盘之类
高速缓存:介于寄存器和存储器之间的存储器,主要备份主存中较常用的数据,减少处理机队主存储器的访问次数
关系如下:
存储单元:
- 存储最小单位:“二进制位“,包含信息0或1
- 最小编址单位:字节,一个字节包含八个二进制位
连续分配存储管理方式
- 固定分区分配
- 可变分区
基于动态搜索的动态分区分配算法
基于顺序搜索:首次适应算法、循环首次适应、最佳适应算法、最坏适应算法
基于索引搜索:快速适应算法 伙伴系统 哈希算法
首次适应算法(FF)
低地址优先分配
算法思想:
空闲分区按地址递增次序链接
从链首开始查找,直到找到一个大小能满足的空闲分区为止,下一次分配从头开始遍历
注意要不断更新喔
循环首次适应算法(NF)—— 邻近适应
算法思想:
从FF演变而来
按地址递增次序,下一次分配不是从头开始,而是从上一次分配的地方开始
最佳适应算法(BF)
最容易产生内存碎片
算法思想:
空闲分区按容量递增排列
等待分配时,空闲区间从小到大排序
最坏适应算法(WF)
算法思想:空闲分区按容量递减排列
从链首开始查找,找到第一个符合要求的空闲分区
时间上比BF快一点
但是在空间上较差,容易产生一些碎片
例题:
可重定位分区分配方式
-
内部碎片:指分配给程序的存储空间中未被利用的部分
-
外部碎片:程序外 不同程序之间的碎片
内存扩充:覆盖与交换(了解)
- 覆盖技术:解决大程序 小内存矛盾
基本分页存储管理方式
什么是页表?
页表是分页式存储管理使用的数据结构。一个进程分为多少页,它的页表就有多少行。每一行记录进程的一页和它存放的物理块的页号、块号对应关系。页表用于进行地址变换。
作用:实现页号到物理块号的地址映射
基本分段存储管理方式
虚拟存储器的基本概念
请求分页存储管理方式
页面置换算法
地址
逻辑地址:
因为用户程序和非 常驻的系统程序随机且动态进入系统,编译和链接程序无法预先确定内存存储位置,所以采用逻辑地址。编译时程序中各个地址总是以“0”作为起始地址
相对地址,虚地址
采用相对地址,其首地址为0,其余 指令中的地址相对于首地址而编址
不能用逻辑地址在内存中读取信息
**物理地址:**绝对地址,实地址
!!高频考点!!
地址重定位
原因:
-
地址空间的逻辑地址往往与分配到存储空间的物理地址不一致
-
处理机必须使用物理地址 就像发快递
方法:
逻辑地址转为物理地址的方法:
首先,计算该逻辑地址所在的页号和页内偏移
页号 = 逻辑地址 / 页面大小(取整)
页内偏移 = 逻辑地址 % 页面大小
其次,在确定该逻辑地址合法的情况下,查页表,根据页号得到对应的物理块号
最后,物理地址 = 快号 x 页面大小 + 页内偏移
上面这是计算公式,下面看一个真实的例题
当页面大小为1K 即2的10次方 所以需要10位表示页内地址 也就是说每页大小是2的10次方B
区分:
MB
Mb
大写是Byte 小写是bit
装入和链接(概念性 了解即可)
装入:
- 绝对装入
在编译时,实现知道用户程序的绝对地址,直接访问即可
该装入方式,仅适用于单道程序(但是效率太低,几乎没有了)
- 可重定位
实现不知道用户程序在内存的驻留位置,而是在装入时根据内存的实际情况把相对地址(逻辑地址)转换为绝对地址,在装入时进行地址转换
在装入时一次定位 后面不再修改 称为静态可重定位
特点:
- 每个程序分配一个连续的存储区
- 不能移动
- 动态运行时
加载到内存时原样装入,在运行时送入重定位寄存器
链接:
- 静态链接 => 绝对装入
编译器编译完成后进行链接,多个obj文件链接形成可执行exe文件
-
装入时动态链接
-
运行时动态链接
分页存储管理
思考问题:
- 页表给谁用 物理器件
- 页表内容
- 页表谁来维护
- 逻辑地址与物理地址之间的转化图
一些计算
单位:
例题:
根据给定的逻辑地址结构,可以看出:
- 偏移量占用 12 位:这意味着每个页内的偏移量大小为 2^12=4096 字节。
- 页号占用 20 位:这表示逻辑地址空间中有 2^20 B基本单位 个页 = 1MB
因此,页的大小为 4096 字节 = 4KB。
对于一级页表的分页存储管理,每个页表项大小为 4 字节,而逻辑地址中的页号占用 20 位,因此最大页表项数为 2^20 个。 N页表项数 * S页表项大小
所以,页表的最大占用空间为 2^20×4字节,即 4 MB。
两级和多级页表
页表是可以离散的,但是物理地址必须连续
为什么物理地址是连续的?
硬件得到 页表的基地址 如果不连续 则页表的索引号找到的物理地址可能不存在 导致出错
例题:
逻辑地址空间64页 每页1024B 内存共有32个存储块
2^6 2^10 => 逻辑地址16位
块大小和页大小一定相同 都是1KB
内存空间为 32 * 1KB = 32KB
段页式存储管理
需要访问内存3次
libc.so 在物理内存中 主存 也就可以实现共享
第五章 虚拟存储器
概述
局部性原理
- 时间局部性
指令或者数据在访问过后的不久可能再次被访问 (如存在循环)
- 空间局部性
数组 开辟相邻的空间
常规存储器:
- 一次性:程序运行时作业一次性全部装入内存
- 驻留性:直到程序结束才会在内存中退出
虚拟存储器定义
虚拟存储器,是一个内存管理技术,强调软件系统。具有请求调入和置换功能,能从逻辑上对内存容量进行扩充的一种存储器管理机制。将程序可以使用的内存与物理内存解耦
- 物理内存大小由谁决定?
内存条设置多少就是多少
- 逻辑地址空间大小由谁决定?
CPU的地址位宽决定
- 逻辑地址与物理地址的映射关系?
页表决定
页表给硬件使用,软件操作系统来维护
通常是动态更新
逻辑地址区间 可以大于 物理内存大小 从而成为虚拟扩充 类比从北京到上海不断换两块铁轨 更新
每个程序可以使用同样大小的逻辑地址空间
如果指向同一个页框数据段,了解沙箱逃逸
- 逻辑地址与物理地址不再是1对1关系
虚拟内存三个主要特征:
- 多次性*:无需在作业运行时一次性全部装入到内存,而是允许被分成多次调入内存
- 对换性*:在作业运行时无需一直常驻内存,可以将作业换入换出
- 虚拟性:从逻辑上扩充内存容量,真实物理内存不变,使用户看到的内存容量远大于实际的容量
不装入内存:
不是什么都不做,而是给他设置页表,但不分配物理内存来处理代码
好处:
- 提高内存利用率
实现方法
分页请求系统
基于分页系统,增加请求调页功能和页面置换功能,从而形成页式虚拟存储系统
置换时以页面为单位
请求分段系统
基于分段系统,增加请求调段和分段置换功能,从而形成段式虚拟存储系统
置换时以段为单位
请求分页系统存储管理
该系统建立在基本分页基础上,增加了请求调页功能和页面置换功能
每次调入和换出的基本单位都是长度固定的页面
-
请求分页中的硬件支持
- 请求页表机制
作用:用户地址空间的逻辑地址 – > 内存空间的物理地址
页表项:
页号 物理块号 状态位P 访问字段A 修改位M 外存地址 指示该页是否已调入内存 记录本页在一段时间内被访问的次数或最近未被访问的时间=>用来置换用 表示该页在调入内存后是否被修改过,如果修改过,换出时需要重写至外存 - 缺页中断机构
在请求分页系统中,当所要访问的页面不在内存时,产生缺页中断,请求OS将所缺页调入内存
与一般中断有两个方面的明显区别:
- 缺页中断在指令执行期间产生和处理中断信号
- 一条指令在执行期间可能产生多次缺页中断
-
地址变换机构
如何从逻辑地址变为物理地址:
首先检索快表
例题:逻辑地址转物理地址
信息提取:
每页1KB => 1024 B =>2^10 B => 页内地址10位
32个页面 => 2^5 =>页号5位
主存16KB => 2^4 => 4个框
物理块分配策略
- 内存分配策略
- 固定分配局部置换
- 可变分配全局置换
- 置换策略
- 全局置换
- 局部置换
物理块的分配算法(固定分配时):
补充:请求分段存储管理
请求段页式存储管理
页面置换算法
最佳置换算法
置换永不使用或未来最长时间内不会被访问的页面
参考:https://blog.csdn.net/weixin_45990326/article/details/120031133
只有缺页才会做替换 所以最后一个出现的页号是 7 然后是1 0 4 3
先进先出FIFO置换算法
原则:淘汰最先进入内存的页面,即选择在内存中驻留时间最久的页面予以淘汰
最近最久未使用LRU置换算法
选择到当前时间为止,访问次数最少的页面淘汰,给每个页面设置访问计数器,每当页面被访问,访问计数器加1。当发生缺页中断时,淘汰计数值最小的页面,同时一定要注意,将所有计数器清零。
Clock置换算法(最近未用NRU置换) 可能会考哦
改进型Clock算法
相比于Clock算法,添加了修改位
A \ M | 0 | 1 |
---|---|---|
0 | 00(1)最佳淘汰页 后面的序号表示优先级 | 01(2) |
1 | 10(3) | 11(4) |
10相比01更可能发生缺页
”抖动“与工作集(not important Learn by self)
请求分段式存储管理方式 (不如分页重点)
- 请求分段中的硬件支持
- 段表机制
- 缺段中断机构
- 地址变换机构
- 分段共享与保护
- 共享段表
- 共享段的分配与回收
段名 | 段长 | 段的基址 | 存取方式 | 访问字段A | 修改位M | 存在位P | 增补位 | 外存地址 |
---|---|---|---|---|---|---|---|---|
缺段中断机构
第六章 输入输出系统
I/O系统的基本功能
- 隐藏物理设备的细节
- 与设备的无关性
- 提高处理机和I/O设备的利用率
- 对I/O设备进行控制
- 确保对设备的正确共享
- 错误处理
I/O系统的层次结构和模型
- 层次结构
用户层软件 | 用于实现用户与I/0设备交互 |
---|---|
设备独立性软件 | 用于实现用户程序与设备驱动器的统一接口、设备命令、设备保护,以及设备分配与释放等。 |
设备驱动程序 | 与硬件直接有关,用来具体实现系统对设备发出的操作指令,驱动I/0设备工作 |
中断处理程序 | 用于保存被中断进程的CPU环境,转入相应的中断处理程序进行处理,处理完后恢复现场,并返回到被中断的进程 |
小知识点:
举例设备驱动程序完成的内容:
- 向设备寄存器写命令
- 检查用户是否有权使用设备 IO请求的合法性
- 解释用户的IO请求,并将请求转化为具体的IO操作
- 及时响应由控制器或通道发来的中断请求
- 了解IO设备的状态,传送有关参数,设置设备的工作方式
不是的举例为:
- 将二进制整数转化为ASCII码以便打印
- 控制IO设备的IO操作
设备的独立性:指用户不指定特定的设备,而指定逻辑设备,使得用户作业和物理设备独立
I/O系统接口
- 块设备接口
- 块设备
- 隐藏了磁盘的二维结构
- 将抽象命令映射为低层操作
- 流设备接口
- 字符设备
- get和put操作
- in-control指令
- 网络通信接口
I/O设备
类型:
-
按使用特性分类
存储设备(外存、辅存) 其中磁盘适合作为共享设备 I/O设备(输入设备,输出设备,交互式设备) -
按传输速率分类
低速设备 中速设备 高速设备
设备与控制器之间的接口
设备不是直接与CPU进行通信,而是与设备控制器通信,所以设备与设备控制器之间存在接口
设备独立性软件,也称设备无关软件。
为了提高0S的可适应性和可扩展性,在现代0S中都毫无例外地实现了设备独立性,也称设备无关性。
基本含义:应用程序独立于具体使用的物理设备。为了实现设备独立性而引入了逻辑设备和物理设备两概念。在应用程序中,使用逻辑设备名称来请求使用某类设备;而系统在实际执行时,还必须使用物理设备名称。
优点:
-
设备分配时的灵活性
- 易于实现I/0重定向(用于I/0操作的设备可以更换(即重定向),而不必改变应用程序。
设备控制器
功能:控制一个或多个I/O设备,以实现I/O设备和计算机之间的数据交换
工作流程:接收CPU发来的命令,去控制I/O设备工作,使处理机能够从繁杂的设备控制事务中解脱出来。
分类:
- 用于控制字符设备的控制器
- 用于控制块设备的控制器
基本功能:
- 接受和识别命令
- 数据交换
- 标识和报告设备的状态
- 地址识别
- 数据缓冲区
- 差错控制
组成:
- 设备控制器与处理机的接口
- 设备控制器和设备的接口
- I/O逻辑
I/O通道
背景:CPU和I/O设备之间增加设备控制器后,大大减少CPU对I/O的干扰,当主机所配置的外设很多,CPU仍然负担很重,所以在CPU和设备控制器之间又增设了I/O通道
目的:建立独立的I/O操作,不仅使数据的传送独立于CPU,而且希望对I/O操作的组织、管理及其结束处理尽量独立,保证CPU有更多时间去进行数据处理,总结来说就是替CPU分担繁杂的I/O任务
特点:IO通道本质是一种特殊的处理机
- 指令类型单一
- 通道没有自己的内存,与CPU共享内存
类型:
- 字节多路通道
- 数组选择通道
- 数组多路通道
瓶颈:通道价格昂贵, 致使机器所设置的通道数量有限,进而造成系统吞吐量下降
如上图所示,当设备1和设备3都请求通道1的时候就会出现冲突
但是我们的目标不是增加通道
所以瓶颈的解决方案为:增加通路 而不增加通道
增加控制器与通道之间的通路 也增加设备与控制器之间的通路
小知识点:利用通道实现了内存和外设之间数据的快速传输
中断
基本简介
中断:CPU对IO设备发来的中断信号的一种相应,CPU暂停正在执行的程序,保留CPU环境后,自动地转去执行该IO设备的中断处理程序,执行完后,再回到端点,继续执行原来的程序。中断是由外部设备引起的,故又称外中断 CPU只有在处于中断允许状态时,才能响应外部设备的中断请求
陷入:CPU内部事件引起的中断,这类中断称为内中断或陷入(trap)
中断向量表:每种设备配以相应的中断处理程序,并把该程序的入口地址放在中断向量表的一个表项中,当IO设备发来中断请求信号,中断控制器确定该请求的的中断号,根据设备终端号去查找中断向量表,从中取得该设备中断处理程序的入口地址,转入中断处理程序执行
中断优先级:对设备发出的中断信号源定级
对多中断源的处理方式:
- 屏蔽(禁止)中断
- 嵌套中断
中断处理程序
步骤:
- 测定是否有未响应的中断信号
- 保护被中断进程的CPU环境
- 转入相应的设备处理程序
- 中断处理
- 恢复CPU的现场并退出中断
小知识点:
硬件采用了中断和通道技术,使得CPU与外设能并行工作
首先获得键盘输入信息的程序是中断处理程序
需要外存的支持:输入井和输出井就是在磁盘(外存)开辟的存储空间
需要多道程序设计技术的支持:实现IO的输入输出控制
需要注意:设备与输入输出井之间的数据传送是系统控制的不是用户作业控制设备控制的
设备驱动程序
对IO设备的控制方式
与设备无关的IO软件
基本概念
软件
设备分配
逻辑设备名到物理设备名
背景:用户程序请求使用IO设备 应当用逻辑设备名,而操作系统内部设备管理设备使用的是物理设备名,因此需要再系统中配置一张逻辑设备表,用于将逻辑设备名映射为物理设备名
用户层的IO软件
系统调用和库函数(dll)
假脱机系统(spooling)
直观类比感受:
多道程序技术:将一台物理CPU虚拟为多台逻辑CPU,允许多个用户共享一台主机
假脱机技术:将一台物理IO设备虚拟为多台逻辑IO设备,允许多个用户共享一台物理IO设备
目的:提高独占设备的利用率
脱机就是指和CPU分离
比如这种 属于真脱机,程序员与CPU之间放了一个外围机来处理输入输出操作
假脱机顾名思义就是指不真正脱离
输入设备速度过慢,CPU处理快,所以建立了井,其作用就是攒数据,不用CPU在那一直等着输入,而是到达一定数据量后一并操作,提高处理效率
组成:
工作原理:
小知识点:
在假脱机IO技术中,对打印机的操作实际上是用对磁盘存储的访问,用以代替打印机的部分通常称作虚拟设备
SPOOLing技术将独占设备改造为共享设备,实现虚拟设备功能,提高独占设备的利用率
缓冲区管理
基本概念
引入缓冲的原因:
- 缓解CPU和IO设备间速度不匹配的矛盾
- 减少对CPU的中断频率,放宽对中断响应时间的限制
- 提高CPU和IO设备之间的并行性
缓存
单缓冲区
双缓冲区
环形缓冲区
缓冲池
便于使得多个进程能有效地同时处理输入和输出
磁盘存储器的性能和调度
磁盘性能简述
- 数据的组织和格式
磁盘设备的特点:
- 传输速率较高,以数据块为传输单位
- IO控制方式常采用DMA方式
- 可以寻址,随机地读写任意数据块
系统将数据读到内存的过程顺序:
- 初始化DMA控制器并启动磁盘
- 从磁盘传输一块数据到内存缓冲区
- DMA控制器发出中断请求
- 执行“DMA结束”中断服务程序
DMA:用来提供在外设和存储器之间或者存储器和存储器之间的高速数据传输,无须CPU干预,节省CPU的资源来做其他操作
-
磁盘的类型
-
磁盘访问时间
读取扇区时间例题:
磁盘转速为3000转每分钟,那么转1圈耗时60 / 3000 = 0.02s = 20ms
盘片划分10个扇区 则读取一个为20ms / 10 = 2ms
P338 例题16
磁盘调度算法
- 早期
- 先来先服务FCFS
按照给出的顺序逐个计算
- 最短寻到时间优先SSTF
不断寻找最近的 保证眼前最优
- 基于扫描
- 扫描(SCAN)算法
从开始位置往大增 直到增到最大后往回缩 类似电梯喔 注意这个方向是提前规定的
小技巧:
判断磁头的移动方向
题目中提示到,完成了1250然后到3500 所以方向是先向大号方向
- 循环扫描(CSCAN)算法
注意看方向
始终同方向
- NStepSCAN算法
小知识点:
磁臂黏着:磁臂停留在某处不动,如有一个或几个进程对某一磁道有较高的访问频率,即这个进程反复请求对某一磁道的IO操作,从而垄断整个磁盘设备。该情况在SSTF、SCAN、CSCAN几种调度算法中都可能出现
- FSCAN算法
第七章 文件管理
文件和文件系统
管理功能:将管理的程序和数据通过组织为一系列文件的方式实现
文件:具有文件名的若干相关元素的集合
元素:通常是记录
记录:一组有意义的数据项的集合
综上 数据组成可以分为数据项、记录、文件三级
下面展开详细解释
- 数据项
最低级的数据组织形式
分类:
(1) 基本数据项
描述一个对象的某种属性的字符集,是数据组织中可以命名的最小逻辑数据单位 如:描述一个学生的基本数据项:学号、姓名、年龄
(2) 组合数据项
若干个基本数据项组成,简称组项
- 记录
一组相关数据项的集合,用于描述一个对象在某方面的属性。关键字是唯一能标识一个记录的数据项。
- 文件
由创建者所定义的、具有文件名的一组相关元素的集合,可以分为有结构文件和无结构文件。
- 有结构的文件中:文件由若干个相关记录组成
- 无结构文件中:被看成是一个字符流
文件是文件系统中一个最大的数据单位
文件属性包括:
- 文件类型
- 文件长度
- 文件的物理位置
- 文件的建立时间
文件类型分类:
- 按用途分裂:
- 系统文件
- 用户文件
- 库文件
- 按文件中数据的形式分类:
- 源文件
- 目标文件
- 可执行文件
- 按存取控制属性分类
- 可执行文件
- 只读文件
- 读写文件
- 按组织形式和处理方式分类
- 普通文件
- 目录文件
- 特殊文件
文件系统的层次结构
用户(程序) 自上而下 | |
---|---|
文件系统接口 | 两种类型:命令接口和程序接口 |
对对象操纵和管理的软件集合 | 文件管理系统的核心部分 |
对象及其属性 | 管理的对象有:文件、目录、磁盘(磁带)存储空间 |
文件的逻辑结构
用户看到的文件成为逻辑文件,在系统中所有文件存在以下两种大类:
- 文件的逻辑结构:文件由一系列的逻辑记录组成的,是用户可以直接处理的数据及其结构,独立于文件的物理特性,又称为文件组织
- 文件的物理结构:又称文件的存储结构,指系统将文件存储在外存上所形成的一种存储组织形式,是用户不能看见的。
对文件的逻辑结构进行分类:
-
有结构文件,指由一个以上的记录构成的文件,又称为记录式文件
- 定长记录
- 变长记录
如大量的信息管理系统和数据库系统中,广泛采用了有结构的文件形式
-
无结构文件,指由字符流构成的文件,又称流式文件
如在系统中运行的大量的源程序、可执行文件、库函数等
按文件的组织方式分类:
- 顺序文件
排列方式:
- 串结构
- 顺序结构
优点:对文件中的记录进行批量存取
缺点:增加或者删除一个记录比较困难
访问 => 记录寻址
为了访问顺序文件中的一条记录,首先应找到该记录的地址,有两种方法:
-
隐式寻址方法
-
显示寻址方法
-
索引文件
-
按关键字建立索引
-
具有多个索引表的索引文件
- 索引顺序文件
-
索引顺序文件的特征
-
一级索引顺序文件
-
二级索引顺序文件
文件目录
对于目录管理的要求如下:
-
实现”按名存取“
-
提高对目录的检索速度
-
文件共享
-
允许文件重名
文件控制块FCB(File Control Block)
小tips:
在一个文件被用户进程首次打开的过程中,操作系统需做的是将文件控制块读到内存中
文件系统中,文件访问控制信息存储的合理位置是文件控制块
通常含有三类信息:
-
基本信息类
-
存取控制信息类
-
使用信息类
索引节点
-
索引结点的引入
-
磁盘索引结点
-
内存索引结点
简单的文件目录
-
单击文件目录
-
两级文件目录
小tips:
- 采用多级目录结构后,不同用户文件的文件名相同或不同均可
树形结构目录(Tree-Structured Directory)
目录查询技术
-
线性检索法
-
Hash方法
文件共享
两种大方式实现共享
- 基于有向无循环图实现文件共享
-
有向无循环图DAG
-
利用索引结点
小tips:
基于索引结点的共享会在文件主删除其共享文件后留下悬空指针
- 利用符号链接实现文件共享
小tips:
若多个进程共享同一个文件F,在系统打开的文件表中仅有一个表项包含F的属性
文件保护
保护域(Protection Domain)
-
访问权
-
保护域
-
进程和域间的静态联系
-
进程和域间的动态联系方式
访问矩阵
利用矩阵描述系统的访问控制,并把该矩阵成为访问矩阵(Access Matrix)
切换
修改
所有权
控制权
实现:
-
访问控制表
-
访问权限(Capabilities)表
分类:
大类上:
- 文本文件 记事本打开后是printable
- 二进制文件 记事本打开后是乱码
结构上:
- 无结构:如txt(不可执行) js、bat(可执行)
- 有结构:如xml 、 ini、obj(不可执行) dll、exe、so(可执行)
索引文件:
两部分组成:
- 索引表:存放元数据
- 主文件
第八章 磁盘存储器的管理
外存的组织方式
连续组织方式
链接分配
FAT技术
索引分配
-
单级索引
-
多级索引
文件存储空间的管理
空闲表法和空闲链表法
位示图法
利用二进制的一位来表示磁盘中一个盘块的使用情况,用0和1表示两种状态空闲和已分配,所有盘块对应的位构成一个集合,称为位示图。本质就是一个二维数组map[m, n]
如
盘块的分配:
三步走
- 顺序扫描位示图
先按顺序找到一个表示空闲的二进制位,当0表示空闲时,就先找到第一个0
- 转化
将找到的一个或一组二进制位转换成预支对应的盘块号,其实说白了就是根据行列号转化成它在这个二维数组中的第几个,对应公式如下:
b = n(i - 1) + j 其中i和j是当前行列号 注意是从1开始的 n代表每行的个数
- 修改位示图
令map[i, j] = 1