計算機作業系統(虛擬儲存器篇含分頁儲存管理方式與頁面置換演算法等)OperatingSystem-VirtualMemory

小楓學IT發表於2020-12-13

作業系統虛擬儲存器篇

一、虛擬儲存器簡介

1、虛擬儲存器定義

  • 虛擬儲存器是指具有請求調入功能和置換功能,能從邏輯上對記憶體容量加以擴充的一種儲存器系統。
  • 虛擬儲存器只是一個容量非常大的儲存器的邏輯模型,不是任何實際的物理儲存器。它藉助於磁碟等輔助儲存器來擴大主存容量,使之為更大或更多的程式所使用。
  • 虛擬儲存器指的是主存-外存層次,它以透明的方式為使用者提供了一個比實際主存空間大得多的程式地址空間。

架構圖如下所示:

在這裡插入圖片描述

2、虛擬儲存器特徵

常規儲存器管理方式特徵:

  1. 一次性: 作業必須一次全部裝入記憶體後才能執行。導致大作業無法在小記憶體中執行,無法進一步提高多道程式度。

  2. 駐留性: 作業裝入記憶體後,一直駐留在記憶體中。導致佔用過多資源,有的模組執行後長期佔用不釋放資源,造成浪費。

虛擬儲存器特徵:

  1. 離散性,是指記憶體分配時採用離散分配的方式。若採用連續分配方式,需要將作業裝入到連續的記憶體區域,這樣需要連續地一次性申請一部分記憶體空間,無法實現虛擬儲存功能,只有採用離散分配方式,才能為它申請記憶體空間,以避免浪費記憶體空間。

  2. 多次性,多次性是指一個作業被分成多次調入記憶體執行。作業在執行時,只將當前執行的那部分程式和資料裝入記憶體,以後再陸續從外存將需要的部分調入記憶體。

  3. 對換性,對換性是指允許在作業執行過程中換進換出。允許將暫時不用的程式和資料從記憶體調至外存的對換區,以後需要時再從外存調入到記憶體。

  4. 虛擬性,虛擬性是指能夠從邏輯上擴充記憶體容量,使使用者所看到的記憶體容量遠大於實際的記憶體容量。

二、虛擬儲存器的實現

1、請求分頁儲存管理方式

(1)請求分頁中的硬體支援

為了實現請求分頁,系統必須提供一定的硬體支援。除了需要一臺具有一定容量的記憶體及外存的計算機系統外,還需要有頁表機制

缺頁中斷機構以及地址變換機構

1)頁表機制

在這裡插入圖片描述

2)缺頁中斷機構

在這裡插入圖片描述

----在請求分頁系統中,每當所要訪問的頁面不在記憶體中時,便產生一次缺頁中斷,請求OS將所缺之頁調入記憶體。

-----缺頁中斷作為中斷,同樣需要經歷諸如保護CPU現場、分析中斷原因、轉入缺頁中斷處理程式進行處理、恢復CPU現場等幾個步驟。

-----但缺頁中斷又是一種特殊的中斷,它與一般的中斷相比,有著明顯的區別,主要表現在下面兩個方面:

  • 在指令執行期間產生和處理中斷訊號。通常,CPU都是在一條指令執行完成後,才檢查是否有中斷請求到達。若有,便去響應,否則,繼續執行下一條指令。然而,缺頁中斷是在指令執行期間,發現所要訪問的指令或資料不在記憶體時所產生和處理的。
  • ***一條指令在執行期間,可能產生多次缺頁中斷****。所以,系統中的硬體機構應能儲存多次中斷時的狀態,並保證最後能返回到中斷前產生缺頁中斷的指令處繼續執行。

3)地址變換機構

在這裡插入圖片描述

  • – 請求分頁系統中的地址變換機構,是在分頁系統地址變換機構的基礎上,為實現虛擬儲存器而增加了某些功能而形成的,如產生和處理缺頁中斷,以及從記憶體中換出一頁的功能等等。
  • ---- 在進行地址變換時,首先去檢索快表,試圖從中找出所要訪問的頁。若找到,便修改頁表項中的訪問位。對於寫指令,還需將修改位置成“1”,然後利用頁表項中給出的物理塊號和頁內地址形成實體地址。地址變換過程到此結束。
  • ---- 如果在快表中未找到該頁的頁表項時,應到記憶體中去查詢頁表,再根據找到的頁表項中的狀態位P,瞭解該頁是否已調入記憶體
  • ---- 若該頁已調入記憶體,這時應將此頁的頁表項寫入快表,當快表已滿時,應先調出按某種演算法所確定的頁的頁表項;然後再寫入該頁的頁表項。
  • ---- 若該頁尚未調入記憶體,這時應產生缺頁中斷,請求OS從外存該頁調入記憶體

2、請求分頁中的記憶體分配

1)最小物理塊數的確定

最小物理塊數指能保證程式正常執行所需的最小的物理塊數,最小物理塊數與計算機的硬體結構有關,取決於指令的格式、功能和定址方式。

採用直接定址方式,所需的最少物理塊數為 2。一塊是用於存放指令,另一塊用於存放資料。

間接定址時,至少要求有三個物理塊。 (間接定址中一些物理塊放的是其它物理塊的塊號)

2)記憶體分配策略

固定分配區域性置換

為每個程式分配固定數目 n 的物理塊,在整個執行中都不改變。如出現缺頁則從該程式的頁面中置換一頁。

每個程式分配多少個物理塊難以確定.

若太少,會頻繁地出現缺頁中斷,降低了系統的吞吐量。

若太多,記憶體中駐留的程式數目減少,可能造成 CPU空閒或其它資源空閒的情況。

可變分配全域性置換

為每個程式分配一定數目的物理塊,但 OS 自留一空閒塊佇列,若發現缺頁,則從空閒塊佇列中分配一空閒塊與該程式,並調入缺頁於其中。當空閒塊佇列用完時,OS 才從記憶體中任選擇一頁置換。

可變分配區域性置換

為每個程式分配一定數目的物理塊,若發現缺頁,則從該程式的頁面中置換一頁,不會影響其它程式的執行。根據程式缺頁率高低,則可增加或減少分配給該程式的物理塊。

3)物理塊分配演算法

平均分配演算法:平均分配給各個程式。未考慮程式大小,小程式浪費物理塊,大程式嚴重缺頁。

按比例分配演算法:根據程式的大小按比例分配給各個程式。如果共有 n 個程式,每程式頁面數 Si ,系統可用物理塊總數為 m,則每程式分到的物理塊數

bi = Si/S*m

考慮優先權的分配演算法:將系統提供的物理塊一部分根據程式大小先按比例分配給各個程式,另一部分再根據各程式的優先權分配物理塊數。

3、頁面調入策略

(1)何時調入頁面

預調頁策略

預調頁:將預計在不久之後便會被訪問的頁面預先調入記憶體。

程式的頁一般存放在外存的一個連續區域中。一次調入若干個相鄰的頁會比一次調入一頁更高效。

但如果調入的一批頁面中的大多數都未被訪問,則浪費了記憶體。

請求調頁策略

當程式在執行中發生缺頁時,就立即提出請求,由系統將缺頁調入記憶體。但這種策略每次僅調入一頁,須花費較大的系統開銷,增加了啟動磁碟 I/O 的頻率。

(2)從何處調入頁面

在請求分頁系統中,外存分成了按離散分配方式存放檔案的檔案區和按連續分配方式存放對換頁的對換區。程式發出缺頁請求時,從何處將缺頁調入記憶體呢?

對換區:如果系統有足夠的對換區空間,執行前可將與程式相關的檔案從檔案區複製至對換區,以後缺頁時全部從對換區調頁。

檔案區:如果系統沒有足夠的對換區空間,凡是不會被修改的檔案,直接從檔案區調頁,不必回寫(換出) 。對可能會修改的檔案第一次直接從檔案區調頁,換出時換至對換區,以後從對換區調頁。

UNIX 方式:凡未執行過的頁面均從檔案區調頁,執行過的頁面和換出的頁面均從對換區調頁。

(3)頁面調入過程

在這裡插入圖片描述

缺頁率

如果一個程式的邏輯空間為 n 頁,分配到的物理塊為 m(m

缺頁中斷處理時間

頁面置換時還需要考慮置換代價。

沒有被修改過的頁面可以直接放棄,而修改過的頁面必須進行儲存。

如果被置換頁面被修改過的概率是 β,其缺頁中斷處理時間為 T a ,被置換頁面沒有被修改過的缺頁中斷處理時間為 T b ,顯然 T a > T b 。則缺頁中斷處理時間 T:
在這裡插入圖片描述

三、頁面置換演算法

頁面置換流程圖:

在這裡插入圖片描述

1. 最佳置換演算法(Optimal)(後附程式碼)

選擇永遠不再需要的頁面或最長時間以後才需要訪問的頁面予以淘汰。

  • 該演算法所選擇調出的頁面:①可能是以後永不使用的 ②可能是在未來最長時間內不會再被訪問的。
  • 該演算法可以保證最低的缺頁率,但人們很難做到上述的預知,因此該演算法可以說是無法實現的。

2. 先進先出演算法(FIFO)(後附程式碼)

  • 該演算法總是調出最先進入記憶體的頁面,實現非常簡單。
  • 實現過程:把一個程式已調入記憶體的頁面按照先後次序連結為一個佇列,設定一個叫替換指標的,總是指向最老的頁面。

3. 最近最久未使用演算法(LRU)(後附程式碼)

  • LRU(Least Recently Used):因為無法預測未來,所以利用“最近的過去”來預測“最近的未來”。

  • LRU演算法是選擇最近最久未使用的頁面淘汰。

  • 演算法賦予每個頁面一個訪問欄位,用於記錄該頁面自上次被訪問以來所經歷的時間t。當需要淘汰一個頁面時,選擇現有頁面中t最大的淘汰。

  • 所需硬體支援(兩種實現方式):
    **①暫存器:**記錄某程式在記憶體中各頁的使用情況。
    記憶體的每一個頁面都配置一個暫存器,例如:R=Rn-1 Rn-2 Rn-3 …… R2 R1 R0。開始時Rn-1為1,然後每隔一定時間暫存器右移一位。如果把暫存器當做一個整數,最小的那個,就是最近最久未使用頁面。
    **②棧:**用一個特殊的棧儲存當前各個頁面號,每當程式訪問一個頁面時,便將其從棧的下面移出,將它壓入棧頂。這棧頂始終是最新被訪問的頁面,而棧底則是最近最久未使用頁面的頁面號。

通過一個程式實現最近最久未使用演算法、最佳置換演算法(記憶體頁面訪問已知的前提條件下)、先進先出置換演算法。程式碼如下所示

#include<iostream>
#include<iomanip>
#include<set>
using namespace std;
#define pinite_num 4//記憶體塊數,可根據情況選擇大小。
const int sequ[20]={7,0,1,2,0,3,0,4,2,3,0,3,2,1,2,0,1,7,0,1};//預先設定即將調入的頁號
int pinite[pinite_num];//記憶體塊陣列
int Mispg[20];//被置換出的頁面序列
int Mispg_num=0;//缺頁次數
double Mispg_prc;//缺頁率
set<int> nums;//不重複頁號
//===============================================================
/**
最佳演算法思路
*/
void Optimal()
{
 cout<<"最佳演算法"<<endl;
 int i=0;//記憶體塊陣列下標
 int j;//預先調入的頁號陣列下標
 int k=0;//缺頁序列陣列下標
 for(j=0;j<20;++j)
 {
  if(i<pinite_num)//當記憶體塊未滿時,就直接將頁號放入記憶體塊中
  {
   pinite[i]=sequ[j];
   ++i;
   ++Mispg_num;
  }
  else
  {
   int a=0;
   while(pinite[a]!=sequ[j]&&a<4)//查詢該頁號是否在記憶體塊中
    ++a;
   if(a<4)//該頁號已經在記憶體塊中
    continue;
   else
   {
    int b[4];//儲存記憶體塊中的頁號在下一次使用的位置
    int h=j;
    while(sequ[h]!=pinite[0]&&h<20)
     ++h;
    b[0]=h;
    h=j;
    while(sequ[h]!=pinite[1]&&h<20)
     ++h;
    b[1]=h;
    h=j;
    while(sequ[h]!=pinite[2]&&h<20)
     ++h;
    b[2]=h;
    h=j;
    while(sequ[h]!=pinite[3]&&h<20)
     ++h;
    b[3]=h;
    int max_x=0;//最後定位要換出的頁號所在的記憶體號
    int max=b[0];
    for(int c=0;c<4;++c)
     if(b[c]>max)
     {max=b[c];max_x=c;}
    Mispg[k]=pinite[max_x];
    pinite[max_x]=sequ[j];
    ++Mispg_num;
    ++k;
   }
  }
 }
 Mispg_prc=Mispg_num/20.0;
 Mispg[k]='\0';
}
//--------------------------------------------------------------
void FIFO()
{
 cout<<"先進先出演算法"<<endl;
 int i=0;//記憶體塊陣列下標
 int j;//預先調入的頁號陣列下標
 int k=0;//缺頁序列陣列下標
 for(j=0;j<20;++j)
 {
  if(i<pinite_num)//當記憶體塊未滿時,就直接將頁號放入記憶體塊中
  {
   pinite[i]=sequ[j];
   ++i;
   ++Mispg_num;
  }
  else//當記憶體塊滿是進行缺頁中斷並置換
  {
   int a=0;
   while(pinite[a]!=sequ[j]&&a<4)//查詢該頁號是否在記憶體塊中
    ++a;
   if(a<4)//該頁號已經在記憶體塊中
    continue;
   else
   {
    Mispg[k]=pinite[0];
    pinite[0]=pinite[1];
    pinite[1]=pinite[2];
    pinite[2]=pinite[pinite_num-1];
    pinite[pinite_num-1]=sequ[j];
    ++Mispg_num;
    ++k;
   }
  }
}
 Mispg_prc=Mispg_num/20.0;
 Mispg[k]='\0';
}
//--------------------------------------------------------------
void LRU()
{
    cout<<"最近最久未使用演算法"<<endl;
    int i=0;
    int j;
    int l=0;//缺頁序列陣列下
    for(j=0;j<20;++j){
        if(i<pinite_num){
            pinite[i]=sequ[j];
            ++i;
            ++Mispg_num;
        }
        else{
            int a =0;
           while(pinite[a]!=sequ[j]&&a<4)//查詢該頁號是否在記憶體塊中
            ++a;
           if(a<4){
            //該頁號已經在記憶體塊中
            //    int t= pinite[a-1];
            //     for(int m=0;m<pinite_num-1;m++){
            //         if(pinite[m]==t){
            //             continue;
            //         }else{
            //             pinite[m]=pinite[m+1];
            //         }
            //     }
            //     pinite[4]=t;
             for(int x = a; x < pinite_num; x++){
                    pinite[x] = pinite[x+1];
                }
                pinite[pinite_num-1]=sequ[j];

           }
           else
           {
            Mispg[l]=pinite[0];
            pinite[0]=pinite[1];
            pinite[1]=pinite[2];
            pinite[2]=pinite[pinite_num-1];
            pinite[pinite_num-1]=sequ[j];
            ++Mispg_num;
            ++l;
           }
        }
    }
    Mispg_prc=Mispg_num/20.0;
    Mispg[l]='\0';
    cout<<"停頓"<<endl;

}

//--------------------------------------------------------------

//---------------------------------------------------------------
void print()
{
 cout<<"缺頁中斷次數:"<<Mispg_num<<"     缺頁率:"<<Mispg_prc<<endl;
 cout<<"置換頁號序列: ";
 int k=0;
 while(Mispg[k]!='\0')
 {cout<<Mispg[k]<<" ";++k;}
 cout<<endl;
}
//-------------------------------------------------------------
void reset()//將記憶體塊、被置換頁面號及缺頁率制零,以備再次使用
{
 for(int i=0;i<pinite_num;++i)
  pinite[i]='\0';
 for(int j=0;j<20;++j)
  Mispg[j]='\0';
 Mispg_num=0;
}

int main()
{
 cout<<setw(18)<<"頁面置換演算法模擬"<<endl;
 char a='y';
 while(a=='y'||a=='Y')
 {
  cout<<"1、最佳置換演算法 2、先進先出演算法 3、最近最久未使用演算法" <<endl;
  cout<<"請選擇頁面置換演算法:";
  int chiose;
  cin>>chiose;
  switch(chiose)
  {
  case 1:Optimal();print();reset();break;
  case 2:FIFO();print();reset();break;
  case 3:LRU();print();reset();break;
  default:cout<<"@選擇錯誤!"<<endl;
  };
  cout<<endl<<"是(y|Y)否繼續: ";
  cin>>a;
  cout<<"停頓"<<endl;
 }
 return 0;
}

4. 第二次機會演算法(SCR)

  • 第二次機會演算法的基本思想是與FIFO相同的,但是有所改進,避免把經常使用的頁面置換出去。
  • 當選擇置換頁面時,檢查它的訪問位。如果是0,就淘汰這頁;如果訪問位是1,就給它第二次機會,並選擇下一個FIFO頁面。
  • 當一個頁面得到第二次機會時,它的訪問位就清為0,它的到達時間就置為當前時間。如果該頁在此期間被訪問過,則訪問位置1。

5. 最少使用置換演算法(LFU)

  • LFU(Least Frequently Used):記憶體中的每個頁都會有一個暫存器,用於記錄該頁面被訪問的頻率。
  • 演算法會選擇最近時間內,用得最少次的頁面淘汰。

6、簡單的Clock置換演算法

簡單的CLOCK演算法是通過給每一個訪問的頁面關聯一個附加位(reference bit),有些地方也叫做使用位(use bit)。他的主要思想是:當某一頁裝入主存時,將use bit置成1;如果該頁之後又被訪問到,使用位也還是標記成1。對於頁面置換演算法,候選的幀集合可以看成是一個迴圈緩衝區,並且有一個指標和緩衝區相關聯。遇到頁面替換時,指標指向緩衝區的下一幀。如果這頁進入主存後發現沒有空餘的幀(frame),即所有頁面的使用位均為1,那麼這時候從指標開始迴圈一個緩衝區,將之前的使用位都清0,並且留在最初的位置上,換出該楨對應的頁。

在這裡插入圖片描述

  1. 最開始頁面號1進入主存,主存裡面有空閒的幀,將其使用位記成1,由於主存中之前沒有頁面1,所以會發生缺頁中斷。
  2. 同理隨後的頁面2,3,4進入主存,將其使用位記成1,發生缺頁中斷。
  3. 當之後的頁面1,2進入主存時,由於頁面1,2已經在主存中,不做處理。
  4. 當之後的頁面5進入主存時,主存內沒有空餘的幀,這時候隨著指標迴圈移動整個緩衝區,將之前頁面的使用位全部清0,即這時候頁面1,2,3,4對應的使用位全部為0,指標回到最初的位置,將頁面1替換出去,頁面5換入主存,同時使用位標記成1。以此類推,可知CLOCK共發生10次缺頁中斷。

7、頁面緩衝演算法(PBA)

1、影響頁面換進換出效率的若干因素

  • 頁面置換演算法。
  • 寫回磁碟的頻率。
  • 讀入記憶體的頻率。

2、頁面緩衝演算法PBA

特點:顯著降低了頁面換進換出的頻率,是磁碟的操作次數大為減少。

在記憶體中設定了兩個連結串列:

  • 空閒頁面連結串列:一個空閒物理塊連結串列,是系統掌握的空閒物理塊,用於分配給頻繁發生缺頁的程式,以降低該程式的缺頁率。
  • 修改頁面連結串列:由已修改的頁面所形成的連結串列,目的是為了減少已修改頁面換出的次數。

四、“抖動”與工作集

1、多道程式度與處理機的利用率

由於虛擬儲存器系統能從邏輯上擴大記憶體,人們希望在系統中能執行更多的程式,即增加多道程式度,以提高處理機的利用率。

如果多道程度過高,頁面在記憶體與外存之間頻繁排程,以至於排程頁面所需時間比程式實際執行的時間還多,此時系統效率急劇下降,甚至導致系統崩潰。這種現象稱為顛簸或抖動(thrashing) 。

在這裡插入圖片描述

抖動的後果:缺頁率急劇增加,記憶體有效存取時間加長,系統吞吐量驟減(趨近於零) ;系統已基本不能完成什麼任務。

抖動產生原因:同時執行的程式數過多,程式頻繁訪問的頁面數高於可用的物理塊數,造成程式執行時頻繁缺頁。CPU 利用率太低時,排程程式就會增加多道程式度,將新程式引入系統中,反而進一步導致處理機利用率的下降。

作業系統需要一種降低缺頁率、防止抖動的記憶體管理方法:工作集策略

2、工作集策略(Working Set Strategy)

工作集是指在某段時間間隔 ∆ 裡,程式實際要訪問的頁面的集合。

把程式在某段時間間隔 ∆ 裡,在時間 t 的工作集記為w(t,∆),變數 ∆ 稱為工作集“視窗尺寸” 。

  • 工作集的大小是變化的。
  • 相對比較穩定的階段和快速變化的階段交替出現。
  • 根據區域性性原理,程式會在一段時間內相對穩定在某些頁面構成的工作集上。
  • 當區域性性區域的位置改變時,工作集大小快速變化。
  • 當工作集視窗滑過這些頁面後,工作集又穩定在一個區域性性階段。

利用工作集進行駐留集調整的策略

  • 作業系統監視每個程式的工作集變化情況。
  • 只有當一個程式的工作集在記憶體中時才執行該程式。
  • 定期淘汰駐留集中不在工作集中的頁面。
  • 總是讓駐留集包含工作集(不能包含時則增大駐留集)

工作集策略存在的問題

  • 工作集過去的變化未必能夠預示工作集未來的變化。
  • 記錄每個程式的工作集變化開銷太大。 (需要為每個程式維護一個基於時間順序的訪問頁面佇列)
  • 工作集視窗尺寸 ∆ 大小的最優值難以確定。
  • 工作集策略的思想是合理的,很多作業系統試圖採用近似的工作集策略。
  • 作業系統可以不直接監視工作集大小,而是通過監視缺頁率來達到類似效果。

3、抖動的預防方法

1)採取區域性置換策略

僅允許程式在自身範圍內進行置換。即使發生抖動,也可以把影響限制在較小範圍內。

2)在處理機排程中引入工作集策略

  • 作業系統跟蹤每個程式的工作集,併為程式分配大於其工作集的物理塊。
  • 作業系統跟蹤每個程式的工作集,併為程式分配大於其工作集的物理塊。
  • 如果所有工作集之和增加到超過了可用物理塊的總數,作業系統會暫停一個程式,將其頁面調出並且將其物理塊分配給其他程式,防止出現抖動現象。

3)用 L=S 準則調節缺頁率(Denning, 1980)

  • L:缺頁之間的平均時間。S:平均缺頁服務時間
  • L 大於 S,很少缺頁,磁碟能力沒有被充分利用。
  • L 小於 S,頻繁缺頁,超過磁碟的處理能力。
  • 調整併發程式度,使得 L 與 S 接近。這種情況下,磁碟和處理機到可以達到最佳利用率。
  • 一種類似的策略稱為“50% 準則”策略:讓磁碟保持50% 的利用率,這時 CPU 也達到最高的利用率。

4)掛起若干程式

  • 當多道程式度偏高,已影響到處理機的利用率時,為了防止發生抖動,系統必須減少多道程式的數目。把某些低優先順序的程式掛起,從而騰出記憶體空間。

參考連結:
https://blog.csdn.net/qq_28602957/article/details/53821061

https://blog.csdn.net/zhuixun_/article/details/85336417

https://blog.csdn.net/dongyanxia1000/article/details/51727339/

https://blog.csdn.net/qq_28602957/article/details/53744956

https://blog.csdn.net/qq_28602957/article/details/53791425

相關文章