操作系统基础

操作系统基础

操作系统概述

操作系统的定义

操作系统是指控制和管理整个计算机系统的硬件和软件资源,并合理地组织调度计算机的工作和资源分配并提供用户与其他软件方便的接口和环境,它是计算机系统中最基本的系统软件。
作为系统资源的管理者主要是对处理机、内存、文件、设备来进行管理。目标是用户能够安全高效的使用系统资源。
作为用户与计算机硬件之间的接口操作系统提供了命令接口与程序接口来供用户方便的使用。其实命令接口分为联机、脱机命令接口允许用户直接调用,程序接口用户只能通过程序间接调用。

操作系统的特征

并发指两个或多个事件在同一时间段内同时发生,但从微观上看却是交替发生。
共享指系统中的资源可供内存中多个并发执行的进程共同使用。共享又分为互斥共享(摄像头)与同时共享(硬盘)两种。
虚拟指把一个物理上的实体变为若干个逻辑上的对应物。比如说虚拟处理器,虚拟内存,虚拟外部设备
异步是指在多道程序环境下,允许多个程序并发执行,但由于资源有限,进程的执行不是一贯到底,而是走走停停,以不可预知的速度向前推进。
并发与共享是操作系统最基本的特征两者互为存在条件,没有了并发共享就没有意义,而没有共享也不能实现并发。

进程与调度

进程

进程的定义

进程是程序的一次执行过程。
进程是具有独立功能的程序在数据集合上运行的过程,它是系统进行资源分配的单位

进程的组成

程序段数据段PCB组成进程实体,一般情况下进程实体简称为进程。进程的创建实际上是进程实体PCB的创建,进程的撤销就是**PCB*的撤销。PCB主要存放进程的描述信息、进程的控制和管理信息、资源分配清单、处理机相关信息等。

进程的状态与转换

创建态:进程正在被创建,操作系统为进程分配资源、初始化PCB。
就绪态:已具备运行的条件,但没有空闲CPU,暂时不能运行。
运行态:占有CPU正在运行。
阻塞态:因等待某一事件的发生,暂时不能运行。
终止态:进程运行的结束或者因为bug导致进程无法继续执行。

在这里插入图片描述

进程通信

共享存储
设置一个共享空间,互斥的进行访问。
管道通信
设置一个共享文件(管道),其实就是一个缓冲区。一个管道只能实现半双工通信,实现双向通信需要建立两个管道。各进程互斥的进行访问,没写满时不能读,没读空时不能写。
消息传递
传递结构化的消息(消息头、消息体)。可以分为直接通信与信箱通信方式。

线程

线程的定义

线程最直接的理解就是“轻量级进程”,它是一个基本的CPU执行单元,也是程序执行流的最小单元。引入线程,是为了减小程序在并发执行时所付出的时空开销,提高操作系统的并发性能。
线程不拥有系统资源,线程也没有自己独立的地址空间,但它可以访问进程所拥有的系统资源并且共享进程所属的的空间。

线程的实现方式与多线程模型

用户级线程
在这里插入图片描述

内核级线程
在这里插入图片描述

多对一模型
一个内核级线程对应多个用户级线程。每个用户进程只对应一个内核级进程。
用户级线程的切换用户即可完成,效率比较高。但是当内核级线程阻塞时,整个进程都会被阻塞,并发能力较弱。
一对一模型
一个内核级线程对应一个用户级线程。用户级线程的切换需要操作系统内核来完成开销比较大,但并发能力比较强。
多对多模型
多个内核级线程对应多个用户级线程。对上述两者的折中,比较均衡。

处理机调度

处理机调度层次与七状态模型

高级调度
高级调度是创建进程的过程。外存与内存之间的调度。每个作业只会调入和调出各一次,作业调入时会为其分配内存等必要资源并建立相应的PCB,调出时撤销PCB。
中级调度
当内存的空间不足时,可以将暂时不能运行的进程调出内存在外存中进行等待。直到它重新获取运行资格且内存稍有空闲时才重新调入内存,但进程的PCB则常驻内存。中级调度(内存调度)就是决定将那个处于挂起状态的进程重新调入内存。其中中级调度调入调出可以是多次。
低级调度
低级调度就是从就绪队列中选取一个进程,将处理机分配给它。低级调度调入调出极为频繁。
七状态模型
在这里插入图片描述

进程调度的时机与过程

调度时机
主动放弃:进程正常终止、运行过程中发生异常、主动阻塞。
被动放弃:分配的时间片用完、有更紧急的事情需要处理(如I/O中断)、有优先级更高的进程进入就绪队列。
当进程主动放弃CPU进行调度属于非抢占式调度,当进程被动放弃CPU进行调度属于抢占式调度。
不能进行调度的时机
在处理中断的过程中、进程在操作系统内核程序临界区中、原子操作过程中。
切换过程
对原先运行进程的各种数据进行保存,对新进程的各种数据进行恢复。

调度算法的评价指标

在这里插入图片描述

调度算法

先来先服务
按照作业/进程到达的先后顺序进行服务是一种非抢占式算法。可以用于作业调度与进程调度。用于作业调度时,应考虑那个作业先到达后备队列;用于进程调度应考虑那个进程先到达就绪队列。该算法对长作业有利对短作业不利并且不会导致饥饿现象。
短作业优先
按照作业/进程需要服务的时间长短进程服务可以是抢占式也可以是非抢占式,抢占式的算法叫做最短剩余时间优先算法。短作业优先算法可以得到最短的平均等待时间与周转时间。该算法对短进程有利,当有源源不断的短进程出现,长进程可能会被饿死。
高响应比优先
按照作业/进程的响应比(响应比 = 1 + 等待时间 / 要求服务时间)进行服务,高响应比优先,可以进行作业和进程调度,该算法属于非抢占式算法。该算法综和考虑了短作业优先与先来先服务算法的优缺点,比较均衡。不会导致进程饥饿。
时间片轮转
按照各个进程到达就绪队列的顺序,轮流的让各个进程执行一个时间片。若进程未在一个时间片内执行完,则剥夺处理机,将进程重新放到就绪队列的末尾重新排队。该算法只用于进程调度,是一种抢占式的算法。不会导致饥饿现象,但是由于高频率的进程切换会导致一定的额外开销,而且不能区分紧急任务。
优先级调度
按照各个进程的优先级进行调度。可以用于进程与作用的调度。有抢占式和非抢占式两种,主要区别是抢占式是当就绪队列发生改变时就会检查是否应该抢占,而非抢占式只有当进程主动放弃处理机时才会进行进程的调度。该算法可以用于进程和作用调度。该算法的优点是可以区分紧急任务,可灵活的调整各种进程/作业的偏好程度。但是当大量的高优先级进程涌入时,优先级低的进程可能会被饿死。
多级反馈队列
设置多级就绪队列,各级队列优先级从高到低,时间片从小到大。新进程到达时先进入第1级队列,按FCFS原则排队等待被分配时间片,若用完时间片进程还未结束,则进程进入下一级队列队尾。如果此时已经是在最下级的队列,则重新放回该队列队尾。只有第k级队列为空时,才会为k+1级队头的进程分配时间片。该算法只用于进程调度。当k级队列中的进程运行时,若更高优先级的队列出现新进程时,该新进程就会抢占处理机,原来的进程放回k级队列的队尾,所以该算法是抢占式的。该算法参考了优先级与时间片调度的优缺点,是一个比较完美的算法。但是当有源源不断的短进程时,长进程可能会导致饥饿。

进程同步与互斥

互斥是指某一个时间段内只能允许一个进程访问某种资源。例如对临界区的访问就需要互斥的进行。
同步是指有的进程之间需要配合的工作,各个进程的工作推进需要有一定的先后顺序。有时需要利用进程同步来解决进程的异步问题。

互斥的四个部分与遵守的原则

四个部分

  1. 进入区。检查临界区是否可以进去,若可以进入,则需要上锁。
  2. 临界区。访问临近资源的代码。
  3. 退出区。负责解锁。
  4. 剩余区。其余代码部分。

遵守的原则

  1. 空闲让进。临界区空闲时,应允许进程访问。
  2. 忙则等待。临界区正在访问时,其余试图访问的进程需要等待。
  3. 有限等待。要在有限的时间内进去临界区,防止出现饥饿。
  4. 让权等待。进入不了临界区需要释放处理机,不能出现盲等。

进程互斥的实现方式

软件实现

在这里插入图片描述

硬件实现

在这里插入图片描述

信号量实现

信号量是一个变量,它代表了一个系统中某种资源的数量。
由于软件实现进程同步的缺点主要是不能在进去区种将各种操作一气呵成的执行导致的。由于原语这一特殊的程序段可以一气呵成的执行,所以我们可以用原语来对信号量进行操作(这时就出现了P、V操作)来解决进程同步问题。

整数型信号量
// 初始化信号量,S = 1可以表示系统中某种资源的数量为1(例如打印机)。
int S = 1;

void P(int S){    //P原语,相当于进去区。是对资源的申请。
  while(S <= 0); //如果资源不够,则一直循环等待。(这时违反了让权等待原则)
  S = S - 1;    //如果资源数够,则占用一个资源。
}

void P(int S){ //V原语,相当于退出区。是对资源的释放。
  S = S + 1;  //使用完资源后,在退出区对资源进行释放。
}

整数型信号量可以解决进程的互斥问题,但是它可能会违反让权等待原则。

记录型信号量
// 定义了记录型信号量semaphore。
typedef struct{
  int value;               //剩余资源数
  struct process *L;      //等待队列
}semaphore;

void P(semaphore S){
  S.value--;        //当申请资源时无论是否有该资源,都将剩余资源数减一。
  if(S.value < 0){ //当剩余资源数小于0时执行block原语。
    block(S.L);   //当value<0时,意味着没有剩余的该资源,因此该进程无法继续执行。block原语就是将该进程从运行态进入阻塞态,并加入到S的等待队列L中去。
  }
}

void S(semaphore S){
S.value++;         // 释放资源,资源的剩余数加一。
if(S.value <= 0){ // 判断资源剩余数是否大于0,小于等于0则执行wakeup原语。
    wakeup(S.L); // 当value的值小于等于0时,这时意味这等待队列中有需要该资源的进程,这时该原语会为队列中的一个进程分配资源,并将该进程从阻塞态变为就绪态。
  }
}

记录型原语解决了整数型原因的问题,是一个比较完美实现进程互斥的方法。

信号量实现进程同步与前驱

同步

在这里插入图片描述

前驱

在这里插入图片描述

信号量实现的应用

解决这些问题需要从资源的角度定义出信号量,并且分析出来进程之间的同步与互斥关系。并利用这些信号量的P、V操作进行解决。

生产者-消费者问题

系统中有一组生产者进程和一组消费者进程,生产者进程每次生产一个产品放入缓冲区,消费者进程每次从缓存区中取出一个产品并使用。生产者、消费者共享一个初始为空、大小为n的缓冲区。只有当缓冲区没满时,生产者才能把产品放入缓冲区,否则必须等待。只有当缓冲区不为空时,消费者才可以从中取出产品,否则必须等待。缓冲区是临界资源,各进程必须互斥地进行访问。

semaphore mutex = 1;  //互斥信号量,实现对缓冲区的互斥访问。
semaphore empty = n; //同步信号量,表示空闲缓冲区的数量。
semaphore full = 0; //同步信号量,表示产品的数量,也即非空闲缓冲区的数量。

//生产者进程
producer(){
  while(1){
    生产产品
    p(empty);
    p(mutex);
    将产品放入缓冲区; //代码块1
    v(mutex);
    v(full);
  }
}

//消费者进程
consumer(){
  while(1){
    p(full);
    p(mutex);
    将产品从缓冲区中取出;//代码2
    v(mutex);
    v(empty);
    使用产品;
  }
}

该题目中有一对互斥关系和两对同步关系。其中消费者和生产者对缓冲区的访问是一对互斥关系。解决互斥关系需要将一个信号量值设置为1,在访问临界区时执行P操作,访问完成后执行V操作。当缓冲区为空时,需要先执行代码块1后才可以执行代码块2。当缓冲区满时,需要先执行代码块2后才可以执行代码块1。这两者是两个同步关系。所以根据同步的实现原理,需要在各自的代码块前执行P操作,在代码块后执行V操作,来实现两者的同步关系。
需要注意的是代码块执行前的两个P操作顺序不能颠倒,颠倒顺序可能会出现死锁现象。代码块执行后的两个V操作顺序是可以颠倒的,因为V操作不会引起进程的阻塞。

多生产者-多消费者问题

桌上有一只盘子,每次只能向其中放一只水果。爸爸专向盘子里面放苹果,妈妈专向盘子里放橘子,儿子专等着吃盘子中的橘子,女儿专等着吃盘子中的苹果。只有盘子为空时,爸爸或妈妈才可以向盘子中放一个水果。仅当盘子中有自己喜欢吃的水果时,儿子或者女儿才可以从盘子中取出水果。

semaphore mutex = 1;   //实现互斥的信号量。
semaphore apple = 0;  //盘子中有几个苹果。
semaphore orange = 0;//盘子中有几个苹果。
semaphore plate = 1;//盘子中可以放几个水果。

dad(){
  while(1){
     准备苹果;
      p(plate);
      p(mutex);
      将苹果放入盘子;
      v(mutex);
      v(apple);
  }
}

mom(){
  while(1){
   准备橘子;
    p(plate);
    p(mutex);
    将橘子放入果盘;
    v(mutex);
    v(orange);
  }
}

daughter(){
  while(1){
    p(apple);
    p(mutex);
    将苹果从盘子中取出;
    v(mutex);
    v(plate);
    吃掉苹果
  }
}

son(){
  while(1){
    p(orange);
    p(mutex);
    将橘子从盘子中取出;
    v(mutex);
    v(plate);
    吃掉橘子;
  }
}

对缓冲区(盘子),四个进程需要互斥的访问。妈妈或爸爸需要在盘子为空才可以向盘子中放水果,而盘子为空这一事件需要儿子或者女儿来触发。反过来只有当盘子中有对应的水果,女儿或者儿子才可以去吃。值的注意的是没有mutex信号量实现互斥操作的话,该程序仍然是正确的,这是由于在任意时刻apple、orange、plate三个信号量只有一个为1其余两个为0,这时只有一个进程可以运行,这同时保证了进程的互斥与同步的要求。

吸烟者问题

假设一个系统有三个抽烟者进程和一个供应者进程。每个抽烟者进程不停地卷烟并且抽掉,但是要卷烟并抽掉一支烟,抽烟者需要三种材料:烟草、纸、胶水。三个抽烟者中,第一个拥有烟草、第二个用于纸、第三个拥有胶水。供应者每次将两种材料放在桌上、拥有剩下那种材料的抽烟者卷一根烟并且抽掉它,并给供应者进程一个信号告诉完成了,供应者就会放另外两种材料在桌子上,这个过程一直重复(让三个抽烟者轮流地抽烟)。

semaphore offer1 = 0;  //桌上的组合1
semaphore offer2 = 0;  //桌上的组合2
semaphore offer3 = 0;  //桌上的组合3
semaphore finish = 0;  //用于控制吸烟是否完成,这个也可以等价于卓上是否为空
int i = 0;             //通过i值来实现轮流抽烟

provider(){
  while(1){
    if(i == 0){
      将组合1放到桌上;
      v(offer1);
    }
      else if(i == 1){
      将组合2放到桌上;
      v(offer3);
    }
      else if(i == 2){
      将组合3放到桌上;
      v(offer3);
    }
    i = (++i)  % 3;
    p(finish); 
  }
}

smoker1{
  while(1){
    p(offer1);
    拿走组合1,卷烟,抽掉;
    v(finish);
  }
}

smoker1{
  while(1){
    p(offer2);
    拿走组合2,卷烟,抽掉;
    v(finish);
  }
}

smoker1{
  while(1){
    p(offer3);
    拿走组合3,卷烟,抽掉;
    v(finish);
  }
}

三个吸烟者和材料提供者四个进程需要互斥的访问桌子,彼此属于互斥关系。只有桌子上有材料,这三个吸烟者其中之一才可以吸烟。反过来只有当吸烟拿走了桌上的材料,材料提供者才可以往桌上放材料,这是两组同步关系。最开始进程只能先执行provider的代码块,并且provider进程会被阻塞到p(finish)这段原语,只有执行吸烟者的代码块并且执行完成v(finish)时,provider才可以继续执行,并且其余的吸烟者进程都会被阻塞。这样就同时实现了进程的同步与互斥。并且我们使用了对i取余来实现吸烟者的轮流吸烟。

读者-写者问题

有读者和写者进程,共享一个文件,当两个或两个以上的读者进程同时访问共享数据时不会产生副作用,但某个写进程和其他进程同时共享数据时则可能导致数据不一致的错误,因此要求:允许多个读进程同时对文件进行读操作;只允许一个写进程往文件里写信息;任一写者进程在完成操作之前不允许其他读者或写者进程工作;写者执行写操作前,应让已有的读者或者写者全部退出。

semaphore rw = 1;     //设置互斥信号量rw,表示当前是否有读/写进程访问文件。
semaphore mutex = 1; //设置互斥信号量mutex,使得对count的访问互斥的进行。
semaphore w = 1;    //设置互斥信号量,实现“写优先”。
int count = 0;     //表示当前访问文件的读进程数量。

//读者优先
writer(){
  while(1){
    p(rw);
    写文件;
    v(rw);
  }
}
reader(){
  while(1){
    p(mutex);
    if(count == 0);
      p(rw);
    ++count;
    v(mutex);
    读文件;
    p(mutex);
    --count;
    if(count == 0)
        v(rw);
     v(mutex);  
  }
}

//“写优先”
writer(){
  while(1){
    p(w);
    p(rw);
    写文件;
    v(rw);
    v(w);
  }
}

reader(){
  while(1){
    p(mutex);
    if(count == 0);
      p(rw);
    ++count;
    v(mutex);
    读文件;
    p(mutex);
    --count;
    if(count == 0)
        v(rw);
     v(mutex);  
  }
}

该问题中写者进程与其他进程(读/写进程)都互斥,读进程只与写进程互斥。这个问题的亮点是如何实现多个读者进程同时对文件的访问,我们利用count来记录读进程的数量来解决此问题。在“读优先”的实现中,有一个问题,就是当有读进程读取文件时,这时如果有源源不断的读进程出现,这时出现的写进程会被阻塞可能会出现饿死现象。这时我们可以设置互斥信号量w来解决此问题,来实现出“写优先”,所谓的写优先并不会出现可能会饿读进程的情况,这种“写优先“对读写双方都是相对公平的。

哲学家进餐问题

有五个哲学家,他们的生活方式是交替地进行思考和进餐。他们共用一张圆桌,分别坐在五张椅子上。
在圆桌上有五个碗和五支叉子,平时一个哲学家进行思考,饥饿时便试图取用其左、右最靠近他的叉子,只有在他拿到两支叉子时才能进餐。进餐完毕,放下叉子又继续思考。

semaphore fork[5] = {1, 1, 1, 1, 1}
philosoohr(int i){
  while(1){
    思考;
    p(fork[i]);
    p(fork[(i + 1) % 5]);
    吃饭;
    v(fork[i]);
    v(for[(i + 1) % 5]);
  }
}

改题目中哲学家旁边的两把叉子属于临界资源,一把叉子旁边的两个哲学家属于互斥关系。上面的实现,当所有哲学家同时拿去左边的叉子时,程序就会陷入死循环中,这种现象称为死锁。
我们可以改进该算法来解决此问题。

//最多只能四个人拿起筷子。
semaphore fork[5] = {1, 1, 1, 1, 1}
semaphore r = 4;
philosoohr(int i){
  while(1){
    思考;
    p(r);
    p(fork[i]);
    p(fork[(i + 1) % 5]);
    吃饭;
    v(fork[i]);
    v(for[(i + 1) % 5]);
    v(r);
  }
}
//最多只能四个人拿起筷子。
semaphore fork[5] = {1, 1, 1, 1, 1}
semaphore r = 4;
philosoohr(int i){
  while(1){
    思考;
    p(r);
    p(fork[i]);
    p(fork[(i + 1) % 5]);
    吃饭;
    v(fork[i]);
    v(for[(i + 1) % 5]);
    v(r);
  }
}

这只是其中一个解决方法,还有其他方法就不再写了。

死锁

死锁的概念

一组进程中,每个进程都无限等待被该组进程中另一进程所占有的资源,因而永远无法得到的资源,这种现象称为进程死锁,这一组进程就称为死锁进程。如果死锁发生,会浪费大量系统资源,甚至导致系统崩溃。

死锁产生的必要条件

(1)互斥使用(资源独占):一个资源每次只能给一个进程使用
(2)占有且等待(请求和保持,部分分配):进程在申请新的资源的同时保持对原有资源的占有
(3)不可抢占(不可剥夺):资源申请者不能强行的从资源占有者手中夺取资源,资源只能由占有者自愿释放
(4)循环等待:存在一个进程等待队列 {P1 , P2 , … , Pn},其中P1等待P2占有的资源,P2等待P3占有的资源,…,Pn等待P1占有的资源,形成一个进程等待环路。
当死锁产生的时候一定会有这四个条件,有一个条件不成立都不会造成死锁。

处理死锁的办法

死锁的预防

从死锁产生的必要条件出发,破坏其中一个就不会产生死锁现象。

  1. 破坏“互斥使用/资源独占”条件
  2. 破坏“占有且等待”条件
  3. 破坏“不可抢占”条件
  4. 破坏“循环等待”条件
死锁避免

在系统运行过程中,对进程发出的每一个系统能够满足的资源申请进行动态检查,并根据检查结果决定是否分配资源,若分配后系统发生死锁或可能发生死锁,则不予分配,否则予以分配,即在资源动态分配过程中,防止系统进入不安全状态,以避免死锁的发生。
利用银行家算法可以检查该进程是否处于安全序列。
安全状态:如果系统中存在一个由所有进程构成的安全序列P1,…,Pn,则称系统处于安全状态。

安全序列:一个进程序列{P1,…,Pn}是安全的,如果对于每一个进程Pi(1≤i≤n),它以后还需要的资源量不超过系统当前剩余资源量与所有进程Pj (j < i )当前占有资源量之和,则称系统处于安全状态。
注意:安全状态一定没有死锁发生。
不安全状态:系统中不存在一个安全序列。不安全状态一定导致死锁,但是可能目前还没有进入死锁,死锁是不安全状态的一个子集。

死锁检查与解除

死锁的检查
给每个进程、每个资源指定唯一编号;设置一张资源分配表记录各进程与其占用资源之间的关系;设置一张进程等待表记录各进程与要申请资源之间的关系。从资源等待表出发,看有没有形成等待的环路。即可以利用资源分配图的思想来检测是否有死锁发生。
死锁的解除
死锁避免的重要是以最小的代价恢复系统的运行,具体的死锁解除的方法有几个:(1)撤消所有死锁进程,代价较大;(2)进程回退(Roll back)再启动,进程在执行过程中,系统会为进程记录一些中间节点,当出现死锁的时候进行回退再一起重新运行,由于需要记录每个进程的现场,所以系统代价也比较大;(3)按照某种原则逐一撤消死锁进程,直到没有死锁;(4)按照某种原则逐一抢占资源(资源被抢占的进程必须回退到之前的对应状态),直到没有死锁。

内存管理

内存的基本概念

内存是存储数据的硬件。程序执行前需要先放到内存中才能被CPU处理。
内存会被分为一个个存储单元,一个存储单元对应一个地址。当计算机按字节编址时,一个存储单元大小为一个字节。如果按字编址一个存储单元的大小为一个字长。
存储单元的相对地址也叫逻辑地址,绝对地址也叫物理地址。

内存空间的地址转换

内存地址的转换就是存储单元相对地址到绝对地址的转换。

编程到运行代码的过程

编译:由编译程序将用户源代码编译成若干个目标模块(将高级语言变为机器语言)。
链接:由链接程序将编译之后形成的一组目标模块,以及所需的函数链接在一起,形成一个完整的装入模块。
装入:由装入程序将装入模块装入内存。

三种链接方式

静态链接:在程序运行之前,先将各个模块及所需的库函数连接成一个完整的可执行文件(装入模块),之后不再拆开。
装入时动态链接:将目标模块装入内存时,边装入边链接的链接方式。
运行时动态链接:在程序执行中需要该目标模块时,才将它进行链接。

三种装入方式

绝对装入:在编译时,如果知道该程序将要装入的内存地址,编译程序将会产生绝对地址的目标代码。绝对装入只适用于单道程序环境。
静态重定位:由于编译链接后的装入模块的逻辑地址都是从零开始的,指令中使用的地址、数据存放地址都是相对于起始地址零的相对地址。我们将装入模块装入内存中的合适位置后,对地址进行重定位变换为物理地址,地址的变化在装入时完成。这种方法必须一次性的分配全部的内存空间,而且装入模块不能移动,也不能再申请其他空间。
动态重定位:这种方式不会在装入模块装入内存后就对地址进行变换,这时所有的地址都是相对地址,地址的变化则要在程序运行时才会变换。这种方式需要一个重定位寄存器的支持。这种方式解决了静态重定位的缺点。后面会详细介绍。

内存空间的保护

方法一:在cpu中设置一对上、下限寄存器,存放进程的上、下限地址。进程的指令要访问某个地址时,cpu检查是否越界。
方法二:采用重定位寄存器(基址寄存器)和界地址寄存器(限长寄存器)进行越界检查。重定位寄存器存放的是进程的其实物理地址。界地址寄存器中存放的是进程的最大逻辑地址。

内存空间的分配与回收

连续存储

单一连续分配

在这里插入图片描述

固定分区分配

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

动态分区分配

这种分配方式不会预先划分内存分区,而是在进程装入内存时,根据进程的大小动态的创建分区,并使得分区的大小正好适合进程的需要。因此分区的大小与数目是可变的。
动态分区算法需要空闲分区表或者空闲分区链来记录内存的空闲情况。
在这里插入图片描述当有多个分区满足条件时,需要以何种方式来选择分区呢?这时会引入动态分区分匹算法。
在这里插入图片描述

非连续分配

支持多道程序运行的连续分配方式都有相应的缺点。
固定分区分配:缺乏灵活性,会产生大量的内部碎片,内存利用率不高。
动态分区分配:会产生大量的外部碎片。
这时便引入了非连续的分配方式。
这里说的连续与非连续说的是进程数据的存放是不是必须连续的存储在内存中。

分页存储

在这里插入图片描述实现地址的转换

  1. 算出逻辑地址对应的页号和页内偏移量
    当逻辑地址是十进制时:页号 = 逻辑地址 / 页面长度、页内偏移量 = 逻辑地址 % 页面长度;
    当页框的大小为2的n次方时,那么二进制逻辑地址的前n位表示页内的偏移量,后面的位数表示页框号。此时就很容易算出页号与页内偏移量。
  2. 算出页号对应的物理块的起始地址
    操作系统会为没一个进程创建一次页表。里面记录着页号与物理块的对应关系。通过物理块号来算出对应物理块的起始地址。
  3. 物理地址 = 页面起始地址 + 页内偏移量
    关于页表
    在这里插入图片描述

在这里插入图片描述在这里插入图片描述 基本地址变换机构
在这里插入图片描述
在这里插入图片描述在这里插入图片描述

局部性原理
在这里插入图片描述具有快表的基本地址变换机构
在这里插入图片描述
在这里插入图片描述在这里插入图片描述

二级页表
单级页表存在两个问题:1.由上面可知页表必须在内存中连续的存储,但当页表很大时,则需要占据多个页框;2.由局部性原理可知,进程在一个时间段内只会访问特定的几个页面,没有必要让整个页表常驻内存。
基于以上两点,人们设计出了二级页表。
我们可将长长的页表进行分组,使得每个内存块刚好放入一个分组。另外需要为离散的页表分组建立一张页表,叫做页表目录或顶层页表。
在这里插入图片描述
在这里插入图片描述在这里插入图片描述

分段存储

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

段页式存储

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

内存空间的扩展(实现虚拟性)

覆盖技术

覆盖技术的思想就是将一个程序分为多个段(多个模块)。常用的模块常驻内存,不常用的段需要时调入内存。
内存中会分为一个“固定区”和若干个“覆盖区”。常用的段放在固定区中,调入后将不再调出(除非运行结束)。不常用的段放在覆盖区,需要时调入内存,不用时调出内存。

交换技术

交换技术的思想是在内存紧张的情况下,系统将内存中的某些进程暂时换出外存,把外存中的某些已具备运行的进程换入内存。
在这里插入图片描述

虚拟内存

请求分页管理

页表机制
在这里插入图片描述

缺页中断机构
在这里插入图片描述

地址变换机构
在这里插入图片描述在这里插入图片描述

页面置换算法

在这里插入图片描述

文件管理

文件的逻辑结构

文件内的组织形式

无结构文件

例如记事本等文本的内部组织形式就是无结构的,它们的组织形式就是是一系列二进制数或者字符流。我们称之为流式文件。

有结构文件

有结构文件是由一系列相似的记录组成,每一条记录有多个数据项组成。记录可以相当于数据库表中的一列。
顺序文件
顺序文件是指文件的一条条记录顺序的排列起来(逻辑上),记录可以是定长的也可以是不定长的。实现逻辑上的顺序存储,物理上可以是顺序存储也可以是链式存储。
只要是物理上采取链式存储则不可以进行随机访问,只能进行顺序访问(类似链表)。
如果是物理上采取顺序存储,项是不定长的,也只能顺序的访问,不能随机访问。
如果是物理上采取顺序存储,项是定长的,则可以顺序的访问,也可以随机访问。
索引文件
在这里插入图片描述

索引顺序文件
在这里插入图片描述
在这里插入图片描述

文件间的组织形式

文件控制块

操作系统会为每一个目录(文件夹)建立一张文件目录表,而文件目录表中的每一项都是一个文件控制块也称FCB。FCB里面记录了文件的基本信息(文件名、物理地址、逻辑地址)、存取控制信息、使用信息等。

目录结构
单级目录结构

在这里插入图片描述

两级目录结构

在这里插入图片描述

多级目录结构

在这里插入图片描述

无环图目录结构

在这里插入图片描述

索引节点(文件控制块的优化)

在这里插入图片描述

文件的物理结构

在内存管理中我们将进程的逻辑存储单位分为一个个页面,同样的我们可以把文件的逻辑存储单位定义为,于是文件的逻辑地址可以表示为(逻辑块号,块内地址)的形式。

对分空闲磁盘块的管理

连续分配

在这里插入图片描述物理上采用连续的存储方法不便进行存储空间的扩展,并且不便分配空间,并且会产生磁盘碎片磁盘利用率低。
但是物理上的连续存储支持块的随机访问,并且连续访问时速度最快。

链接分配

在这里插入图片描述

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

索引分配

在这里插入图片描述假设一个磁盘块的大小为1KB,一个索引表项为4B时,一个磁盘块只能存储256个索引项,但当文件过大时,一个物理块不能存储的下时,这时就出现了三种解决方式。
链接方式
在这里插入图片描述

多层索引
在这里插入图片描述

混合索引
在这里插入图片描述

索引分配的总结
在这里插入图片描述

对空闲磁盘块的管理

位示图法

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

文件的基本操作

在这里插入图片描述

文件共享

在这里插入图片描述

在这里插入图片描述

磁盘管理

磁盘的基本概念

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

磁盘寻道算法

在这里插入图片描述
参考资料:王道考研操作系统课程

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值