windows和linux的内存管理
? ? ? 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系統調用的參數,用戶必須為線程分配??臻g,而這在windows中卻是被默默執行的,實際上windows盡力去向用戶隱藏底層的很多重要的信息,然而類似線程棧的位置這樣的信息很多用戶空間的管理機制還是要用到的,因此最好將這一切都交給用戶,系統不要管的太多。
轉載于:https://www.cnblogs.com/dartagnan/archive/2011/06/15/2126880.html
總結
以上是生活随笔為你收集整理的windows和linux的内存管理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: idea导出快捷键配置
- 下一篇: 《Linux多线程服务端编程——使用mu