计算机操作系统期末考试

注:本篇文章基于《计算机操作系统》(慕课版)所写——当然殊途同归,对于考试可能会考核到的重点内容都会涉及,如果有缺少还请补充。

第一章 引论

操作系统的目标和作用

目标:目标
作用:
在这里插入图片描述

操作系统的发展

1.单道批处理系统:一个接一个处理
2.多道批处理系统:放在外存,按照算法选择若干个作业共享cpu和系统资源——此时已经初具操作系统的模型
3.分时系统:多路性,独立性,及时性,交互性
4.实时系统:多路性,独立性,及时性,交互性,可靠性
实时任务类型5.微机操作系统:配置在微机上的系统
6.嵌入式操作系统:系统内核小,系统精简,实时性高,具有可配置性
7.网络操作系统:硬件独立性,接口一致性,资源透明性,系统可靠性,执行并行性
8.分布式操作系统:分布性,透明性,同一性,全局性——鸿蒙OS

操作系统发展的主要动力

  1. 提高计算机系统资源的利用率
  2. 方便用户
  3. 器件不断更新迭代
  4. 计算机体系结构不断发展
  5. 不断提出新的应用需求

操作系统的基本特征

并发,共享,虚拟,异步

  1. 并发:指两个或多个事件在同一时间间隔内发生

  2. 共享:
    互斥共享——资源不多,要的人却多,而且每一个还要独占资源
    同时共享——要的人多,但是资源可以共用

  3. 虚拟:
    时分复用技术
    利用处理机的空闲时间来运行其他程序以提高处理机的利用率

    • 虚拟处理机技术:利用多道程序设计技术,为每道程序建立至少一个进程,使多道程序并发执行
    • 虚拟设备技术:将一台物理上的I/O设备虚拟为多台逻辑上的I/O设备,允许多个用户占用一台逻辑I/O设备

    空分复用技术
    利用存储器的空闲时间来存放其他程序以提高内存利用率

  4. 异步:
    因为资源有限,进程的执行不可能一气呵成,而会走走停停,因此每道程序总共需要多少时间运行完是不可知的

操作系统功能

这张图作为参考
操作系统的主要功能

第二章 进程的描述与控制

程序的并发执行

只有不存在前趋关系的程序才可以并发执行,否则无法并发执行——并发执行是为了使计算机资源利用率更高

  • 间断性:因为并发执行且共享资源,就涉及到在使用临界资源时需要涉及资源争抢,没拿到资源的暂停执行
  • 失去封闭性
  • 不可再现性失去封闭性导致失去再现性

进程的基本概念,进程实体的结构(三部分),基本特征

概念:进程是程序的执行过程,是系统进行资源分配和调度的一个独立单位
结构:程序段,相关数据,PCB
基本特征:

  1. 动态性:进程的实质是程序的执行过程,它是一直动态改变的——进程由创建而产生,由调度而执行,由撤销而消亡
  2. 并发性:指多个进程共存于内存中,且能在同一时间同时执行
  3. 独立性——参考进程的定义
  4. 异步性:进程是按异步方式运行的,即按各自独立的、不可预知的速度向前推进

PCB——进程管理表

  1. 概念:
    OS管理的控制表可以分为:内存表、设备表、文件表和进程表(用于进程管理)——PCB就是进程表
    PCB作用是使一个在多道程序环境下不能独立运行的程序(含数据),成为一个可以独立运行的基本单位,即一个能与其他程序并发执行的进程
  2. PCB的作用:
  • 作为独立运行的基本单位的标志
  • 实现间断性运行方式
  • 提供进程管理所需要的信息
  • 提供进程调度所需要的信息
  • 实现与其他信息的同步与通信
  1. PCB中的信息
    PCB中的信息
  2. PCB组织方式
  • 线性方式
  • 链接方式
  • 索引方式

进程的三种基本状态、状态转换的原因(重点)

  1. 就绪状态(ready):进程已经分配到除了CPU以外的所有资源,进入就绪队列等待调度
  2. 执行状态(running):被调度后获得CPU
  3. 阻塞状态(block):当进程触发了某个事件后无法继续执行,即阻塞。OS会把处理机分配给别的进程,而让阻塞进程进入阻塞队列——因为不同原因引起的阻塞会被分到不同的阻塞队列,提高效率
  4. 创建状态:首先由进程申请一个空白PCB,并向PCB中填写用于控制和管理进程的信息;然后由进程分配运行时所需的资源;最后,把进程状态转换为就绪状态将它插入就绪队列中
  5. 终止状态:首先等待OS进行善后处理;然后将进程的PCB清零,并将PCB空间返还给OS
    进程状态转换原因

挂起和引入挂起后的进程状态转换

挂起操作引入是为了:

  • 终端用户的需要
  • 父进程的需要
  • 负荷调节的需要
  • OS的需要
    挂起操作引入后的状态转换——引入挂起原语Suspend和激活原语Active
    综合后的状态转换图

进程的创建

  1. 申请空白的PCB——进程的创建需要分配资源和对信息进行管理,因此需要先创建PCB以方便系统对他管控
  2. 为新的进程分配其运行所需要的资源——包括各种物理资源和逻辑资源
  3. 初始化PCB
    • 初始化标志信息
    • 初始化处理机状态信息
    • 初始化处理机控制信息
  4. 如果进程就绪队列能够接纳新的进程,就将新进程插入就绪队列

进程的终止

调用进程终止原语,执行以下操作:

  1. 根据被终止进程的标识符,在PCB集合中检索出该进程PCB,从该进程PCB读出进程状态
  2. 若被终止进程处于执行状态就立即终止该进程的执行,并置调度标志为真,以指示该进程被中止后应该重新进行调度
  3. 若被终止进程有子孙进程也一并终止,防止它们成为不可控进程
  4. 将被终止进程的所有资源归还给父进程或OS
  5. 将被终止进程的PCB从所在队列(或者链表)中移出,等待其他程序来搜集信息

进程的阻塞与唤醒、挂起与激活实现

  1. 进程会通过调用阻塞原语block将自己阻塞——因此阻塞是进程自身的一种主动行为
  2. 当阻塞进程所期待的事件——即引起阻塞的事件在完成后,相关进程就会调用唤醒原语wakeup以将等待该事件的进程唤醒。
    block和weakup必须成对出现,否则可能造成某个进程的的永久阻塞
  3. 挂起:suspend原语
  4. 激活:active原语

进程通信方式

  1. 管道(Pipe):管道是一种半双工的通信方式,可以在具有亲缘关系的进程之间进行通信。管道可以是匿名管道(只能在父子进程间使用)或命名管道(可以在不相关的进程间使用)。
  2. 命名管道(Named Pipe):命名管道是一种通过文件系统中的命名对象进行通信的机制。不相关的进程可以通过打开和读写命名管道来进行通信。
  3. 共享内存(Shared Memory):共享内存是一种高效的进程间通信方式,通过将一块内存区域映射到多个进程的地址空间,使得这些进程可以直接读写共享内存。共享内存适用于需要频繁交换大量数据的进程间通信。
  4. 消息队列(Message Queue):消息队列是一种可以在不同进程间传递消息的通信方式。进程可以将消息发送到消息队列,其他进程可以从消息队列中接收消息。消息队列可以实现异步通信和解耦合。
  5. 信号量(Semaphore):信号量是一种用于进程间同步和互斥的机制。它可以控制对共享资源的访问,确保同时只有一个进程可以访问某个资源。(信号量机制会是一个重点,在后续会讲到)
  6. 套接字(Socket):套接字是一种网络通信的方式,可以用于不同主机上的进程之间进行通信。套接字提供了一种基于网络的进程间通信机制。

线程

  1. 概念:
    进程的目的是使多个程序能够并发执行,以提高资源利用率和系统吞吐量。
    线程则是为了减少程序在并发执行时所付出的时空开销,以使OS有更好的并发性
  2. 线程与进程的比较
    线程具有传统进程的很多特征,因此也被称为轻型进程(light-weight process,LWP)或进程元;相应的传统进程叫做重型进程(heavy-weight process,HWP),它只有一个线程的任务
    • 调度的基本单位:线程成为了计算机分配资源和调度的基本单位,且同一个进程中的线程切换所需开销远小于进程切换——也不会引起进程切换,但是当两个进程中的线程切换时会引起进程切换
    • 并发性
    • 拥有资源
    • 独立性
    • 系统开销
    • 支持多处理机系统
  3. 线程控制块
    TCB是线程控制块,相当于PCB
    • 线程标识符
    • 一组寄存器
    • 线程的执行状态
    • 优先级
    • 线程专有存储区——线程切换时存储信息
    • 信号屏蔽
    • 堆栈指针——过程调用,可能出现多重嵌套,,因此要为每个线程设置一个堆栈,还有两个指向堆栈的指针:指向用户自己堆栈的指针和指向核心栈的指针——分别对应线程运行在用户态和核心态时使用的堆栈

第三章 处理机调度与死锁

处理机调度的三个层次

  1. 高级调度——作业调度
    调度对象:作业
    调度过程:根据某种算法将外存上处于后备队列中的作业调入内存,为其创建进程和分配资源,然后让其在就绪队列等待调度
    调度应用:主要用于多道批处理系统
    无—>创建态—>就绪态 运行频率低
  2. 中级调度——内存调度
    调度对象:前情提要:当进程处于挂起状态时,会被从内存转移至外存
    调度过程:中级调度就是根据某种算法,在挂起队列中选择合适的进程,将它处于外存的数据调入内存运行
    挂起态—>就绪态 运行频率中
  3. 低级调度——进程调度
    调度对象:进程
    调度过程:根据某种算法决定就绪队列中的哪个进程获得处理机
    是操作系统中最基本的一种调度
    调度应用:主要用于多道批处理系统、分时、实时OS
    就绪态—>执行态 运行频率高

进程调度方式

非抢占式:—旦把处理机分配给某进程后,便让该进程—直执行,而绝不会因为时钟中断或其他原因去抢占该进程的处理机,直至该进程完成或发生某事件而被阻塞时,才再把处理机分配给其他进程。——实现简单,系统开销小但是无法及时处理紧急任务,适用于早实现简单,系统开销小但是无法及时处理紧急任务,适用于早期批处理系统

抢占式:允许调度程序根据某种原则,去暂停某个正在执行的进程,将已分配给该进程的处理机重新分配给另—进程。(现代OS广泛采用)抢占方式:允许调度程序根据某种原则,去暂停某个正在执行的进程,将已分配给该进程的处理机重新分配给另-进程.(现代OS广泛采用)——可以优先处理更紧急的进程,也可实现让进程按时间片被轮流服务。适合于分时操作可以优先处理更紧急的进程,也可实现让进程按时间片被轮流服务.适合于分时操作系统、实时操作系统

处理机调度的计算

CPU利用率

CPU利用率

系统吞吐量

在这里插入图片描述

周转时间

在这里插入图片描述

调度算法

FCFS——先来先服务算法

它按照作业到达的先后顺序进行调度,即先到先服务。在FCFS算法中,当一个作业进入就绪队列时,它会被放到队列的尾部,等待CPU调度执行。如果当前CPU正在执行一个作业,那么直到该作业完成后才会调度执行下一个作业。
优点:平均等待时间,平均周转时间更短。
缺点:对短作业有利,对长作业不利,如果有与源源不断的短进程加入到就绪队列时,长进程会长时间得不到服务,会产生饥饿现象,如果一直得不到服务则服务被“饿死”。

举例说明:日常生活买饭排队,谁先来谁排前面,不允许插队
在这里插入图片描述

SJF——短作业优先算法

算法思想:追求最短的平均等待时间、最短的平均周转时间、最短的平均带权时间。
非抢占式的短作业优先算法:有服务时间更短的进程到达就绪队列时,要等待运行中的进程执行完,才调度这个服务时间更短的进程。

举例说明:在幼儿园上车,会让按年龄从小到大排队,让最小的先上车,排好队之后,后面来的人就按照年龄的大小插入队伍中——当然这个队伍还都是没有上车的人,也不可能说让正在上车的人别上了重新排队。

抢占式的短作业优先算法:又叫做,最短剩余时间优先算法,即就绪队列中,哪个进程剩余运行时间更短,哪个进程先被调度。

举例说明(这个真挺难为人的):假设是在吃饭,只有一个凳子,当前正在坐的人还要十分钟才吃完,新来一个说只要3分钟吃完,那坐凳子的就只能给它让。如果两个进程执行时间是一样的,按照他们的先后顺序上分配处理机(一个垃圾的例子,不如不举)

优点:平均等待时间,平均周转时间更短。
缺点:对短作业有利,对长作业不利,如果有与源源不断的短进程加入到就绪队列时,长进程会长时间得不到服务,会产生饥饿现象,如果一直得不到服务则服务被“饿死”。

HRRN——高响应比优先算法

高响应比优先调度算法(Highest Response Ratio Next, HRRN)是一种基于SJF调度算法的进程调度算法,旨在兼顾长作业和短作业的优先级。
HRRN算法会计算每个进程的响应比(Response Ratio, RR),并按照RR从高到低的顺序进行调度。
RR = (等待时间 + 服务时间) / 服务时间 ——响应比大于等于1

RR——时间片轮转法

将所有就绪队列中的进程按照FCFS的顺序排列,然后每次取出队首的进程,分配一个时间片给该进程执行,当时间片用尽之后,将该进程放回就绪队列的队尾,取出队首的下一个进程执行。
这种机制可以保证所有进程都能够得到执行,且不会有某个进程一直占用CPU而导致其他进程饥饿的情况。
准则:时间片/10>进程上下文切换时间——切换进程的开销不能超过1%

PR——优先级调度算法

在优先级调度算法中,每个进程被赋予一个优先级,CPU在可执行进程中选择优先级最高的进程进行执行。当优先级相同时,通常采用轮转法或先进先出法进行调度

  1. 非抢占式
    简单来说就是对就绪队列中等待的进程按照优先级进行排序,只有当当前运行的进程主动放弃处理机时才对排序好的进程进行调度
  2. 抢占式
    在非抢占的基础上,还会对就绪队列进行“监控”,每当就绪队列改变就要检查是否需要发生抢占

注意:优先级有两种
静态优先级:优先级在进程创建时赋给,简单、开销小,但是死板,会出现饥饿
动态优先级:优先级在进程创建时赋给,随着进程推进或时间增加而改变
在这里插入图片描述

多级反馈队列调度算法

在多级队列调度算法中,进程被分为多个不同的队列,每个队列有不同的优先级,通常高优先级队列中的进程会被优先执行,低优先级队列中的进程则可能需要等待较长时间才能执行。进程可以根据其特征被分配到不同的队列中,例如CPU密集型进程可以被分配到高优先级队列中,而I/O密集型进程则可以被分配到低优先级队列中。

死锁的定义、产生原因和预防

死锁(Deadlock )∶指多个进程在运行过程中因争夺资源而造成的一种僵局,当进程处于这种僵持状态时,若无外力作用,这些进程都将永远不能再向前推进。

死锁产生的4个必要条件:

  1. 互斥条件:指进程对所分配到的资源进行排它性使用,即在一段时间内某资源只由一个进程占有 ;
  2. 请求和保持:进程至少持有一个资源,但又提出了新的资源请求,而该资源又被其他进程占有,此时请求进程被阻塞但又对自己占有的资源保持不放
  3. 不可抢占:进程所获得的资源在未使用完之间,不能被其他进程抢占,只能主动释放
  4. 循环等待条件:指在发生死锁时,必然存在一个进程——资源的环形链,即进程集合{P0,P1,P2,…,Pn}中的P0正在等待一个P1占用的资源,P1正在等待P2占有的资源,…,而Pn正在等待已被P0占有 的资源。

死锁的预防就是破坏死锁的四个必要条件中一个或多个——互斥是资源共享必须的,所以不能破坏反而要保证

死锁的避免和银行家算法

死锁的避免

一次封锁法:每个进程(事务)将所有要使用的数据全部加锁,否则,就不能继续执行;
顺序封锁法:预先对数据对象规定一个封锁顺序,所有进程(事务)都按这个顺序加锁;
银行家算法:保证进程处于安全进程序列。

银行家算法

核心思想∶在进程提出资源申请时,先预判此次分配是否会导致系统进入不安全状态。如果会进入不安全状态,就暂时不答应这次请求,让改进程先阻塞等待。
银行家算法(Banker’s Algorithm)是一种用于解决资源分配问题的算法,它用于确保一个进程在获得资源的同时不会导致系统进入不安全状态。主要思想是:在给每个进程分配资源前,通过检查系统的安全状态来确定是否可以满足该进程的需求。如果可以,则分配资源;如果不可以,则拒绝该进程的请求。
银行家算法的步骤如下:

  • 定义Max矩阵,表示每个进程最多可以获得的资源数量。
  • 定义Allocation矩阵,表示当前每个进程已经得到的资源数量。
  • 定义Need矩阵,Need[i][j]表示第i个进程还需要多少个资源j才能完成任务。Need[i][j] = Max[i][j] - Allocation[i][j]
  • 定义Available向量,Available[j]表示第j种资源当前可用的数量。
  • 检查系统是否处于安全状态。即检查Need[i] ≤ Available,如果都满足,则系统处于安全状态,可以分配资源;否则,说明系统进入不安全状态,无法分配资源。
  • 如果系统处于安全状态,则把请求资源分配给该进程。Allocation[i][j] = Allocation[i][j] + Request[i][j]; Available[j] = Available[j] - Request[i][j];
  • 重复步骤5和6,直到资源被全部分配完成。

银行家算法Java代码实现

package zhuxuanyu;

import java.util.Arrays;

/**
 * @title: BankerAlgorithm
 * @Author 竹玄羽
 * @Date: 2023/4/19 10:35
 */

public class BankerAlgorithm {
    
    
    private int[] available; // 可用资源向量
    private int[][] max; // 最大需求矩阵
    private int[][] allocation; // 已分配资源矩阵
    private int[][] need; // 需求资源矩阵
    private int[] work; // 工作向量,记录当前系统可用资源

    public BankerAlgorithm(int[] available, int[][] max, int[][] allocation) {
    
    
        this.available = Arrays.copyOf(available, available.length);
        this.max = Arrays.copyOf(max, max.length);
        this.allocation = Arrays.copyOf(allocation, allocation.length);
        this.need = new int[max.length][max[0].length];
        this.work = Arrays.copyOf(available, available.length);
        calculateNeed();
    }

    // 计算需求资源矩阵
    private void calculateNeed() {
    
    
        for (int i = 0; i < max.length; i++) {
    
    
            for (int j = 0; j < max[i].length; j++) {
    
    
                need[i][j] = max[i][j] - allocation[i][j];
            }
        }
    }

    // 判断系统是否处于安全状态
    public boolean isSafe() {
    
    
        boolean[] finish = new boolean[max.length];
        Arrays.fill(finish, false);
        int[] workCopy = Arrays.copyOf(work, work.length);

        int count = 0;
        while (count < max.length) {
    
    
            boolean found = false;
            for (int i = 0; i < max.length; i++) {
    
    
                if (!finish[i] && isLessOrEqual(need[i], workCopy)) {
    
    
                    finish[i] = true;
                    for (int j = 0; j < workCopy.length; j++) {
    
    
                        workCopy[j] += allocation[i][j];
                    }
                    found = true;
                    count++;
                }
            }
            if (!found) {
    
    
                break;
            }
        }

        return count == max.length;
    }

    // 判断资源请求是否安全
    public boolean isSafe(int processId, int[] request) {
    
    
        if (isLessOrEqual(request, need[processId]) && isLessOrEqual(request, available)) {
    
    
            int[] workCopy = Arrays.copyOf(work, work.length);
            int[][] allocationCopy = copy2DArray(allocation);
            int[][] needCopy = copy2DArray(need);

            for (int i = 0; i < request.length; i++) {
    
    
                workCopy[i] -= request[i];
                allocationCopy[processId][i] += request[i];
                needCopy[processId][i] -= request[i];
            }

            boolean[] finish = new boolean[max.length];
            Arrays.fill(finish, false);

            int count = 0;
            while (count < max.length) {
    
    
                boolean found = false;
                for (int i = 0; i < max.length; i++) {
    
    
                    if (!finish[i] && isLessOrEqual(needCopy[i], workCopy)) {
    
    
                        finish[i] = true;
                        for (int j = 0; j < workCopy.length; j++) {
    
    
                            workCopy[j] += allocationCopy[i][j];
                        }
                        found = true;
                        count++;
                    }
                }
                if (!found) {
    
    
                    break;
                }
            }

            if (count == max.length) {
    
    
                return true;
            } else {
    
    
                return false;
            }
        } else {
    
    
            return false;
        }
    }

    // 复制二维数组
    private int[][] copy2DArray(int[][] array) {
    
    
        int[][] copy = new int[array.length][array[0].length];
        for (int i = 0; i < array.length; i++) {
    
    
            for (int j = 0; j < array[i].length; j++) {
    
    
                copy[i][j] = array[i][j];
            }
        }
        return copy;
    }

    // 判断数组a是否小于等于数组b的每个元素
    private boolean isLessOrEqual(int[] a, int[] b) {
    
    
        for (int i = 0; i < a.length; i++) {
    
    
            if (a[i] > b[i]) {
    
    
                return false;
            }
        }
        return true;
    }

    public static void main(String[] args) {
    
    
        int[] available = {
    
    10, 5, 7};
        int[][] max = {
    
    {
    
    7, 5, 3}, {
    
    3, 2, 2}, {
    
    9, 0, 2}, {
    
    2, 2, 2}, {
    
    4, 3, 3}};
        int[][] allocation = {
    
    {
    
    0, 1, 0}, {
    
    2, 0, 0}, {
    
    3, 0, 2}, {
    
    2, 1, 1}, {
    
    0, 0, 2}};

        BankerAlgorithm bankerAlgorithm = new BankerAlgorithm(available, max, allocation);

        // 判断初始状态是否安全
        System.out.println("初始状态是否安全:" + bankerAlgorithm.isSafe());

        // 判断P1再请求(1,0,2)是否安全
        int[] request1 = {
    
    1, 0, 2};
        System.out.println("P1再请求(1,0,2)是否安全:" + bankerAlgorithm.isSafe(1, request1));

        // 判断P0请求(0,2,0)是否可分配
        int[] request2 = {
    
    0, 2, 0};
        System.out.println("P0请求(0,2,0)是否可分配:" + bankerAlgorithm.isSafe(0, request2));
    }
}

第四章 进程同步

互斥与同步

同步是一种合作关系,为完成某种任务而建立的多个进程或者线程之间的协调调用,次序等待,传递消息告知资源占用情况。
互斥是一种制约关系,当一个进程或者多个进程进入临界区后会进行加锁操作,此时其他进程(线程)无法进入临界区,只有当该进程(线程)使用后进行解锁其他人才可以使用,这种技术往往是通过阻塞完成。

临界资源和临界区

临界资源

  • 系统中某些资源—次只允许一个进程使用,称这样的资源为临界资源或互斥资源或共享变量。
  • 诸进程间应采取互斥方式,实现对这种资源的共享

临界区

临界区(Critical Section)是指在多线程或多进程的环境中,访问共享资源的那部分代码段或区域。在临界区内,对共享资源的访问是互斥的,同一时间只允许一个线程或进程进入临界区执行相关操作,其他线程或进程需要等待。
临界区的目的是确保对共享资源的并发访问是安全的,避免竞争条件(Race Condition)和数据不一致的问题。当多个线程或进程同时访问共享资源时,如果没有适当的同步机制保护临界区,可能会导致数据损坏、不一致的结果或无法预料的行为。

临界区问题

临界区问题是指在多线程或多进程环境下,对共享资源的访问可能引发的一系列并发问题,例如竞态条件(Race Condition)、死锁(Deadlock)、活锁(Livelock)等。
解决临界区的同步机制应遵循四条准则:
在这里插入图片描述

信号量机制

信号量

信号量(Semaphore)是一种经典的同步机制,用于控制对共享资源的访问。它是一种计数器,可以用来限制同时访问某个资源的线程或进程数量。
信号量主要有两种操作:P(等待,wait)和V(发信号,signal)。

  • P(等待)操作:当线程或进程要访问共享资源时,它首先要执行P操作,表示它希望获得访问权限。如果信号量的计数器大于0,即有可用资源,那么计数器减1,线程或进程可以继续执行并访问资源。如果计数器等于0,表示没有可用资源,那么线程或进程会被阻塞,进入等待状态。
  • V(发信号)操作:当线程或进程完成对共享资源的访问后,它执行V操作,表示它释放了资源。这会导致信号量的计数器加1。如果有其他线程或进程在等待资源,那么其中一个会被唤醒,并获得访问权限。
    在这里插入图片描述

整型信号量

用一个整数型的变量作为信号量,用来表示系统中某种资源的数量
有且仅有三种操作:初始化、P和V
缺点:进程忙等

//初始化整型信号量,表示当前的临界资源
int S = 1;

//P操作
void wait(int S){
    
    
	//检查临界资源是否被其他人持有
	while(S<=0);
	//如果资源满足就持有一个
	S--;
}

//V操作
void signal(int S){
    
    
	//使用完资源后释放资源
	S++;
}

记录型信号量

在并发编程中,记录型信号量(Record Semaphore)是一种特殊类型的信号量,也称为记账信号量。与传统的二元信号量或计数信号量不同,记录型信号量会跟踪每个线程对资源的使用情况,而不仅仅是计数。
操作:记录资源状态、P和V
每个信号量S除一个整数值S.value外,还有一个进程等待队列S.list,存放阻塞在该信号量的各个进程PCB

  • 信号量只能通过初始化和两个标准的原语PV来访问——作为OS核心代码执行,不受进程调度的打断
  • 初始化指定一个非负整数值,表示空闲资源总数(又称为”资源信号量”)- - 若为非负值表示当前的空闲资源数,若为负值其绝对值表示当前等待临界区的进程数
typedef struct{
    
    
	//剩余资源数
	int value;
	//等待队列
	struct process control_block *list;
}semaphore;

//请求一个资源
void wait(semaphore *S){
    
    
	//减少资源
	S->value--;
	if(S->value < 0 ){
    
    
		//进程自我阻塞
		block(S->list);
	}
}

//释放一个资源
void signal(semaphore *S){
    
    
	//资源增加一个
	S->value++;
	if(S.value <= 0 ){
    
    
		//唤醒等待队列中的一个进程
		wakeup(S.list);
	}
}

管程

一个管程定义了一个数据结构和能为并发进程所执行(在该数据结构上)的一组操作(函数),这组操作能同步进程和改变管程中的数据

  1. 互斥:
    • 管程中的变量只能被管程中的操作访问
    • 任何时候只有一个进程在管程中操作
    • 由编译器完成
  2. 同步:
    • 条件变量
    • 唤醒和阻塞操作

在这里插入图片描述

第五章 存储器管理

在这里插入图片描述

程序的装入与链接

在这里插入图片描述

程序的装入

  1. 绝对装入方式
    即编译时产生的地址是绝对地址——每次更改程序都需要重新编译
  2. 可重定位装入方式——静态重定位
    编译后的目标模块使用相对地址——在进程装入时一次性完成地址转换
  3. 动态运行时装入方式
    编译链接后的装入模块使用相对地址——装入内存后还是相对地址,需要重定位寄存器帮助

程序的链接

静态链接(Static Linking)

在静态链接中,编译器将所有目标文件和库文件的代码和数据合并到一个单一的可执行文件中。这样生成的可执行文件包含了所有需要的代码和数据,无需依赖外部的库文件,在执行时可以独立运行。静态链接的优点是简单、独立、无需外部依赖,但缺点是可执行文件较大,可能造成代码冗余和浪费。

动态链接(Dynamic Linking)

在动态链接中,编译器将程序的目标文件和库文件中的符号引用保留为未解析状态,直到程序运行时才由链接器将其与操作系统或运行时环境中的共享库文件进行链接。这样生成的可执行文件较小,只包含程序的代码和数据,共享库文件在运行时由系统加载和链接,从而减小了可执行文件的大小和内存占用。动态链接的优点是节省内存和磁盘空间,便于共享和更新,但缺点是需要依赖外部的共享库文件,并可能受到版本和兼容性的限制。

对换与覆盖

对换

对换(swapping)是一种将进程暂时从主存(RAM)中移到辅助存储器(如硬盘)中的技术,以便为其他进程腾出足够的主存空间或者满足内存资源的动态分配需求。对换技术允许操作系统根据进程的内存需求和系统资源的可用性,动态地将进程的部分或全部页面从主存中移出,从而优化系统的内存利用率。
步骤:

  1. 页面置换(Page Replacement):当主存空间不足时,操作系统需要选择一个或多个页面从主存中移出,以便为新的页面腾出空间。页面置换算法是一种决策算法,用于选择被置换的页面。常见的页面置换算法包括最佳(Optimal)、先进先出(FIFO)、最近最久未使用(LRU)、时钟(Clock)等。
  2. 页面调度(Page Scheduling):当需要将页面从主存移出时,操作系统需要将页面的内容写回到辅助存储器中,并记录页面的位置信息,以便将来需要时能够重新加载到主存。页面调度算法用于决定页面写回到辅助存储器的时机和顺序,以及页面重新加载到主存的时机和位置。
  3. 页面加载(Page Loading):当需要从辅助存储器加载页面到主存时,操作系统需要负责将页面的内容读取到主存中,并更新页面的状态信息。页面加载通常涉及到辅助存储器的读取操作和主存中页面的分配操作。

覆盖

覆盖是一种将进程的不同部分在内存中交替加载的技术。当内存不足以容纳整个进程时,操作系统可以将进程分为多个逻辑段(Overlay),每次只加载需要执行的部分,而其他部分则保留在外存中。当进程的不同部分之间发生切换时,操作系统会负责将合适的部分加载到内存中,并将上一个部分替换出去。

对换和覆盖是处理内存不足的两种主要技术。对换是将整个进程从内存移到外存,而覆盖是将进程的不同部分在内存中交替加载。它们在数据迁移方式、延迟、系统性能和程序员的工作负担等方面有所不同。操作系统会根据具体情况和策略选择适当的内存管理技术来优化内存利用和系统性能。

动态分区分配

数据结构

  1. 空闲分区表
    - 一个空闲分区占一个表目
    - 一个表目包括分区号,分区大小和分区起始地址
  2. 空闲分区链
    - 双向单链表结构

基于顺序搜索的动态分配算法

FF——首次适应算法

地址递增,保留了高地址的大的空闲分区
在这里插入图片描述

NF——循环首次适应算法

地址递增,空闲分区分布更均匀
在这里插入图片描述

BF——最佳适应算法

在这里插入图片描述

WF——最坏适应算法

在这里插入图片描述

基于索引的动态分配算法

快速适应算法

在这里插入图片描述

伙伴系统

在这里插入图片描述

哈希算法

在这里插入图片描述

内存回收

  1. 回收区与插入点的前一个空闲分区相邻接——将两个空闲分区合并,修改分区大小
  2. 回收区与插入点的后一个空闲分区相邻接——将两个空闲分区合并,修
    改起始地址、分区大小
  3. 回收区同时与插入点前、后两个空闲相邻接——将三个空闲分区合并,修
    改起始地址、分区大小
  4. 回收区的前、后都没有相邻的空闲分区——建立一个新的表项,填写回收区的起始地址和大小
    在这里插入图片描述

分页存储管理方式

页面和物理块

  1. 页面:分页存储管理将进程的地址空间分成若干个页,并未每页编号——即页号
  2. 页框:内存空间也分成若干个块,编号即为页框号
  3. 页面大小:应为2的幂

地址结构

在这里插入图片描述

页表

分页时,每个进程拥有一个页表,且页表驻留在内存中,记录页面和页框之间对应关系的数据结构

  1. 一个进程对应一张页表
  2. 进程的每一页对应一个页表项
  3. 每个页表项由“页号”和“块号”组成
  4. 页表记录进程页面和实际存放的内存块之间的对应关系

页表项的地址a=页表基址A+页号n×表项的字节数w

地址变换机构

系统设置一个页表寄存器存储页表在内存中的起始地址和页表长度在这里插入图片描述

例题

在这里插入图片描述

快表

在这个机制中,每一次的数据/指令存取需要两次内存访问,一次是访问页表,一次是访问数据/指令。解决两次访问的问题,是采用小但专用且快速的硬件缓冲,这种缓冲称为转换表缓冲器(TLB)或联想寄存器。
在这里插入图片描述
在这里插入图片描述

两级页表和多级页表

多级页表也即是在无限的套娃
在这里插入图片描述

分段式存储管理

优点

  1. 方便编程
  2. 信息共享
  3. 信息保护
  4. 动态链接
  5. 动态增长

基本原理

在分段存储管理方式中,作业的地址空间被划分为若干个段,每个段定义了一组逻辑信息
每个段都有自己的名字,通常用一个段号来代替段名,每个段都从0开始编址,并存储在一段连续的地址空间内
段的长度由相应的逻辑信息组的长度决定,因此各段长度不等
在这里插入图片描述

段表

类似于分页系统,在系统中为每个进程建立一张段映射表(段表),用于实现从逻辑段到物理内存区的映射。
每个段在表中占有一个表项记录了该段在内存中的起始地址(基址)和段的长度
段表保存在内存中由控制寄存器保存其地址

地址变换

在这里插入图片描述

分页分段区别

在这里插入图片描述
在这里插入图片描述

段页式存储管理

分段和分页原理的结合,即先将用户程序分成若干段,再把每个段分成若干个页,并为每个段赋予一个段名

结构

在这里插入图片描述

地址转换

在这里插入图片描述

第六章 虚拟存储器

局部性原理

指程序在访问存储器时,很可能会访问到离上一次访问比较近的数据或指令,而不是完全随机的访问存储器

  1. 时间局部性:指一个程序在某个时间访问某个存储单元的概率很高,也就是说,如果一个存储单元被访问了一次,那么在不久的将来它很可能会被再次访问。
  2. 空间局部性:指一个程序在某个时间访问某个存储单元的附近存储单元的概率很高,也就是说,如果一个存储单元被访问了一次,那么它附近的存储单元也很可能会被访问。

请求分页存储管理方式

请求页表机制

基本作用仍然是将用户地址空间中的逻辑地址映射为内存空间中的物理地址
在这里插入图片描述

  • 状态位(存在位)P:用于指示该页是否已调入内存
  • 访问字段A:用于记录本页在一段时间内被访问的次数
  • 修改位M:标识该页在调入内存后是否被修改过
  • 外存地址:用于指出该页在外存上的地址

缺页中断机制

在请求分页系统中,每当所要访问的页面不在内存时,便产生一缺页中断,请求OS将所缺之页调入内存。
缺页中断是一种特殊的中断,它与一般的中断相比有着明显的区别,主要表现在两个方面:

  • 指令执行期间产生和处理中断信号
  • 一条指令在执行期间可能产生多次缺页中断

地址变换机构

在这里插入图片描述

内存分配策略和算法

  1. 固定分配局部配置(Fixed Allocation with Local Replacement):每个进程都分配固定数量的页面,这些页面在进程执行过程中一直被占用。如果进程需要更多的页面,则必须等待其他进程释放它们。如果进程需要的页面数超出了它的固定分配页面数,则会出现“缺页中断”。
  2. 可变分配全局配置(Variable Allocation with Global Replacement):在这种策略中,所有进程共享一个可变大小的内存池。当进程需要页面时,操作系统会为其分配一定数量的页面,如果需要更多的页面,则可以从内存池中分配。如果内存池中没有足够的页面,则需要进行页面替换(置换)。
  3. 可变分配局部置换(Variable Allocation with Local Replacement):在这种策略中,每个进程可以从一个可变大小的内存池中分配页面。如果进程需要更多的页面,则可以从内存池中分配。如果内存池中没有足够的页面,则需要进行页面替换(置换)。与可变分配全局配置不同,每个进程拥有自己的页面替换算法,这意味着当一个进程需要进行页面替换时,只会考虑该进程中的页面,而不是所有进程中的页面。
  1. 平均分配算法:将系统中所有可供分配的物理块,平均分配给各个进程
  2. 按比例分配算法:根据进程的大小按比例分配物理块
    在这里插入图片描述
  3. 考虑优先权的分配算法:两部分,一部分按比例分配,一部分根据各个进程的优先权进行分配

缺页率

假设一个进程逻辑空间为n页,分配的内存物理块为m(m<=n),运行过程中访问页面成功的次数为S,访问失败的次数为F:
该进程总的页面访问次数:A=S+F
缺页率:f=F/A;
影响因素:

  1. 页面大小:页面较大则缺页率较低;反之则高
  2. 进程所分配物理块数:越多越低
  3. 页面置换算法
  4. 程序固有的特性

页面置换算法

最佳页面置换算法(OPT)

对于每个需要置换的页面,选择下一次最晚使用的时间最远的页面进行置换,即选择最久未被访问的页面进行置换。这样可以保证置换出去的页面不会再次被访问,从而最大限度地减少缺页次数。该算法是理论上的算法,也是一种最佳的置换算法,但实际上无法在实际的操作系统中实现
在这里插入图片描述

先进先出页面置换算法(FIFO)

先进先出页面置换算法(FIFO)是一种简单的页面置换算法,其核心思想是选择最早进入内存的页面进行替换。当需要替换页面时,选择最早进入内存的页面进行替换。由于 FIFO 算法只考虑页面进入内存的时间,而不考虑页面使用频率和未来使用概率等因素,因此其性能相对较差。
在这里插入图片描述

最少使用页面置换算法(LFU)

最少使用(Least Frequently Used,LFU)页面置换算法是一种根据页面被访问的频率来进行页面置换的算法。其原理是通过维护每个页面被访问的次数,从而找出访问频率最小的页面进行置换。

Clock页面置换算法

在Clock算法中,页面被保存在一个循环链表中,每个页面有一个被访问位(accessed bit)和一个修改位(modified bit)。
当需要置换页面时,算法从链表的头部开始扫描页面,如果页面的accessed bit为0,表示该页面没有被访问过,那么就置换该页面。如果accessed bit为1,表示该页面已经被访问过,那么将accessed bit清零,并将该页面移到链表的尾部。如果所有页面的accessed bit都为1,那么算法会从链表的头部开始扫描页面,找到第一个accessed bit为0的页面进行置换。
简单的CLOCK算法是给每一顿关联一个附加位,称为使用位。

  • 当一页装入物理块,访问位置1
  • 该页在物理块中且又被访间,仿问位置1
  • 页面替换后,指针指向被替换的下一页
  • 该页在物理块中,且又被访问,指针指向被访问的下一页
  • 访问位为0可替换页面,进行页面替换时,访问位为1则置0.
    总结:将 物理块看成个循环,访问位为0时表示可以替换,指针找到页面或置换页面访问位置1,指针指向下一个物理块
    (手绘真累)
    在这里插入图片描述

改进型Clock页面置换算法

改进型Clock页面置换算法引入了一个使用次数计数器,用于记录页面被访问的次数。除了使用次数计数器之外,该算法的工作方式与传统的Clock页面置换算法相同。当需要置换一个页面时,算法从当前指针位置开始查找。它首先搜索一圈:找到这一圈中最先被访问的且使用次数为0的页面。如果找到了这样的页面,则该页面被选中进行置换。如果没有找到,则算法进入第二个搜索阶段:选择这一圈中使用次数为1的页面进行置换。在第二个搜索阶段中,同样首先搜索一圈,然后在这一圈中选择使用次数为1的页面。
在这里插入图片描述

页面缓冲算法

页面缓冲算法是一种用于管理计算机内存中的页面或数据块的算法,它旨在最小化内存中的页面或数据块的数量,并确保最常用的页面或数据块总是可用于快速访问。该算法通常用于数据库管理系统中的缓存机制。
页面缓冲算法的主要思想是将内存分成固定大小的块,并在块中存储页面或数据块。当需要访问某个页面或数据块时,算法会首先查看该页面或数据块是否已经在内存中,如果是,则直接访问该页面或数据块;如果不是,则需要将该页面或数据块从磁盘读取到内存中,并将其存储在内存中的一个块中。

第七章 输入输出系统

I/O系统层次结构

在这里插入图片描述
设备驱动程序负责与硬件设备进行直接的交互,包括向设备寄存器写入命令或数据。
用户层软件负责对用户的权限进行管理和验证,包括检查用户是否有权使用特定的设备;设备驱动程序负责与硬件设备进行直接的交互,包括向设备寄存器写入命令或数据。
设备独立性软件负责处理与设备无关的操作,包括对缓冲区的管理、数据的缓存和调度等。

I/O系统接口

在这里插入图片描述

I/O设备和设备控制器

分类

在这里插入图片描述
设备并非与CPU直接通信,而是与设备控制器通信,因此,在设备与设备控制器之间应有一接口,在该接口中有三类信号各对应一条信号线。
在这里插入图片描述

设备控制器

功能

  1. 主要功能∶控制一个或多个IO设备,以实现l/O设备和计算机之间的数据交换
  2. 设备控制器是CPU与/O设备之间的接口,接收从CPU发来的命令,并去控制I/O设备工作
  3. 设备控制器是一个可编址的设备
    当仅控制一个设备时,它只有一个惟一的设备地址
    若控制器可连接多个设备时,则应含有多个设备地址,并使每一个设备地址对应一个设备

组成

设备控制器与处理机的接口:实现CPU与设备控制器之间的通信,包括数据线、地址线和控制线
设备控制器与设备的接口:一个设备控制器可以连接一个或多个设备
I/O逻辑:实现对设备的控制
在这里插入图片描述
驱动程序将抽象I/O命令转换成具体的命令和参数等装入设备控制器的相应寄存器,由控制器执行这些命令,具体实施对I/O设备的控制。

I/O通道

概念

I/O通道(I/O Channels)是一种用于在计算机系统中进行输入和输出操作的抽象概念。它提供了一种以统一的方式访问不同类型的输入和输出设备的机制,使得程序可以与这些设备进行交互。通过执行通道程序来控制I/0操作

类型

字节多路通道

在这里插入图片描述

数组选择通道

在这里插入图片描述

数组多路通道

在这里插入图片描述

瓶颈问题

原因:因为通道价格昂贵,通道不足造成导致瓶颈问题
解决方法:

  1. 增加设备到CPU间的通路而不增加通道
  2. 多通路方式不仅解决了“瓶颈”问题,而且提高了系统的可靠性

单通路与多通路对比

单通路

在这里插入图片描述

多通路

在这里插入图片描述

I/O设备的控制方式

轮询的可编程I/O方式

串行操作——全过程都需要CPU干预

中断的可编程I/O方式

并行操作——开始和结束才需要CPU干预

直接存储器访问(DMA)

块设备的主要I/O控制主要采用DMA方式

在这里插入图片描述

I/O通道方式

传输多个数据块才进行干预——需要CPU干预最少

中断和中断处理程序

中断

概念

中断是指在计算机执行过程中,发生了一个与当前正在执行的指令无关的事件,需要立即中断当前指令的执行,并转而处理该事件。当发生中断时,计算机会暂停当前的执行流程,保存当前的上下文(程序计数器、寄存器等),并跳转到中断处理程序中去执行相应的操作。中断机制可以提高计算机系统的并发性和响应性。
中断是由外部事件触发的,用于处理与当前指令无关的事件;而陷入是由程序内部触发的,用于进行系统调用或异常处理等操作

中断向量表

中断向量表存放每个设备的中断处理程序的入口地址,并为每个设备的中断请求作为一个中断号,对应于中断向量表中的一个表项

中断优先级

系统为每个中断源规定不同的优先级

多中断源处理

在这里插入图片描述

中断处理程序

在这里插入图片描述

设备分配与回收

分配

步骤

  1. 设备请求:进程向操作系统发出请求,申请某个特定设备或设备类的访问权限。
  2. 设备分配策略:操作系统根据设备的特性和当前系统状态,选择合适的设备分配策略。常见的策略包括先来先服务(FCFS)、最短作业优先(SJF)、优先级等。
  3. 设备分配:操作系统将所请求的设备分配给进程,并建立进程与设备之间的联系,使得进程可以使用设备进行输入输出操作。

设备分配算法

FCFS——先来先服务算法

它按照请求设备的顺序分配设备资源,即先到达的进程或用户先获得设备的使用权。
FCFS算法的工作原理如下:

  1. 当进程或用户请求使用设备时,操作系统将其加入设备请求队列的末尾。
  2. 如果当前设备空闲,操作系统将设备分配给队列中的第一个进程或用户,并开始执行设备操作。
  3. 当设备操作完成后,操作系统从队列中移除该进程或用户,释放设备资源,并将设备分配给下一个请求的进程或用户。
  4. 这个过程循环进行,直到设备请求队列为空。

FCFS算法的特点是简单直观,按照请求的先后顺序进行设备分配,公平且无偏好。然而,它可能会导致长作业等待时间增加,产生"饥饿"问题。因为如果一个长时间运行的进程先请求设备,后续的短作业可能会被阻塞等待较长时间。

HPF——最高优先级优先算法

它根据进程或作业的优先级来决定设备资源的分配顺序。具有最高优先级的进程或作业将首先获得设备的使用权。
HPF算法的工作原理如下:

  1. 当进程或作业请求使用设备时,操作系统会根据其优先级将其插入到相应的优先级队列中。通常,优先级较高的进程会被插入到队列的前面。
  2. 如果当前设备空闲,操作系统将设备分配给具有最高优先级的进程或作业,并开始执行设备操作。
  3. 当设备操作完成后,操作系统可能会继续分配设备给同一优先级队列中的下一个进程或作业,或者根据调度策略决定是否切换到更高优先级队列中的进程或作业。
  4. 这个过程循环进行,直到所有队列为空或系统中没有更高优先级的进程或作业。

HPF算法的特点是根据优先级来进行设备分配,优先级高的进程或作业具有较高的执行优先权。它适用于需要快速响应高优先级任务的场景,如实时系统。然而,如果系统中存在长时间运行的低优先级任务,可能会导致低优先级任务长时间等待设备资源,造成资源浪费或低优先级任务的延迟。

回收

  1. 设备释放:进程使用完设备后,将设备释放并通知操作系统。
  2. 设备回收策略:操作系统根据设备的回收策略,决定是否立即回收设备资源,或者延迟回收以供其他进程使用。
  3. 设备回收:操作系统回收已释放的设备资源,并将其标记为可用状态,以便其他进程可以再次请求使用。

假脱机系统

SPOOLing——假脱机技术

为了缓和CPU的高速性与I/O设备的低速性间的矛盾而引入了脱机输入脱机输出技术。

  • 利用一个程序模拟脱机输入时的外围控制机功能,把低速I/O设备上的数据传送到高速磁盘上
  • 用另一道程序模拟脱机输出时外围控制机的功能,把数据从磁盘传送到低速输出设备

外围操作与CPU对数据的处理同时进行,这种在联机情况下实现的同时外围操作称为SPOOLing(Simultaneous Peripheral Operations On-Line),或假脱机技术

系统组成

在这里插入图片描述
在这里插入图片描述

SPOOLing系统工作原理

在这里插入图片描述

特点

  1. 提高了I/O速度
  2. 将独占设备改造为共享设备
  3. 实现了虚拟设备功能

SPOOLing技术——打印机例题

打印机属于独占设备。利用SPOOLing技术,可将之改造为一台可供多个用户共享的设备,从而提高设备的利用率,也方便了用户。
在这里插入图片描述
当多个用户进程提出输出打印的请求时,系统会答应它们的请求,但并不会立即把打印机分配给他们,而是由假脱机管理进程完成如下工作:

  1. 在磁盘缓冲区为进程申请一个空闲缓冲区并将要打印的数据送入其中暂存;
  2. 为用户进程申请一张空白的用户请求打印表,并将用户的打印要求填入其中,再将该表改到假脱机文件队列上;
  3. 当打印机空闲时,由假脱机打印进程将假脱机文件队列的队头取出一张打印请求表,然后根据表中要求将要打印的数据由输出井传送到输出缓冲区,在交付打印机进行打印;
  4. 用这种的方式一次处理全部的打印任务

系统主要包含三部分:

  1. 磁盘缓冲区
  2. 打印缓冲区
  3. 假脱机管理进程和假脱机打印进程

缓冲区

缓冲区管理的主要目标是优化数据的读取和写入操作,以减少对底层存储设备的访问次数和延迟。它通过以下几个方面来实现:

  1. 缓存:缓冲区管理使用缓存机制,将频繁访问的数据缓存到内存中,以提供快速访问。当应用程序需要读取数据时,首先在缓冲区中查找,如果找到则直接返回,减少了对底层存储设备的访问。
  2. 预读和预写:缓冲区管理可以通过预读和预写来提前将数据加载到缓冲区或将数据写入到存储设备,以减少实际读写操作的延迟。预读可以在应用程序访问数据之前预先将数据加载到缓冲区,提高数据的可用性和响应速度。预写可以将数据缓冲到内存中,然后定期将数据批量写入存储设备,减少实际写入操作的次数和延迟。
  3. 缓冲区替换策略:当缓冲区空间有限时,缓冲区管理需要选择合适的缓冲区替换策略来决定哪些数据应该留在缓冲区中,哪些数据应该被替换出去。常见的缓冲区替换策略包括最近最少使用(LRU)、先进先出(FIFO)和最不常用(LFU)等。
  4. 同步和异步操作:缓冲区管理可以通过同步和异步操作来控制数据的读取和写入。同步操作要求应用程序等待数据的读取或写入完成后再继续执行,而异步操作则允许应用程序继续执行其他任务,而不必等待数据操作完成。

总的来说,缓冲区管理在操作系统中起着至关重要的作用,它通过合理地管理缓冲区资源,提高数据访问的效率和性能,从而改善系统的整体运行效果。

功能

  1. 缓和CPU与I/O设备间速度不匹配的矛盾
  2. 减少对CPU的中断频率,放宽对CPU中断响应时间的限
  3. 解决数据粒度不匹配的问题提高CPU与I/O设备之间的并行性

分类

单缓冲
双缓冲
环形缓冲
缓冲池

磁盘

磁盘的结构

盘面(磁头):磁盘设备可包含一或多个盘片,每个盘片分为一个或两个盘面,每个面上有一个读写磁头
磁道(柱面):每个盘面可分成若干条磁道
扇区:每条磁道逻辑上分成若千个大小相同的扇区。每个扇区的大小相当于一个盘块(数据块)
每条磁道上可存储相同数目的二进制位
磁盘密度即每英寸中所存储的位数,显然是内层磁道的密度较外层磁道的密度高。

磁盘访问时间

在这里插入图片描述

磁盘调度算法

磁盘调度算法是指操作系统中用于决定磁盘访问请求顺序的算法。磁盘调度算法的目标是优化磁盘的访问效率,减少磁盘访问的平均响应时间和等待时间——因此,磁盘调度的目标,是使磁盘的平均寻道时间最小]

先来先服务(FCFS)

最简单的磁盘调度算法
根据进程请求访问磁盘的先后次序进行调度
优点:公平、简单,每个进程的请求都能依次得到处理,不会出现某进程的请求长期得不到满足的情况
缺点:由于未对寻道进行优化,致使平均寻道时间可能较长
FCFS仅适用于请求磁盘I/O的进程数目较少的场合
如果请求访问的磁道比较集中,算法性能尚可;如果请求访问的磁道很分散,算法性能就会很差

最短寻道时间优先(SSTF)

选择离当前磁道最近的请求进行处理。这种算法可以减少磁头的寻道距离,提高磁盘访问效率。
性能较好,平均寻道时间短可能产生“饥饿”现象

举例说明:需要访问的磁道号为,10,28,48,39,100,124,37,69当前磁头在50
那么根据SSTF,就会访问离它最近的48号,访问48号后,再看谁离48近就访问谁——就近原则,因此可能导致距离磁头较远的磁道很久才被访问,产生饥饿

扫描算法(SCAN)

磁头按照一个方向依次访问磁道,直到到达磁盘的边界,然后改变方向继续访问。这种算法可以避免频繁的磁头移动,但可能导致某些磁道的访问延迟较高。

举例说明:需要访问的磁道号为,10,28,48,39,100,124,37,69当前磁头在50,且磁头向磁道号增长的方向移动
这时候就要给磁道号排序,从小到大,再从当前磁道号给分隔开来,沿着磁头移动方向访问,到边缘了在逆向访问:
排序:10,28,37,39,48,69,100,124
访问顺序:69,100,124,48,39,37,28,10

循环扫描算法(C-SCAN)

类似于扫描算法,磁头朝某个特定方向移动,随着移动不断的处理请求。不过,当磁头移动最外欲访问磁道,马上返回到最里欲访问磁道,返回时并不处理请求。将柱面当作—个环链,将最后柱面和第一柱面相连。这样可以使磁道的访问时间更加均衡。

举例说明:需要访问的磁道号为,10,28,48,39,100,124,37,69当前磁头在50,且磁头向磁道号增长的方向移动
这时候就要给磁道号排序,从小到大,再从当前磁道号给分隔开来,沿着磁头移动方向访问,到边缘了在逆向访问:
排序:10,28,37,39,48,69,100,124
访问顺序:69,100,124,10,28,37,39,48

最佳(最优)算法(OPT)

根据未来的磁盘访问情况,选择最优的磁道进行访问。这种算法需要预测未来的请求,因此在实际中很难实现,但可以作为磁盘调度算法的理想标准进行评估。

NStepScan调度算法和FSCAN调度算法

在这里插入图片描述

第八章 文件管理系统

文件系统层次结构

  1. I/O控制层:文件系统的最底层,由磁盘驱动程序组成
  2. 基本文件系统:用于实现内存与磁盘之间数据块测交换
  3. 文件组织模块:完成与磁盘I/O有关的事务
  4. 逻辑文件系统:处理和记录与文件有关的操作
    在这里插入图片描述

文件逻辑结构

分类

在这里插入图片描述

顺序文件

排列方式

串结构∶按存入时间的先后排序,记录间的顺序与关键字无关——检索比较费时
顺序结构∶指定一个字段为关键字,所有记录按关键字排序——检索时可利用有效的查找算法,折半查找法、插值查找法、跳步查找法等

优缺点

优点:
有利于大批记录读写存取效率最高
可存储在顺序存储设备(磁带)上
缺点:
查找或修改单个记录:差
增加或删除一个记录∶难
例,一个顺序文件有10000条记录,则采用顺序查找法查找到一个指定的记录,平均需要查找5000次。
(统计结果)

记录寻址

  1. 隐式寻址
    Rptr、Wptr为读、写指针,L为记录的长度
    定长记录︰下一条记录的地址Rptr=Rptr+L / Wptr=Wptr+L
    变长记录∶下一条记录的地址Rptr=Rptr+L; / Wptr=Wptr+Li
  2. 显式寻址方式
    通过文件中记录的位置
    利用关键字
    在这里插入图片描述

索引文件

  1. 按关键字建立索引
    为变长记录文件建立一张索引表
    索引表按关键字排序
    实现直接存取
    本身是一个定长记录的顺序文件
  2. 具有多个索引表的索引文件
    为顺序文件建立多个索引表
    为每一个可能成为检索条件的数据项配置一张索引表

索引顺序文件

有效克服了变长记录文件不便于直接存取的缺点。
保留了顺序文件的关键特征∶记录按关键字的顺序组织。
一级目录平均访盘次数=1/2盘块数
在这里插入图片描述
在这里插入图片描述

文件目录

文件目录也是一种数据结构,用于标志系统中的文件及其物理地址,供检索使用——主要实现按名存取

文件控制块(FCB)和索引节点

文件控制块(FCB)︰管理和控制文件的数据结构,与文件一一对应
文件目录∶文件控制块的集合,即一个文件控制块就是一个文件目录项
目录文件∶文件目录也被看做是一个文件

FCB包含的信息

  1. 基本信息类︰文件名、文件物理位置、文件逻辑结构、文件物理结构
  2. 存取控制信息类︰文件主的存取权限、核准用户的存取权限以及一般用户的存取权限
  3. 使用信息类∶文件的建立日期和时间、文件上一次修改的日期和时间及当前使用信息

索引节点

在这里插入图片描述

目录结构

单级目录:所有用户的文件都在同一目录中,整个系统只有一张目录表,每个文件占一个目录项
两级文件目录:

  • MFD:即主文件目录,记录用户名及相应用户文件目录的存放位置
  • UFD:用户文件目录,由该用户的文件FCB组成

树形结构:文件管理的树形目录结构是一种常见的文件组织方式,它使用树状结构来表示文件系统中的文件和目录之间的层次关系

树形结构

  1. 根目录(Root Directory):根目录是树形目录结构的顶层目录,它是整个文件系统的起点。根目录可以包含多个子目录和文件。
  2. 目录(Directory):目录是文件系统中用于组织和存储文件的容器。每个目录可以包含其他目录和文件,形成目录层次结构。目录可以用于分类、归类和管理文件,提供更好的组织和查找功能。
  3. 子目录(Subdirectory):子目录是位于其他目录中的目录,它扩展了目录层次结构。子目录可以进一步包含其他子目录和文件,形成更深的层次结构。
  4. 文件(File):文件是存储数据的实体,可以是文本文件、图像文件、可执行文件等。文件存储在目录中,可以通过目录层次结构来定位和访问。
  5. 路径(Path):路径是用于唯一标识文件或目录在树形目录结构中位置的字符串。它由从根目录到目标文件或目录的目录层次序列组成,通常使用斜杠(/)作为分隔符。
  6. 创建和删除目录:树形目录结构允许用户创建新的目录或删除现有的目录。目录的创建和删除可以通过操作系统提供的命令或文件管理接口进行。
  7. 移动和重命名文件或目录:树形目录结构允许用户移动文件或目录到不同的位置,或者修改文件或目录的名称。这样可以对文件和目录进行组织和管理,以适应不同的需求。
  8. 文件和目录权限管理:树形目录结构可以应用权限控制机制,限制对文件和目录的访问和操作。用户可以通过权限设置来保护敏感文件和目录的安全性。
    (有B树那味儿了)
    在这里插入图片描述

文件共享

  1. 有向无环图实现:树形目录中,允许一个文件有多个父目录,文件目录中包含文件物理地址等信息.利用索引结点实现共享,并设置链接计数count
  2. 符号链接实现:允许一个文件有多个父目录,但其中仅有一个作为主父目录,其它的都是通过符号链接方式与之相链接

文件保护

用访问矩阵来描述系统的访问控制

权限

  1. 复制权
    利用复制权将某个域所拥有的访问权access(ii)扩展到同一列的其他域中
  2. 所有权
    利用所有权(owner right)能增加某种访问权,或者能删除某种访问权
  3. 控制权
    用于改变矩阵内同一行(域)中的各项访问权,亦即,用于改变在某个域中运行的进程对不同对象的访问权

第九章 磁盘存储器管理

外存的组织方式

连续组织

为每个文件分配一组相邻接(连续)的磁盘块
优点:

  • 顺序访问容易- 仅需要起始块号和长度
  • 顺序访问速度快: 磁头移动距离最少——最快
  • 支持随机访问

缺点:

  • 要求分配连续的存储空间,会产生许多外碎片因此外存空间利用率低
  • 必须事先知道文件的长度
  • 不能灵活地删除和插入记录
  • 文件不能动态增长

链接组织

为文件分配不连续的盘块,通过链接指针将一个文件的所有盘块链接在一起,所形成的物理文件称为链接文件
链接方式:

  • 隐式链接:指针存放在每个盘块中,只适合于顺序访问
  • 显式链接:指针显式地存放在内存的文件分配表FAT中

优点:

  • 消除了磁盘的外碎片,提高了外存的利用率
  • 对插入、删除和修改记录都非常容易
  • 文件可动态增长

索引组织

系统为每个文件建立一个索引表,索引表中记录了文件对应的各个物理块。索引表存放的磁盘块称为索引块。文件数据存放的磁盘块称为数据块。

  1. 链接方式:如果索引表太大,一个索引块装不下,那么可将多个索引块链接起来存放。
    缺点: 若文件很大,索表很长,就要将很多个索引块链接起来要想找到i号索引
    块,必须先一次读入0~i-1号索引块,这就导致磁盘I/O次数过多,查找效率低下
  2. 多级索引:建立多层索引,使第一层索引块指向第二层的索引块。如果文件非常大,则还可采用三级、四级甚至更多级索引方式。若采用K级索引结构,且顶级索引表未调入内存,则访问一个数据块只需要K+1次读磁盘操作
    缺点: 即使是小文件,访问一个数据块依然需要K+1次读磁盘
  3. 增量式索:多种索引方式的混合。例如,一个文件的顶级索引表中,既包含直接寻址(直接指向数据块),又包含一次间址( 指向单层索引表)、还包含二次间址(指向两层索引表)等
    优点: 对中小文件来说,访问一个数据块所需的度磁盘次数更少

文件存储空间的管理

  1. 空闲区表法
    系统为外存上所有空闲区建立一张空闲表,属于连续分配方式
  2. 空闲链表法
    在这里插入图片描述
    在这里插入图片描述
  3. 成组链接法
    在这里插入图片描述
  4. 位示图法
    位示图比较重要,单独拿出来说

位示图法

利用二进制的一位来表示磁盘中一个盘块的使用情况
- 当值为0时,表示对应的盘块空闲
- 当值为1时,表示已分配

磁盘上的所有盘块都有一个二进制位与之对应,这样,由所有盘块所对应的位构成一个集合,称为位示图
在这里插入图片描述
盘块号 : b=n(i-1)+j —— n代表每行的位数
b号盘块号对应的行号 i 和列号 j
i=(b-1)/n+1
j=( b-1)/n+1

盘块的分配

  1. 顺序扫描位示图,从中找出一个或一组其值为0的二进制位
  2. 将所找到的一个或一组二进制位,转换成与之相应的盘块号。假定找到的其值为0的二进制位,位于位示图的第i行、第列,则其相应的盘块号为:b=n(i-1)+j
  3. 修改位示图,令map[i,j]=1

盘块的回收

  1. 将回收盘块的盘块号b转换成位示图中的行号和列号
    i = (b - 1 ) / n + 1
    j=(b - 1 ) % n + 1
  2. 修改位示图,令map[i , j] = 0

操作系统涉及的公式操作系统涉及的公式

猜你喜欢

转载自blog.csdn.net/weixin_53811934/article/details/131404363
今日推荐