原文:the abstraction: address spaces在早期,構建計算機系統是很簡單的。為什麼?你可能會想。因為使用者期望值不高。都是那些『該死』的使用者,想要乙個"易用"、"高效能"、"可靠"的系統,才引起了一系列頭疼的問題。下一次你遇到這些計算機使用者們,別忘記感謝一下他們製造出來的問題:)
從記憶體的角度來說,早期的機器並沒有提供太多的抽象給使用者。一般而言,機器的物理記憶體可以用下圖表示:
整個 os(圖中的陰影部分,譯者注) 不過就是物理記憶體中的一些routines(本質上來說,乙個 library),在這個例子中,從實體地址的 address 0開始。同時,還有乙個執行的程式(程序)位於實體地址的 address 64k 處,並且獨自佔據了整個剩下的記憶體。使用者並沒對作業系統寄予太大的期望,作業系統程式設計師的生活還是很輕鬆的,不是嗎?
一段時間過後,因為計算機實在是太貴了,人們開始更有效地共享有限的計算資源。於是多道程式設計
dv66:programming semantics for multiprogrammed computations 的時代開始了,在乙個時刻,多個程序可以同時等待被執行,然後作業系統不斷在這些程序中切換(比如當前程序進行 io 的時候就可以切換到其他程序)。這樣很有效地提高了 cpu 的利用效率。在那個計算機動輒幾百萬美元的時代,這些效率的提高是非常重要的。
然而,人是永遠不會滿足的,人名開始期待能夠從作業系統那獲取更多功能,於是分時系統的時代開始了。很多人尤其是程式設計師們自己意識到了 batch programming 的侷限性,厭倦了很長(於是效率低下)的 debug 週期。因為很多使用者在同時使用一台計算機,每個人都在等待(甚至可以說是期望)自己的程式返回結果,可互動性(interactivity)的概念開始變得越來越重要。
實現 time sharing 的一種方法是讓乙個程序執行一小段時間,這段時間內讓它佔據所有的記憶體,然後停止它,把其所有的狀態保持到硬碟上(包括所有的物理記憶體!),介面匯入另外乙個程序,執行一段時間...... 這也算是一種簡陋的實現方法。
顯然,這種方法有很大的問題:太慢了,尤其是記憶體不斷增大的時候。儲存和歡迎暫存器級別的狀態(比如 pc,general-purpose registers等等)是很快的,但是要把整個記憶體的全部內容儲存到硬碟,這無疑不是一種高效的方法。所以,我們得想辦法在切換的時候把程序的狀態保持在記憶體裡面。
在這個圖中,有三個程序(a,b,c),每個佔據 512kb記憶體的一小部分。現在對於 cpu,他可以選擇任何乙個抽象來執行(比如說 a),然後讓另外兩個(b 和 c)等待。
隨著分時系統越來越流行,你肯定猜到了人們又開始提出新的需求了。尤其重要的一點是:讓這麼多程式同時儲存在記憶體裡,保護措施就很重要,你可不希望乙個程序可以看到其他程序的內容,更別說可以隨意修改其他程序的內容。
使用者是很難伺候的,作業系統的設計者得對物理記憶體做乙個抽象,提供乙個好用的介面給他們。我們把這種抽象就叫做位址空間。對於執行中的程式而言,它對物理記憶體是無感知的,它知道的只有位址空間(虛擬的)。理解基本的作業系統抽象,是理解記憶體是如何虛擬化的關鍵!
程序的位址空間包含了執行程序的所有狀態。比如說,**總得儲存在某個地方吧?所以**就在位址空間裡面。程序執行的時候,會用 stack 來儲存當前函式呼叫鏈的位置,分配空間給區域性變數,並且返回值。還有一部分,叫做 heap,被用來儲存動態分配、使用者管理的記憶體,比如呼叫 malloc() 函式,或者 new 乙個物件,都是從這裡面得到需要的記憶體。當然,儲存在位址空間的內容還有很多,目前而言我們只需要關注於這三點就行了。
上圖中,有乙個很小的位址空間(16kb)。程式的**儲存在位址空間的最上方(0-1kb)。**是靜態的,不會增加也不會減少,可以直接放在位址空間的最上部。還有兩部分大小可變的區域,那就是 heap 和 stack。上圖中,stack 和 heap 往兩個不同的方向增長。如進行malloc()
申請記憶體時,heap 往下增長;當進行乙個procedure call
時,stack 往上增長。當然這只是乙個慣例,並沒有物理上的限制要求一定要這樣,你可以以其他方法重新組織位址空間。(事實上,當多個 threads 共存的時候,也不存在這麼優雅的位址空間劃分方法。)
我們談位址空間的時候,我們談的是作業系統提供給程序的抽象。上圖的程序並不是在物理記憶體的0-16kb 位置,它可以被存放在物理記憶體的任意位置。回顧一下圖13.2,你可以看到每個程序被載入到記憶體的不同位置。
當作業系統這麼做的時候,我們說作業系統是在虛擬化記憶體,因為執行中的程序是以為自己佔據了所有的記憶體的。然而現實很不一樣。
比如說,圖13.2的程序a想要載入位址0處的資料(這裡指的是虛擬記憶體),作業系統,具備特定的硬體支援,會把這個這個位址0對映到物理記憶體中另外的位置。這個就是記憶體虛擬化的核心,幾乎每乙個現代計算機系統都在試用這種方法。
虛擬記憶體系統的最主要目標是透明化
。也就是底層的實體地址對執行的程式不可見。所以,程式根本就不會意識到記憶體是虛擬化的,而是認為自己有乙個完整的私有實體地址。作業系統在幕後,承當著虛擬位址到實體地址的轉換工作。這極大地降低了程式開發者地複雜度!
第二個目標是高效率
。作業系統應該更可能讓抽象更加高效,包括時間上的和空間上的。比如,作業系統會依賴於一些硬體,比如 tlb 快取,可以縮短作業系統訪問使用者記憶體的時間。
最後乙個目標是保護
。作業系統需要確保程序間是隔離的,乙個程序不能隨意讀取另乙個程序的資料,尤其是不能隨意在別人的地盤亂寫資料,不然會帶來很多很多不可預期的問題。
虛擬記憶體本質上就是一層抽象。作業系統給程式提供了乙個簡單易用的介面,把虛擬記憶體對映成底層的物理記憶體,這中間的複雜度全部交給了作業系統,極大地降低了軟體開發的難度,同時降低了軟體出錯的可能性。
再次深刻地體會到了那句名言:
計算機領域的一切難題都可以通過抽象解決。
Linux記憶體 核心位址空間
一,為什麼需要高階記憶體 答 對於32位機器,linux虛擬記憶體最大為4g,其中3 4g空間是用作核心空間,考慮到如果物理記憶體大於1g,那麼物理記憶體不能得到完全的對映,因此,linux 規定 核心直接對映空間 最多對映 896m 物理記憶體,arm體系架構上有高階記憶體的概念,不過不是固定在8...
Linux核心之程序位址空間
程序使用的是虛擬記憶體中的位址,也叫線性位址,由作業系統協助相關硬體 如mmu 對映到實體地址。線性位址是通過頁表 page table 對映到物理記憶體,頁表由作業系統維護並被處理器引用。核心空間在頁表中擁有較高特權級,因此使用者態程式試圖訪問這些頁時會導致乙個頁錯誤。在linux中,核心空間是持...
Linux核心之程序位址空間
核心是作業系統中優先順序最高的成分 核心信任自己 核心總是盡量推遲給使用者態程序分配動態記憶體 核心必須能隨時準備捕獲使用者態程序引起的所有定址錯誤 當使用者態程序請求動態記憶體時,並沒有會的請求頁框,而僅僅獲得對乙個新的線性位址區間的使用權,而這一線性位址區間就稱為程序位址空間的一部分,這一區間叫...