操作系统那些事儿

进程和线程

对于无线程系统:

  • 进程是资源调度、分配的独立单位

进程是一个具有独立功能的程序关于某个数据集合的一次运行活动,它可以申请和拥有系统资源,是一个动态的概念,是一个活动的实体。进程实体由三部分构成:程序段相关的数据段PCB(进程控制块)。所谓创建/撤销进程,实际上就是创建/撤销进程实体中的PCB。

对于有线程系统:

  • 进程是资源分配的独立单位
  • 线程是资源调度的独立单位

在引入线程的操作系统中,通常都是把进程作为分配资源的基本单位,而把线程作为独立运行和独立调度的基本单位。由于线程比进程更小,基本上不拥有系统资源,故对它的调度所付出的开销就会小得多,能更高效的提高系统内多个程序间并发执行的程度。

1)线程是进程的实体,一个进程可以有多个线程,多个线程可以并发执行。
2)进程作为系统中拥有资源的基本单位,但线程并不拥有系统资源,它仅有一点必不可少的、能保证独立运行的资源。进程中的多个线程可以共享该进程所拥有的资源。
3)不同线程之间的独立性要比不同进程之间的独立性低得多。因为进程不允许别的进程来访问它的资源(除了共享全局变量),但同一个进程下的所有线程可以共享该进程的内存地址空间和资源。
4)线程的创建和撤销的系统开销明显小于进程的创建和撤销。

Ⅰ 拥有资源

进程是资源分配的基本单位,但是线程几乎不拥有资源(只拥有线程栈、寄存器和程序计数器等少量资源),线程可以访问所属进程的资源。

Ⅱ 调度

线程是独立调度的基本单位,在同一进程中,线程的切换不会引起进程切换,从一个进程中的线程切换到另一个进程中的线程时,会引起进程切换。

Ⅲ 系统开销

由于创建或撤销进程时,系统都要为之分配或回收资源,如内存空间、I/O 设备等,所付出的开销远大于创建或撤销线程时的开销。类似地,在进行进程切换时,涉及当前执行进程 CPU 环境的保存及新调度进程 CPU 环境的设置,而线程切换时只需保存和设置少量寄存器内容,开销很小。

Ⅳ 通信方面

线程间可以通过直接读写同一进程中的数据进行通信,但是进程通信需要借助 IPC。

进程之间私有和共享的资源

  • 私有:堆、地址空间、全局变量、栈、寄存器
  • 共享:代码段,公共数据,进程目录,进程 ID

线程之间私有和共享的资源

  • 私有:线程栈,寄存器,程序计数器
  • 共享:堆,地址空间,全局变量,静态变量

多进程与多线程间的对比、优劣与选择

对比
对比维度多进程多线程总结
数据共享、同步 数据共享复杂,需要用 IPC;数据是分开的,同步简单 因为共享进程数据,数据共享简单,但也是因为这个原因导致同步复杂 各有优势
内存、CPU 占用内存多,切换复杂,CPU 利用率低 占用内存少,切换简单,CPU 利用率高 线程占优
创建销毁、切换 创建销毁、切换复杂,速度慢 创建销毁、切换简单,速度很快 线程占优
编程、调试 编程简单,调试简单 编程复杂,调试复杂 进程占优
可靠性(鲁棒性?) 进程间不会互相影响 一个线程挂掉将导致整个进程挂掉 进程占优
分布式 适应于多核、多机分布式;如果一台机器不够,扩展到多台机器比较简单 适应于多核分布式 进程占优
优劣
优劣多进程多线程
优点 编程、调试简单,可靠性较高 创建、销毁、切换速度快,内存、资源占用小
缺点 创建、销毁、切换速度慢,内存、资源占用大 编程、调试复杂,可靠性较差
选择
  • 需要频繁创建销毁的优先用线程
  • 需要进行大量计算的优先使用线程
  • 强相关的处理用线程,弱相关的处理用进程
  • 可能要扩展到多机分布的用进程,多核分布的用线程
  • 都满足需求的情况下,用你最熟悉、最拿手的方式

进程之间的通信方式以及优缺点

  • 管道(PIPE):一种半双工的通信方式(虽然也有全双工管道,但是为了可移植性,一般仅将其用于单向通信-UNIX环境高级编程),数据只能单向流动,需要双方通信时,需要建立起两个管道。
    • 无名管道:简称管道,只能在具有亲缘关系的进程间(具有公共祖先的进程,如父子进程或者兄弟进程)使用
      • 优点:简单方便
      • 缺点:
        1. 局限于单向通信 
        2. 缓冲区有限
        3. 只能创建在它的进程以及其有亲缘关系的进程之间
    • 命名管道(FIFO):先进先出,它允许无亲缘关系进程间的通信
      • 优点:可以实现任意关系的进程间的通信
      • 缺点:
        1. 局限于单向通信
        2. 缓冲区有限
        3. 长期存于系统中,使用不当容易出错
  • 消息队列(Message Queue):消息的链接表,存放在内核中,一个消息队列由一个标识符(即队列ID)来标识。克服了管道缓冲区大小受限且只能在父子进程或者兄弟进程间使用、FIFO不能随机访问、信号量传递信息少(几乎无法传递信息)、共享内存需要考虑同步问题等缺点。消息队列是进程间实现共享资源的一种机制,不同进程将格式化的数据流以消息队列形式发送给任意进程,有权限的进程都可以完成对消息队列的操作控制。通过使用消息类型,进程可以按任何顺序读信息,或为消息安排优先级顺序。消息队列具有以下特点:消息队列是面向记录的,其中的消息具有特定的格式以及特定的优先级;消息队列可以独立于进程存在,进程终止时,消息队列及其内容并不会被删除;避免了 FIFO 的同步阻塞问题,不需要进程自己提供同步方法;接受消息时可以根据消息类型实现消息的随机查询,消息不一定要以先进先出的次序读取。
    • 优点:可以实现任意进程间的通信,并通过系统调用函数来实现消息发送和接收之间的同步,无需考虑同步问题,方便
    • 缺点:信息的复制需要额外消耗 CPU 的时间,不适宜于信息量大或操作频繁的场合
  • 信号量(Semaphore):一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
    • 优点:可以同步进程
    • 缺点:信号量有限。且信号量一般只用于实现进程间的互斥与同步,而不是用于存储进程间通信数据,若要在进程间传递数据需要结合共享内存。
  • 共享内存(Shared Memory):映射一段能被其他进程所访问的内存,可以存放数据和文件,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的IPC(进程间通信)方式,因为进程是直接对内存进行存取。但是因为多个进程可以同时操作,所以需要进行同步,通常与其他通信机制配合使用,比如信号量,用信号量来同步对共享内存的访问。
    • 优点:无须复制,快捷,信息量大
    • 缺点:
      1. 通信是通过将共享空间缓冲区直接附加到进程的虚拟地址空间中来实现的,因此需要注意进程间的读写操作的同步问题
      2. 利用内存缓冲区直接交换信息,内存的实体存在于计算机中,只能同一个计算机系统中的诸多进程共享,不方便网络通信
  • 套接字(Socket):可用于网络中不同计算机间的进程通信。
    • 优点:
      1. 传输数据为字节级,传输数据可自定义,数据量小效率高
      2. 传输数据时间短,性能高
      3. 适合于客户端和服务器端之间信息实时交互
      4. 可以加密,数据安全性强
    • 缺点:需对传输的数据进行解析,转化成应用级的数据。
  • Unix域套接字:一种高级IPC,可以实现全双工管道,在同一计算机的进程间传送文件描述符。然而,虽然所有平台都支持,但实现不一致,因此可移植性较差。

几种通讯方式总结:

1.管道:速度慢,容量有限,只有父子进程能通讯    

2.FIFO:任何进程间都能通讯,但速度慢    

3.消息队列:容量受到系统限制,且要注意第一次读的时候,要考虑上一次没有读完数据的问题    

4.信号量:不能传递复杂消息,只能用来同步    

5.共享内存:能够很容易控制容量,速度快,但要保持同步,比如一个进程在写的时候,另一个进程要注意读写的问题,相当于线程中的线程安全,当然,共享内存区同样可以用作线程间通讯,不过没这个必要,线程间本来就已经共享了同一进程内的一块内存

同步和通信

同步与通信很容易混淆,它们的区别在于:

  • 同步:按一定顺序执行
  • 通信:传输信息

通信是一种手段,而同步是一种目的。也可以说,为了能够达到同步的目的,需要进行通信,传输一些同步所需要的信息。通信目的主要是用于同步。

通信与同步的概念不必区分过于清晰,很多时候通信和同步的意思是想通的,一般同步是针对线程,通信是针对进程。进程间也有同步问题,而IPC是同步的实现手段。

同步与互斥

  • 同步:多个进程因为合作产生的直接制约关系,使得进程有一定的先后执行关系。
  • 互斥:多个进程在同一时刻只有一个进程能进入临界区。

互斥:某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。但互斥无法限制访问者对资源的访问顺序,即访问是无序的,线程间不需要知道彼此的存在。

同步:在互斥的基础上(大多数情况),通过其它机制实现访问者对资源的有序访问,线程间知道彼此的存在。在大多数情况下,同步已经实现了互斥,特别是所有写入资源的情况必定是互斥的。少数情况是指可以允许多个访问者同时访问资源。

 线程的同步

为什么需要线程同步:线程有时候会和其他线程共享一些资源,比如内存、数据库等。当多个线程同时读写同一份共享资源的时候,可能会发生冲突。因此需要线程的同步,多个线程按顺序访问资源。

  • 《UNIX环境高级编程第三版》线程同步机制:

    • 锁机制:包括互斥量(也叫互斥锁)(mutex)、读写锁(reader-writer lock)、自旋锁(spin lock)和条件变量(condition)
      • 1、互斥量(mutex):提供了以排他方式防止数据被并发修改的方法。
      • 2、条件变量(condition):可以以原子的方式阻塞进程,直到某个特定条件为真为止。一般将条件变量与互斥量一起使用,条件由互斥量保护。
      • 3、自旋锁(spin lock)与互斥量类似,都是为了保护共享资源。互斥量是当资源被占用,申请者进入睡眠状态;而自旋锁则处于忙等阻塞状态,循环检测保持者是否已经释放锁。使用情形:锁被持有的时间较短,而且线程不希望在重新调度上花费太多成本。
      • 4、读写锁(reader-writer lock):允许多个线程同时读共享数据,而对写操作是互斥的。一次只有一个线程占用写模式的读写锁,但是可以有多个进程占用读模式的读写锁,允许更高的并行性,不像互斥量一次只有一个线程可以加锁。
    • 5、屏障(barrier):屏障允许每个线程等待,直到所有的合作线程都达到某一点,然后从该点继续执行。
  • 其他版本答案:
  • 1)互斥量 Mutex:互斥量是内核对象,只有拥有互斥对象的线程才有访问互斥资源的权限。因为互斥对象只有一个,所以可以保证互斥资源不会被多个线程同时访问;当前拥有互斥对象的线程处理完任务后必须将互斥对象交出,以便其他线程访问该资源;
  • 2)信号量 Semaphore:信号量是内核对象,它允许同一时刻多个线程访问同一资源,但是需要控制同一时刻访问此资源的最大线程数量。信号量对象保存了最大资源计数当前可用资源计数,每增加一个线程对共享资源的访问,当前可用资源计数就减1,只要当前可用资源计数大于0,就可以发出信号量信号,如果为0,则将线程放入一个队列中等待。线程处理完共享资源后,应在离开的同时通过ReleaseSemaphore函数将当前可用资源数加1。如果信号量的取值只能为0或1,那么信号量就成为了互斥量;
  • 3)事件 Event:允许一个线程在处理完一个任务后,主动唤醒另外一个线程执行任务。事件分为手动重置事件和自动重置事件。手动重置事件被设置为激发状态后,会唤醒所有等待的线程,而且一直保持为激发状态,直到程序重新把它设置为未激发状态。自动重置事件被设置为激发状态后,会唤醒一个等待中的线程,然后自动恢复为未激发状态。
  • 4)临界区 Critical Section:临界区是一段独占对某些共享资源访问的代码,在任意时刻只允许一个线程对共享资源进行访问。拥有临界区对象的线程可以访问该临界资源,如果有多个线程试图同时访问临界区,那么在有一个线程进入后其他所有试图访问此临界区的线程将被挂起,并一直持续到进入临界区的线程离开,临界区对象被释放。临界区在被释放后,其他线程可以继续抢占,并以此达到用原子方式操作共享资源的目的。

 临界区的概念:各个进程中对临界资源(互斥资源/共享变量,一次只能给一个进程使用)进行操作的程序片段

经典同步问题

1)生产者消费者问题

 使用一个缓冲区来存放数据,只有缓冲区没有满,生产者才可以写入数据;只有缓冲区不为空,消费者才可以读出数据。

2)哲学家进餐问题

五个哲学家围着一张圆桌,每个哲学家面前放着食物。哲学家的生活有两种交替活动:吃饭以及思考。当一个哲学家吃饭时,需要先拿起自己左右两边的两根筷子,并且一次只能拿起一根筷子。筷子的数量和哲学家相等,所以每只筷子必须由两位哲学家共享。

 为了防止死锁的发生,可以设置两个条件(临界资源): 

  • 必须同时拿起左右两根筷子;
  • 只有在两个邻居都没有进餐的情况下才允许进餐。

3)读者-写者问题

允许多个进程同时对数据进行读操作,但是不允许读和写以及写和写操作同时发生。

 管程

管程 (英语:Monitors,也称为监视器,c 语言不支持管程) 是一种程序结构,结构内的多个子程序(对象或模块)形成的多个工作线程互斥访问共享资源。

管程是为了解决信号量在临界区的 PV 操作上的配对的麻烦,把配对的 PV 操作集中在一起,生成的一种并发编程方法。其中使用了条件变量这种同步机制。

管程将共享变量以及对这些共享变量的操作封装起来,形成一个具有一定接口的功能模块,这样只能通过管程提供的某个过程才能访问管程中的资源。进程只能互斥地使用管程,使用完之后必须释放管程并唤醒入口等待队列中的进程。

管程有一个重要特性:在一个时刻只能有一个进程使用管程。进程在无法继续执行的时候不能一直占用管程,否则其它进程永远不能使用管程。

 

信号量机制功能强大,但使用时对信号量的操作分散,而且难以控制,读写和维护都很困难。因此后来又提出了一种集中式同步进程——管程。

其基本思想是将共享变量和对它们的操作集中在一个模块中,操作系统或并发程序就由这样的模块构成。这样模块之间联系清晰,便于维护和修改,易于保证正确性。

从语言的角度看,管程主要有以下特性: 
(1)模块化。管程是一个基本程序单位,可以单独编译; 
(2)抽象数据类型。管程中不仅有数据,而且有对数据的操作; 
(3)信息掩蔽。管程外可以调用管程内部定义的一些函数,但函数的具体实现外部不可见; 
对于管程中定义的共享变量的所有操作都局限在管程中,外部只能通过调用管程的某些函数来间接访问这些变量。因此管程有很好的封装性。  

进程的状态与转换(生命周期)

 创建-终止

  • 就绪状态(ready):等待被调度
  • 运行状态(running)
  • 阻塞状态(waiting):等待资源

应该注意以下内容:

  • 只有就绪态和运行态可以相互转换,其它的都是单向转换。就绪状态的进程通过调度算法从而获得 CPU 时间,转为运行状态;而运行状态的进程,在分配给它的 CPU 时间片用完之后就会转为就绪状态,等待下一次调度。
  • 阻塞状态是缺少需要的资源从而由运行状态转换而来,但是该资源不包括 CPU 时间,缺少 CPU 时间会从运行态转换为就绪态。

线程的状态与转换

 与进程相同,创建、就绪、运行、阻塞、终止。

孤儿进程

一个父进程退出,而它的一个或多个子进程还在运行,那么这些子进程将成为孤儿进程。

孤儿进程将被 init 进程(进程号为 1)所收养,并由 init 进程对它们完成状态收集工作。

由于孤儿进程会被 init 进程收养,所以孤儿进程不会对系统造成危害。

僵尸进程

一个子进程的进程描述符在子进程退出时不会释放,只有当父进程通过 wait() 或 waitpid() 获取了子进程信息后才会释放。

如果子进程退出,而父进程并没有调用 wait() 或 waitpid(),那么子进程的进程描述符仍然保存在系统中,这种进程称之为僵尸进程。

僵尸进程通过 ps 命令显示出来的状态为 Z(zombie)。

僵尸进程造成的问题,以及如何解决

系统所能使用的进程号是有限的,如果大量的产生僵尸进程,将因为没有可用的进程号而导致系统不能产生新的进程。

要消灭系统中大量的僵尸进程,只需要将其父进程杀死,此时所有的僵尸进程就会变成孤儿进程,从而被 init 所收养,这样 init 就会释放所有的僵死进程所占有的资源,从而结束僵尸进程。

 进程调度算法(进程的优先级)

(1)先来先服务(FCFS,First-Come-First-Served): 此算法的原则是按照作业到达后备作业队列(或进程进入就绪队列)的先后次序来选择作业(或进程)。按照请求的顺序进行调度。

(2)短作业优先(SJF,Shortest Process Next)/ 短进程优先(SPF):按估计运行时间最短的顺序进行调度。长作业有可能会饿死,处于一直等待短作业执行完毕的状态。因为如果一直有短作业到来,那么长作业永远得不到调度。

(3)最短剩余时间优先算法:按估计剩余时间最短的顺序进行调度。

(4)高响应比优先(HRRN,Highest Response Ratio Next): 按照高响应比((已等待时间+要求运行时间)/ 要求运行时间)优先的原则,在每次选择作业投入运行时,先计算此时后备作业队列中每个作业的响应比RP然后选择其值最大的作业投入运行。

(5)多级队列调度算法:(1)多级队列是为需要连续执行多个时间片的进程考虑,它设置了多个队列,每个队列时间片大小都不同,例如 1,2,4,8,..。进程在第一个队列没执行完,就会被移到下一个队列。(2)每个队列优先权也不同,最上面的优先权最高。因此只有上一个队列没有进程在排队,才能调度当前队列上的进程。(3)如果一个进程需要执行 100 个时间片,采用时间片轮转调度算法需要交换 100 次,但在多级队列方式下只需要交换 7 次就可以执行完。

(6)时间片轮转调度算法(RR,Round-Robin):将所有就绪进程按 FCFS (先来先服务) 的原则排成一个队列,每次调度时,把 CPU 时间分配给队首进程,该进程可以执行一个时间片。当时间片用完时,由计时器发出时钟中断,调度程序便停止该进程的执行,并将它送往就绪队列的末尾,同时继续把 CPU 时间分配给队首的进程。时间片轮转算法的效率和时间片的大小有很大关系。因为进程切换都要保存进程的信息并且载入新进程的信息,如果时间片太小,会导致进程切换得太频繁,在进程切换上就会花过多时间。

(7)实时调度算法:实时系统要求一个请求在一个确定时间内得到响应。分为硬实时和软实时,前者必须满足绝对的截止时间,后者可以容忍一定的超时。

 

  多线程会出现什么问题? 1、数据安全问题,非线程安全;2、一个线程的崩溃会导致整个进程崩溃;3、死锁。

 线程安全

保证线程安全的方法

线程安全就是多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用。不会出现数据不一致或者数据污染。
非线程安全就是不提供数据访问保护,有可能出现多个线程先后更改数据造成所得到的数据是脏数据。

 C++创建线程的方式,怎么实现多线程?

1、C++ 并发编程(从C++11到C++17)

2、C++ 4类构造函数创建线程

3、C++11创建线程的三种方式

4、C++多线程的三种创建方式

 

锁和死锁

 所谓的锁,说白了就是内存中的一个整型数,拥有两种状态:空闲状态和上锁状态。加锁时,判断锁是否空闲,如果空闲,修改为上锁状态,返回成功;如果已经上锁,则返回失败。解锁时,则把锁状态修改为空闲状态。为了保证数据操作的一致性,操作系统引入了锁机制,用于保证临界区代码的安全。

死锁是指两个或两个以上的进程(线程)在运行过程中因争夺资源而造成的一种僵局(Deadly-Embrace) ) ,若无外力作用,这些进程(线程)都将无法向前推进。

死锁的原因是在多线程(或多进程)环境中,资源具有竞争性,而程序运行推进顺序不当,导致出现一种状态:多个线程(或进程)互相依赖其它线程(或进程)释放它们持有的资源,在未改变这种状态之前都不能向前推进。

产生条件:

  • 互斥:每个资源要么已经分配给了一个进程,要么就是可用的。
  • 请求和保持:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
  • 不可抢占:已经分配给一个进程的资源不能强制性地被抢占,它只能被占有它的进程显式地释放。
  • 循环等待:有两个或者两个以上的进程组成一条环路,该环路中的每个进程都在等待下一个进程所占有的资源。

处理死锁的基本方法
预防死锁:设置某些限制条件,破坏四个必要条件中的一个或几个。

  • 打破互斥条件(一般不采用):改造独占性资源为虚拟资源,大部分资源已无法改造。
  • 打破不可抢占条件:当一进程占有一独占性资源后又申请一独占性资源而无法满足,则退出原占有的资源。
  • 打破占有且申请条件:采用资源预先分配策略,即进程运行前申请全部资源,满足则运行,不然就等待,这样就不会占有且申请。
  • 打破循环等待条件:实现资源有序分配策略,对所有设备实现分类编号,所有进程只能采用按序号递增的形式申请资源。

避免死锁:进行有序资源分配,在资源的动态分配过程用某种方法防止系统进入不安全状态。
  优点:较弱限制条件可获得较高系统资源利用率和吞吐量。
  缺点:有一定实现难度。
检测死锁:允许系统在运行过程中发生死锁,但可设置检测机制及时检测死锁的发生,再采取适当措施加以清除。

解除死锁:检测出死锁后,采取适当的措施来解除死锁。死锁解除的方法:

  • 利用抢占:挂起某些进程,并抢占它的资源。但应防止某些进程被长时间挂起而处于饥饿状态;
  • 利用回滚:让某些进程回退到足以解除死锁的地步,进程回退时自愿释放资源。要求系统保持进程的历史信息,设置还原点;
  • 利用杀死进程:强制杀死某些进程直到死锁解除为止,可以按照优先级进行。

鸵鸟策略

  • 把头埋在沙子里,假装根本没发生问题。
  • 因为解决死锁问题的代价很高,因此鸵鸟策略这种不采取任务措施的方案会获得更高的性能。当发生死锁时不会对用户造成多大影响,或发生死锁的概率很低,可以采用鸵鸟策略。
  • 大多数操作系统,包括 Unix,Linux 和 Windows,处理死锁问题的办法仅仅是忽略它。

银行家算法

银行家算法思想——死锁避免策略:

1)当前状态下,某进程申请资源;
2)系统假设将资源分给该进程,满足它的需求;
3)检查分配后的系统状态是否是安全的,如果是安全,就确认本次分配;如果系统是不安全的,就取消本次分配并阻塞该进程。(第三步又称安全算法)
避免死锁实质:在进程资源分配时,如何使系统不进入不安全状态。

虚拟内存

为了更好的管理内存,操作系统将内存抽象成地址空间。每个程序都拥有自己的地址空间,这个地址空间被分成大小相等的页,这些页被映射到物理内存;但不需要所有的页都在物理内存中,当程序引用到不在物理内存中的页时,由操作系统将缺失的部分装入物理内存。

这样,对于程序来说,逻辑上似乎有很大的内存空间,只是实际上有一部分是存储在磁盘上,因此叫做虚拟内存。

虚拟内存的优点是让程序在逻辑上可以获得更多的可用内存。

内存管理单元(MMU)管理着逻辑地址和物理地址的转换,其中的页表(Page table)存储着页(逻辑地址)和页框(物理内存空间)的映射表,页表中还包含有效位(是在内存还是磁盘)、访问位(是否被访问过)、修改位(内存中是否被修改过)、保护位(只读还是可读写)。逻辑地址:页号+页内地址(偏移);每个进程一个页表,放在内存,页表起始地址在PCB/寄存器中。

页面置换算法有哪些?

(1)最佳置换算法(Optimal):即选择那些永不使用的,或者是在最长时间内不再被访问的页面置换出去。(它是一种理想化的算法,性能最好,但在实际上难于实现)。

(2)先进先出置换算法FIFO:该算法总是淘汰最先进入内存的页面,即选择在内存中驻留时间最久的页面予以淘汰。

(3)最近最久未使用置换算法LRU(Least Recently Used):该算法是选择最近最久未使用的页面予以淘汰,系统在每个页面设置一个访问字段,用以记录这个页面自上次被访问以来所经历的时间T,当要淘汰一个页面时,选择T最大的页面。

(4)Clock置换算法:也叫最近未用算法NRU(Not RecentlyUsed)。该算法为每个页面设置一位访问位,将内存中的所有页面都通过链接指针链成一个循环队列。当某页被访问时,其访问位置“1”。在选择一页淘汰时,就检查其访问位,如果是“0”,就选择该页换出;若为“1”,则重新置为“0”,暂不换出该页,在循环队列中检查下一个页面,直到访问位为“0”的页面为止。由于该算法只有一位访问位,只能用它表示该页是否已经使用过,而置换时是将未使用过的页面换出去,所以把该算法称为最近未用算法。

(5)最少使用置换算法LFU:该算法选择最近时期使用最少的页面作为淘汰页。

LRU算法-最近最久未使用

一种页面置换算法。原理是:

虽然无法知道将来要使用的页面情况,但是可以知道过去使用页面的情况。LRU 将最近最久未使用的页面换出。

为了实现 LRU,需要在内存中维护一个所有页面的链表。当一个页面被访问时,将这个页面移到链表表头。这样就能保证链表表尾的页面是最近最久未访问的。因为每次访问都需要更新链表,因此这种方式实现的 LRU 代价很高。

局部性原理

  • 时间上:最近被访问的页在不久的将来还会被访问;
  • 空间上:内存中被访问的页周围的页也很可能被访问。

内存抖动(颠簸)

颠簸本质上是指频繁的页调度行为。进程发生缺页中断时必须置换某一页。然而,其他所有的页都在使用,它置换一个页,但又立刻再次需要这个页。因此会不断产生缺页中断,导致整个系统的效率急剧下降,这种现象称为颠簸。内存颠簸的解决策略包括:

  • 修改页面置换算法;
  • 降低同时运行的程序的数量;
  • 终止该进程或增加物理内存容量。
posted @ 2020-06-13 02:13  zicmic  阅读(292)  评论(0编辑  收藏  举报