天天看點

《30天自制作業系統》筆記(07)——記憶體管理進度回顧什麼是記憶體管理如何擷取記憶體容量記憶體管理算法總結

《30天自制作業系統》筆記(07)——記憶體管理

中處理掉了絕大部分與CPU配置相關的東西。本篇介紹記憶體管理的思路和算法。

現在想想,從軟體工程師的角度看,CPU也隻是一個軟體而已:它的功能就是加載指令、執行指令和響應中斷,而響應中斷也是在加載指令、執行指令。就像火車沿着一條環形鐵軌前進;當中斷發生時,就好像鐵軌岔口處變軌了,火車就順着另一條軌迹走了;走完之後又繞回來重新開始。決定CPU是否變軌的,就是CPU裡的特定寄存器。

《30天自制作業系統》筆記(07)——記憶體管理進度回顧什麼是記憶體管理如何擷取記憶體容量記憶體管理算法總結

這是題外話,就此為止。

假設記憶體大小是128MB,應用程式A暫時需要100KB,畫面控制需要1.2MB……。

像這樣,作業系統有時要配置設定一定大小的記憶體,用完後又要收回。是以,必須管理好哪些記憶體空閑可用,哪些正在被占用。這就是記憶體管理。

記憶體管理的兩個任務,一是記憶體配置設定,一是記憶體釋放。

要管理記憶體,首先得知道作業系統所在的這個計算機記憶體有多大。檢查記憶體容量的方法很簡單,就是從要檢查的起始位置到最後位置依次寫入一個數值(例如0xaa55aa55),然後按位反轉,檢查是否正确地完成了反轉(變成0x55aa55aa);然後再次反轉,檢查是否正确地完成了反轉。如果反轉結果都是正确的,說明這個位址的記憶體是存在的;否則就說明記憶體的最大位址就到此為止了。其代碼如下。

但直接用C語言來寫這個函數的話,C編譯器會把它優化成這個樣子。

C編譯器不會理睬"記憶體到頭了"這種事情,它隻在應用程式的層面看問題。是以它認為for循環裡的if判定都是必然為真(或為假)的,認為沒有被其它代碼使用的變量都是沒用的。是以它就幹脆把這些"沒用的"代碼删掉了。

為了解決這個問題,還是用彙編語言來寫這個memtest_sub函數好了。代碼如下。

《30天自制作業系統》筆記(07)——記憶體管理進度回顧什麼是記憶體管理如何擷取記憶體容量記憶體管理算法總結
《30天自制作業系統》筆記(07)——記憶體管理進度回顧什麼是記憶體管理如何擷取記憶體容量記憶體管理算法總結

彙編版本的memtest_sub

知道了記憶體容量,就可以進行管理了。

486以上的CPU是有高速緩存(cache)的。CPU每次通路記憶體,都要将所通路的位址和内容存入cache,也就是存放成這樣"18号位址的值是54"。如果下次要用18号位址的内容,CPU就不需要再讀記憶體了(速度慢),而是直接從cache中取得18号位址的内容(速度快)。

如果開啟着CPU高速緩存(cache),上述的檢測代碼就不會正常工作。因為寫入一個記憶體位址,然後立即讀出,這樣的操作符合cache到的情況,CPU不會從記憶體讀,而是直接讀cache到的東西。結果,所有的記憶體都"正常",檢測代碼就起不到作用了。

假設記憶體有128MB(0x08000000位元組),以4KB(0x1000位元組)為機關進行管理。

128MB/4KB=32768。是以我們建立32768位元組的區域,寫入0表示對應的記憶體是空閑的,寫入1表示對應的記憶體是正在使用的。這個方法的名字我沒有查到。

比如需要100KB的記憶體,那麼隻要從a中找出連續25個标記為0的地方就可以了。

需要收回這100KB的時候,用位址值/0x1000,計算出j就可以了。

當然,我們可以用1bit來代替1個char,這樣所需的管理空間就可以省下7/8,使用方法則是一樣的。

把類似于"從xxx号位址開始的yyy位元組的空間是空閑的"這種資訊都存儲到清單裡。

比如,如果需要100KB的記憶體,隻要檢視memman中free的狀況,從中找到100MB以上的可用空間就行了。

釋放記憶體時,增加1條可用資訊,frees加1。而且還要看看新釋放出的記憶體與相鄰的記憶體能不能連到一起,如果可以,就要歸為1條。

與上文的最簡單的方法相比,這種清單管理的方法,占用記憶體少,且記憶體的申請和釋放更迅速。

缺點是釋放記憶體的代碼比較複雜。另外,如果記憶體變成零零碎碎的,那麼需要的MEMMAN裡的數組就會超過1000,這是個問題。如果真發生這種情況,隻能将一部分零碎的空閑記憶體都視作被占用的,然後合并。

記憶體管理要結合GDT的設定進行。按段(Segment)設計的GDT,記憶體就得按段申請和回收。按頁設計的GDT,額我不知道,以後再說。

記憶體管理需要的預備知識還包括"擷取記憶體容量"、"禁止/啟用高速緩存"、"資料結構-連結清單"。

記憶體管理的算法還有很多,本篇隻給出了兩種最基本最簡單的,夠做個簡易的OS就行了,現在不是深究算法的時候。