記憶體是現代計算機運作的中心。記憶體有很大一組字或位元組組成,每個字或位元組都有它們自己的位址。cpu根據程式計數器(pc)的值從記憶體中提取指令,這些指令可能會引起進一步對特定記憶體位址的讀取和寫入。
一個典型指令執行周期,首先從記憶體中讀取指令。接着該指令被解碼,且可能需要從記憶體中讀取操作數。在指令對操作數執行後,其結果可能被存回到記憶體。記憶體單元隻看到位址流,而并不直到這些位址是如何産生的(由指令計數器、索引、間接尋址、實位址等)或它們是什麼位址(指令或資料)。
cpu所能直接通路的存儲器隻有記憶體和處理器内的寄存器。機器指令可以用記憶體位址作為參數,而不能用磁盤位址作為參數。如果資料不在記憶體中,那麼cpu使用前必須先把資料移到記憶體中。
cpu内置寄存器通常可以在一個cpu時鐘周期内完成通路。對于寄存器的内容,絕大多數cpu可以在一個時鐘周期内解析并執行一個或多個指令,而對于記憶體就不行。完成記憶體通路需要多個cpu時鐘周期,由于沒有資料以便完成正在執行的指令,cpu通常需要暫停(stall)。由于記憶體通路頻繁,這種情況是難以忍受的,解決方法是在cpu與記憶體之間增加高速記憶體。這種協調速度差異的記憶體緩沖去,稱為高速緩存(cache)。
除了保證通路實體記憶體的相對速度之外,還要確定作業系統不會被使用者程序所通路,以及確定使用者程序不會被其他使用者程序通路。
其中一種可能方案為:
首先確定每個程序都有獨立的記憶體空間,為此,需要确定程序可通路的合法位址的範圍,并確定程序隻能通路其合法位址。通過基位址寄存器(base register)和界限位址寄存器(limit register)可以實作這種保護。基位址寄存器(base register)含有最小的實體記憶體位址,界限位址寄存器(limit register)決定了範圍的大小。例如:如果基位址寄存器為300040而界限寄存器為120900,那麼程式可以通路從300040到420940的所有位址。

記憶體空間保護的實作,是通過cpu硬體對使用者模式所産生的每個位址與寄存器的位址程序比較來完成的。如果通路了不該通路的位址,則會陷入到作業系統中,并作為緻命錯誤處理。
作業系統在核心模式下,可以無限制地通路作業系統和使用者記憶體。是以作業系統可以将使用者程式裝入使用者記憶體,在出錯時輸出這些程式,通路并修改系統調用的參數等。
通常,程式以二進制可執行檔案的形式存儲在磁盤上。為了執行,程式被調入記憶體并放入程序空間内。
根據所使用的記憶體管理方案,程序在執行時,可以在磁盤和記憶體之間移動。在磁盤上等待調入記憶體以便執行的程序形成輸入隊列(input queue)。
通常的步驟是從輸入隊列中選取一個程序并裝入記憶體。程序在執行時,會通路記憶體中的指令和資料。最後,程序終止,其位址空間将被釋放。
許多系統允許使用者程序放在實體位址的任意位置。這種組合方式會影響使用者程式能夠使用的位址空間。在絕大多數情況下,使用者程式在執行前,會經過好幾個步驟,在這些步驟中,位址可能有不同的表示形式,源程式中的位址通常是用符号(如count)來表示,編譯器通常将這些符号位址綁定(bind)在可重定位的位址(如:從本子產品開始的第14位元組)。連結程式或加載程式再将這些可重定位的位址綁定成絕對位址(如74014)。每次綁定都是從一個位址空間到另一位址空間的映射。
通常,将指令與資料綁定到記憶體位址有以下幾種情況:
編譯時(compile time):如果編譯時就知道程序将在記憶體中的駐留位址,那麼就可以生成絕對代碼(absolute code)。如果将來開始位址發生變化,那麼就必須重新編譯代碼。
加載時(load time):當編譯時不知道程序将駐留在記憶體的什麼地方,那麼編譯器就必須生成可重定位代碼(reloadable code)。綁定會延遲到加載時才進行。如果開始位址發生變化。隻需要重新加載使用者代碼已引入改變值。
執行時(execution time):如果程序在執行時可以從一個記憶體段移到另一個記憶體段,那麼綁定必須延遲到執行時才發生。絕大多數通用計算機作業系統采用這種方法。
cpu生成的位址通常稱為邏輯位址(logical address),而記憶體單元所看到的位址(即加載到記憶體位址寄存器(memory-address register)中的位址)通常稱為實體位址(physical address)。
編譯和加載時的位址綁定方法生成相同的邏輯位址和實體位址。但是,執行時的位址綁定方案導緻不同的邏輯位址和實體位址。對于這種情況,通常稱邏輯位址為虛拟位址(virtual address)。由程式所生成的所有邏輯位址稱為邏輯位址空間(logical address space),與這些邏輯位址相對應的實體位址的集合稱為實體位址空間(physical address space)。
運作時從虛拟位址到實體位址的映射由被稱為記憶體管理單元(memory-management unit,mmu)的硬體裝置來完成。有很多可選擇的方法來完成這種映射,如使用一個簡單的mmu方案來實作這種映射,這是一種基位址寄存器方案的推廣,基位址寄存器在這裡稱為重定位寄存器(relocation register),使用者程序所生成的位址在送交記憶體之前,都加上重定位寄存器的值。
假如,基位址為14000,那麼使用者對位址346的通路将映射為位址14346。
使用者程式絕對不會看到真正的實體位址。如,程式可以建立一個指向位置346的指針,将他儲存在記憶體中,使用它,與其他位址進行比較等等,所有這些操作都是基于346進行的。使用者程式處理邏輯位址時,記憶體映射硬體将邏輯位址轉變為實體位址。所引用的記憶體位址隻有在引用時才最後定位。
邏輯位址空間綁定到單獨的一套實體位址空間。
一個程序的整個程式和資料如果都必須處于實體記憶體中,則程序的大小受實體記憶體大小的限制。
為了獲得更好的記憶體空間使用率,使用動态加載(dynamic loading),即一個子程式隻有在調用時才被加載。
所有的子程式都以可重定位的形式儲存在磁盤上。主程式裝入記憶體并執行。當一個子程式需要調用另外一個子程式的時候,調用子程式首先檢查另一個子程式是否已經被加載。如果沒有,可重定位的連結程式将用來加載所需要的子程式,并更新程式的位址表以反應這一變化。接着控制傳遞給新加載的子程式。
動态加載的優點是不用子程式絕不會被加載,如果大多數代碼需要用來處理異常情況,如錯誤處理,那麼這種方法特别有用。對于這種情況,雖然總體上程式比較大,但是所使用的部分可能小很多。
動态加載不需要作業系統提供特别的支援。利用這種方法來設計程式主要是使用者的責任。
有的作業系統隻支援* 靜态連結(static linking)*此時系統語言庫的處理與其他目标子產品一樣,由加載程式合并到二進制程式鏡像中。
動态連結的概念與動态加載相似。隻是這裡不是将加載延遲到運作時,而是将連結延遲到運作時。這一特點通常用于系統庫,如語言子程式庫。沒有這一點,系統上的所有程式都需要一份語言庫的副本,這一需求浪費了磁盤空間和記憶體空間。
如果有動态連結,二進制鏡像中每個庫程式的應用都有一個存根(stub)。存根是一小段代碼,用以指出如何定位适當的記憶體駐留的庫程式,或如果該程式不在記憶體中應如何安裝入庫。不管怎樣,存根會用子程式位址來代替自己,并開始執行子程式。是以,下次再執行該子程式代碼時,就可以直接進行,而不會因動态連結産生任何開銷。采用這種方案,使用語言庫的所有程序隻需要一個庫代碼副本就可以了。
動态連接配接也可用于庫更新。一個庫可以被新的版本所替代,且使用該庫的所有程式會自動使用新的版本。沒有動态連結,所有這些程式必須重新連結以便通路。
為了不使程式錯用新的、不相容版本的庫,程式和庫将包括版本資訊。多個版本的庫都可以裝入記憶體,程式通過版本資訊來确定使用哪個庫副本。
是以,隻有用新庫編譯的程式才會收到新庫的不相容變化影響。在新程式裝入之前所連結的其他程式可以繼續使用老庫。這種系統也稱為共享庫。
與動态加載不同,動态連結通常需要作業系統幫助。如果記憶體中的程序是彼此保護的,那麼隻有作業系統才可以檢查所需子程式是否在其他程序記憶體空間内,或是允許多個程序通路同一記憶體位址。
程序需要在記憶體中以便執行。程序也可以暫時從記憶體中交換(swap)到備份存儲(backing store)上,當需要再次執行時在調回到記憶體中。
在交換政策的變種被用在基于優先級的排程算法中。如果有一個更高優先級的程序且需要服務,記憶體管理器可以交換出低優先級的程序,以便裝入和執行更高優先級的的程序。當高優先級程序執行完後,低優先級程序可以交換回記憶體繼續執行,這種交換有時候稱為滾入(roll in/swap in)和滾出(roll out/swap out)。
通常,一個交換出的程序需要交換回它原來所占有的記憶體空間。這一限制是由位址綁定方式決定的。如果綁定是在彙編時或加載時所定的,那麼就不可以移動到不同的位置。如果綁定在運作時才确定,由于實體位址是在運作時才确定,那麼程序可以移動到不同的位址空間。
交換需要備份存儲。備份存儲通常是快速磁盤。這必須足夠大,以便容納所有不同使用者的記憶體鏡像副本,它也必須提供對這些記憶體鏡像的直接通路。系統有一個就緒隊列,它包括在備份存儲或記憶體中等待運作的所有程序。當cpu排程程式決定執行程序時,它調用排程程式。排程程式檢查隊列中的下一程序是否在記憶體中,如果不在記憶體中且沒有空閑記憶體空間,排程程式講一個已在記憶體中的程序交換出去,并換入所需要的程序。然後,它重新裝載寄存器,并将控制轉交給所選擇的程序。
交換系統的上下文切換時間比較長。為了有效使用cpu,需要使每個程序的執行時間比交換時間長。
注意交換時間主要部分是轉移時間。總的轉移時間與所交換的記憶體空間總量成正比。是以如果隻需交換真正使用的記憶體,便可以減少交換時間。為有效使用這種方法,使用者需要告訴系統其記憶體需求情況。是以,具有動态記憶體需求的程序要通過系統調用(請求記憶體和釋放記憶體)來通知作業系統其記憶體需求變化情況。
交換也受其他因素限制。如果要換進的程序,那麼必須確定該程序完全處于空閑狀态。如果i/o異步通路使用者記憶體的i/o緩沖區,那麼該程序就不能被換出。假定由于裝置忙,i/o操作在排隊等待。如果換出程序p1換入程序p2,那麼i/o操作可能試圖使用現在已經屬于程序p2的記憶體。
對于這個問題有兩種解決方法:
一是,不能換出有待處理i/o的程序。
二是,i/o操作的執行隻能使用作業系統緩沖區。僅當換入程序後,才執行作業系統緩沖與程序記憶體之間的資料轉移。
交換空間通常作為磁盤的一整塊,且獨立與檔案系統,是以使用就可能很快。
通常并不執行交換,但當有許多程序運作且記憶體空間吃緊時,交換開始啟動。如果系統負荷降低,交換就暫停。
記憶體必須容納作業系統和各種使用者程序,是以應該盡可能有效地配置設定記憶體的各個部分。
記憶體通常分為兩個區域:一個用于駐留作業系統,一個用于使用者程序。作業系統可以位于低記憶體或高記憶體,影響這一決定的主要因素是中斷向量的位置。由于中斷向量通常位于低記憶體,是以程式員通常将作業系統放到低記憶體。
通常需要将多個程序同時放入記憶體中,是以需要考慮如何為輸入隊列中需要調入記憶體的程序配置設定記憶體空間。
采用連續記憶體配置設定(contiguous memory allocation)時,每個程序位于一個連續的記憶體區域。
通過采用重定位寄存器和界限位址寄存器可以實作保護。
重定位寄存器含有最小的實體位址值;界限位址寄存器含有邏輯位址的範圍值。
這樣每個邏輯位址必須小于界限位址寄存器。mmu動态第将邏輯位址加上重定位寄存器的值後影射成實體位址。映射後的實體位址再送交記憶體單元。
當cpu排程器選擇一個程序來執行時,作為上下文切換工作的一個部分,排程程式會用正确的值來初始化重定位寄存器和界限位址寄存器,由于cpu所産生的每一位址都需要與寄存器程序核對,是以可以保證作業系統和其他使用者程式和資料不受該程序運作所影響。
重定位寄存器機制為允許作業系統動态改變提供了一個有效方法。如某驅動程式(或其他作業系統服務)不常使用便可以不必在記憶體中,這類代碼有時稱為暫時(transient)作業系統代碼,它們根據需要調入或調出。是以,使用這種代碼可以在程式執行時動态改變作業系統的大小。
最簡單的記憶體配置設定方法之一是将記憶體分為多個固定大小的分區(partition)。每個分區隻能容納一個程序。那麼多道程式的程度會受分區數限制。如果使用這種多分區方法(multiple-partition method),當一個分區空閑時,可以輸入隊列中選擇一個程序,以調入到空閑分區。當程序終止時,其分區可以被其他程序所使用。這種方法現在已不再使用。對于固定分區方案的推廣(稱為mvt),它主要用于批處理環境。也可用于純分段記憶體管理的分時作業系統。
在可變分區(variable-partition)方案中,作業系統有一個表,用于記錄那些記憶體可用和哪些記憶體已被占用。一開始,所有記憶體都可用于使用者程序,是以可以作為一大塊可用記憶體,稱為孔(hole),當新程序需要記憶體時,為該程序查找足夠大的孔,如果找到,可以從該孔程序配置設定所需的記憶體,孔内未配置設定的記憶體可用于下次再用。
随着程序進入系統,它們将被加入輸入隊列中。作業系統根據排程算法來對輸入隊列進行排序。記憶體不斷地配置設定給程序,直到下一個程序的記憶體需求不能滿足為止,如果沒有足夠大的孔來裝入程序,作業系統可以等到有足夠大的空間,或者往下掃描輸入隊列以确定是否其他記憶體需求較小的程序可以被滿足。
通常,一組不同大小的孔分散在記憶體中。當新程序需要記憶體時,系統為程序查找足夠大的孔。如果孔太大,那麼就分成兩塊:一塊配置設定給新程序,另一塊還回到孔集合,當程序終止時,它将釋放其記憶體,改記憶體将還給孔集合。如果孔與其他孔相鄰,那麼将這些孔合并為大孔。這時,系統可以檢查是否有程序在等待記憶體空間,新合并的記憶體空間是否滿足等待程序。
這種方法是通用動态存儲配置設定問題的一種情況(根據一組空閑孔來配置設定大小為n的請求),這個問題有許多解決方法。從一組可用孔中選擇一個空閑孔的最為常用方法有首次适應(first-fit)(第一個對夠大的孔)、最佳适應(best-fit)(最小的最夠大的孔)、最差适應(worst-fit)(配置設定最大的孔)。
首次适應(first-fit):配置設定第一個足夠大的孔,查找可以從頭開始,也可以從上次首次适應結束時開始。一旦找到足夠大的空閑孔,就可以停止。
最佳适應(best-fit):配置設定最小的足夠大的孔。必須查找整個清單,除非清單按照大小排序。這種方法可以産生最小剩餘孔。
最差适應(worst-fit):配置設定最大的孔,同樣必須查找整個清單,除非清單按照大小排序。這種方法可以産生最大剩餘孔。該孔可能比最佳适應方法産生的最小剩餘孔更有用。
模拟結果顯示:首次适應和最佳适應方法在執行時間和利用空間方面都好于最差适應方法。首次适應和最佳适應方法在利用空間方面難分伯仲,首次适應方法更快些。
首次适應和最佳适應算法都有外部碎片問題(external fragmentation)。随着程序裝入和移出記憶體,空閑記憶體空間被分割為小分段,
當所有總的空用記憶體之和可以滿足請求,但并不連續時,這就出現了外部碎片問題。最壞的情況下,每兩個程序之間就有空閑塊(或浪費)。如果這些記憶體是一整塊,那麼就可以再運作多個程序。
在首次适應和最佳适應之間的選擇可能會影響碎片的量。另一個影響因素是從空閑塊的哪端開始配置設定。不管使用哪種算法,外部碎片始終是個問題。
根據記憶體的總大小和平均程序大小的不同,外部碎片化的重要程度也不同。例如,對采用首次适應方法的統計說明,對于首次适應方法不管怎麼優化,假定n個可配置設定塊,那麼可能有0.5n個塊為外部碎片。即1/3記憶體可能不能使用,這一特性稱為50%規則。
記憶體碎片可以是内部的,也可以是外部的。如果記憶體以固定大小的塊為單元來配置設定,程序所配置設定的記憶體可能比所要的要大。這兩個數字之差稱為内部碎片(internal fragmentation)這部分記憶體在分區内,但又不能使用。
一種解決外部碎片問題的方法是緊縮(compaction),緊縮的目的是移動記憶體内容,以便所有空閑空間合并成一整塊。但是緊縮并非總是可能的。如果重定位是靜态的,并且在彙編時或裝入時進行的,那麼就不能緊縮。緊縮僅在重定位是動态的并在運作時可采用。如果位址被動态重定位,可以首先移動程式和資料,然後再跟據新基位址的值來改變基位址寄存器。如果采用緊縮,還要評估其開銷,最簡單的合并算法是簡單地将所有進城移到記憶體的一端,而将所有的孔移到記憶體的另一端,以生成一個大的空閑塊。這種方案開銷較大。
另一種解決方法外部碎片問題的方法是允許實體位址為非連續的。這樣隻要有實體記憶體就可以為程序配置設定。這種方案有兩種互補的實作技術:分頁和分段。這兩種技術也可以合并。