windows和linux的記憶體管理

來源:互聯網
上載者:User

      windows的記憶體管理很是嚴謹,使用記憶體必須首先分配,當然每個作業系統都是這樣,然而windows的嚴謹在於分配的過程,分為保留和提交兩個階段,其中保留的含義就是在進程的虛擬位址空間保留一塊空間,不能用作他用,保留的概念是針對虛擬位址空間的,而提交的含義是將剛才保留的虛擬位址空間的虛擬記憶體區塊對應到實體記憶體,這裡windows擴充了實體記憶體的含義,包括記憶體條代表的實體記憶體和磁碟頁檔案以及任何可以和真正的實體記憶體進行換入換出操作的備份存放區,提交的概念其實就是一個映射,為了將虛擬記憶體變得可用而做的一個到實際實體儲存體的一個映射,就是將假的變真了。 


      windows的保留和提交兩階段方式涉及到幾件事情,一個就是頁表什麼時候確立,我們可以設想一種合理的方式,就是在記憶體塊保留的時候,不操作頁表,僅僅將虛擬記憶體段插入到一個便於尋找和插入,刪除的資料結構中,而在提交階段操作頁表,當然此時記憶體不一定已經到了真正的實體記憶體,很有可能只在頁檔案中為之分配了一個slot而已,此情形下,頁表的相關位就可以用來描述這個slot的位置以及別的資訊,只要頁表的存在位為0即可,這一點和linux可以一樣,事實上,提交記憶體只是在擴充實體記憶體含義的前提下才表示映射實體記憶體,虛擬記憶體真正被映射到原始實體記憶體只有在該記憶體被訪問的時候才會發生,這是絕對懶惰的。事實上我們可以看到windows的方式不夠懶惰,linux沒有保留和提交的概念,當一個執行緒調用mmap或者malloc或者brk等等不同層次的函數時,實際上就等於保留了記憶體地區,而只有在該記憶體被訪問的時候,才會直接映射到實體記憶體而在這之前,根本不會將虛擬記憶體和物理的事實有任何聯絡,真對於假只有在不能再隱蔽事實的的時候才會顯露,linux的記憶體管理是一種絕對的懶惰,訪問記憶體其實就可以被看做記憶體提交。windows之所以採用這一種的方式來管理記憶體其實是為了用一種更加統一的方式去管理所有的記憶體,只要記憶體提交了,那麼記憶體管理器就要跟蹤這塊記憶體,不管它在實體儲存體器還是在磁碟頁檔案。linux的方式看來更加不規範,linux使用頁表來充當雙面角色,既可以尋找實體儲存體器又可以尋找交換分區記憶體的位置,並且linux中沒有一種機制來統一管理實體儲存體器和交換分區的空間,靠強大的檔案系統功能和高效的記憶體管理和檔案管理資料結構就可以輕易做到記憶體的高效換入換出,解除了實體儲存體器和交換分區的耦合,相反,在windows下,統一華麗的外表下扭曲著混亂不堪的繁雜,比如說如果想修改頁檔案的格式,那麼必須涉及記憶體提交時的邏輯,而在linux中只需要換一個file_operations就可以了,統一華麗的外表完全不是僅僅帶來了觀感上的舒服,同樣也付出了代價,比如平衡進程間記憶體數量的任務就交給了使用者,其實使用者只要可以在本進程記憶體配置和管理記憶體上保持高度靈活就可以了,進程間的記憶體平衡這樣的任務顯然是作業系統應該擔負起來的,由於只要提交記憶體,或者在實體儲存體器或者在磁碟頁檔案會佔據一定的空間,而這些空間是所有的進程共用的,如果一個進程瘋狂的提交了過多的記憶體,那麼別的進程就要忍饑挨餓,這一點上作業系統作為一個協調者實際上幫不上什麼忙,頂多將貪婪者滅掉了事,實體記憶體在各個進程間的分配比例完全取決於進程自己而失去了別的進程的監督以及核心機制的協調,這一點看起來不如linux,在linux中記憶體管理模組盡量使記憶體在進程間公平的分配,即使一個進程自己分配了大量的記憶體,只要它不訪問這些記憶體,這些記憶體連交換分區都不會佔據更別說實體儲存體器了,當然如果這個貪婪的進程要是訪問了這些記憶體,那結果就和windows一樣了,從程式的行為應該很容易辨別出這個進程,不過不管怎樣也比windows那種允許佔著茅坑不拉屎的策略要好得多,雖然記憶體已經很便宜,但是對於同樣增長的應用來講記憶體仍然是稀缺資源,因此完全懶惰式的分配方式應該就是最節省的方式。

 

 
      作為以上討論的直接結果,我們來看一下兩個系統中的堆棧。在windows中堆棧的分配是靜態,也就是說在PE檔案中確定了線程堆棧的大小並且一般不能在運行時動態改變,在對堆棧進行管理的時候,windows使用了一種稍微複雜一點但是考慮的很周到的方法,windows儘力去保護自己的堆棧不會溢出,怎麼保護呢?在《windows核心編程》上有詳細的描述,大致就是說首先為你的堆棧確定一個大小,然後將這段如此大小的記憶體塊的第一個和最後一個版面設定為保留,其餘的頁面遵循以下原則:假設堆棧向下增長,windows將依次把正在被使用的下一個版面設定為保護提交,當然正在被使用的頁面肯定是提交的了,每當保護提交頁面被訪問時系統會得到通知,注意得到通知而不是出錯資訊,並沒有什麼嚴重的錯誤,因為保護提交頁面可以被訪問,它已經提交了,只不過由於具有保護屬性,所有要告知系統這一件事,系統得知後可以將保護提交屬性設定給後一個頁面,依次類推,堆棧有著嚴格的順序訪問特性,就是說首先是高地址被訪問,在略低的地址不被使用之前更低的地址不會被使用,當然除非你使用組合語言完全脫離堆棧的概念,這樣的話,線程的堆棧空間頁面將按照從高到底的順序一個個被提交,而緊接著被提交的頁面將被設定為保護提交,直到最後,到達堆棧的末尾的時候,windows會檢測到,此時不再將最後一個版面設定為保護提交,而是引發一個棧溢出異常。windows的這種機制的結果就是有效地保護了堆棧後面的資料不被堆棧資料覆蓋,但是這種機制並不是每次都奏效的,比如一個足以使棧溢出的大數組分配在棧上,數組的起始其實已經出了堆棧,如果我直接存取這第一個元素的話,並且恰好該元素覆蓋的記憶體已經被提交,那就完蛋了,如果你覺得上述執行個體會被編譯器發現的話,那麼考慮下面的例子: 
char s[1]; 
s -= 100000; 
*s = 100; 

 

      看看linux是怎麼做的,很簡單,十分懶惰,linux沒有為堆棧分配靜態大小,而是利用缺頁中斷使得堆棧在運行期動態增長,當然沒有了固定的大小也就不存在溢出的問題了,只要虛擬記憶體足夠,動態增長的需求就有可能被滿足,那麼linux有沒有什麼辦法來保護非堆棧資料被堆棧資料損毀或者反過來的情況呢?說實話,沒有,主要是因為一來實現那個機制很複雜,維護引入的額外資料結構肯定會影響效率,二來這是使用者空間的事情,程式員如果不合格直接開掉他就是了,核心不用為他擦屁股,實際上核心如果真的用雕蟲小技幫他擦了屁股,沒有會說核心很高明的,因此開源的linux沒有這種複雜而且單單對核心沒有什麼用的機制,實際上如果程式員不合格,那麼他寫的程式是防不勝防的,機器能和人PK嗎?很顯然不能,再好的作業系統面對一般爛的程式員也是無力去愛誰啊! 

  
     

 

 

      最後討論一下“如何分配記憶體以及在哪裡分配到底要不要讓使用者看到”這個有點哲學味道的問題,這個問題關鍵要看分配的記憶體做什麼用以及這種作用和系統機制的聯絡的緊密程度,比如說我需要一塊記憶體儲存一些我程式裡面的結構,比如大型資料庫緩衝,比如一個字串,這種情況下分配越透明越好,因為程式沒有必要和實現機制交流,這樣程式可以更加集中精力解決所謂的業務問題,但是如果一塊記憶體被一個管理機制需要,那麼就有必要匯出給使用者更多的資訊,因為這種需求往往都是關注實現本身的需求,而不是介面需求,比如線程棧的位置,因為線程是作業系統的一種機制,目的是最佳化程式執行,它其實和商務邏輯沒有什麼太大的關係,線程更多的被程式流程的管理機制使用而不是被商務程序使用。在這一點點上,linux要比windows好得多,看看clone系統調用的參數,使用者必須為線程分配棧空間,而這在windows中卻是被默默執行的,實際上windows儘力去向使用者隱藏底層的很多重要的資訊,然而類似線程棧的位置這樣的資訊很多使用者空間的管理機制還是要用到的,因此最好將這一切都交給使用者,系統不要管的太多。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.