作業系統的記憶體管理你知道嗎
導讀 | brk()的作用也只是通知 核心哪個範圍的堆記憶體是可用的,真正的實體記憶體頁是在程式實際讀寫記憶體的時候才會申請,而且是由核心根據寫時複製/需求載入自動完成的,應用程式感知不到這點。 |
記憶體管理,是作業系統的主要功能。
作業系統從啟動一直到建立0號程式(idle程式),執行的大部分程式碼都跟記憶體有關。
作業系統的記憶體管理,大概分這麼幾個層次:
實體記憶體是電腦上的真實記憶體大小,這個資料可以透過BIOS獲取。
在分頁之後,實體記憶體的管理結構是個陣列,每項表示1個實體記憶體頁,每頁4096位元組。
如下圖:
實體記憶體的管理結構
在一個簡單的核心demo裡,實體記憶體頁的管理結構可以只有一項:
atomic_t refs;
即,實體記憶體頁的引用計數:計數為0表示空閒,> 0表示正在使用,具體數字表示共享這一頁的程式個數。
簡單的核心demo一般是不支援SMP架構的,所以自旋鎖(spinlock)也就省了。
在對稱多處理器(SMP)的CPU上,因為全域性資料結構會被多個CPU併發訪問,所以要加自旋鎖。
那麼,實體記憶體頁的管理結構至少有2項:
atomic_t spinlock; atomic_t refs;
自旋鎖的作用,與應用程式裡的鎖(mutex)差不多,只是它在獲取失敗之後會不斷地再次獲取,直到成功。
void spin_lock(atomic_t* lock) { while (spin_trylock(lock) == 0); }
這就是給自旋鎖加鎖的函式,while迴圈直到成功,不成功時就自旋在那裡一直轉圈,所以叫自旋鎖。
它在(對稱多處理器)SMP環境裡用於保護共享的資料結構:當一個CPU持有自旋鎖時,另一個CPU沒法訪問共享資料。
如果是單個CPU的環境,沒必要用自旋鎖,直接關閉中斷就行了。
單個CPU的情況下,關了中斷就可以阻止核心的併發,共享資料也就不會被踩踏了。
但多個CPU必須使用自旋鎖,因為關中斷只能關閉當前CPU的,沒法關其他CPU的:這時需要自旋鎖保護共享資料。
實體記憶體的管理陣列,是最重要的全域性共享資料。
當需要給一個程式申請記憶體的時候,哪個記憶體頁是空閒的,哪個已經被使用了,全靠檢視這個陣列。
加自旋鎖的時候一定要先關中斷,因為如果在加了鎖之後、關中斷之前、正好有個中斷來了,而在中斷處理函式里再次請求加同一個鎖,那就會遞迴死鎖了。
Linux核心的關中斷加鎖的函式叫:spin_lock_irqsave().
Linux核心的分配實體記憶體頁的函式叫:get_free_pages(),它可以分配1頁或連續的多頁記憶體。
如果分配多頁記憶體的話,起始地址是要按頁數對齊的。
虛擬記憶體都是透過程式的頁表管理的。
為了節省實體記憶體,新建立的程式是與父程式共享同一套實體記憶體頁的。
只有新程式要寫某個記憶體頁時,才會給它複製一份新的實體記憶體頁,然後取消該頁與父程式的共享,這就是寫時複製。
寫時複製的過程
寫時複製的過程:
1)申請一個新記憶體頁,
2)把老記憶體頁的內容,複製到新記憶體頁上,
3)把新記憶體頁的地址填入子程式的頁表,
4)把老記憶體頁的引用計數減1。
所以,新程式剛被建立出來時,它的使用者空間並沒有自己的實體記憶體頁,只有當執行需要時才一點點地透過寫時複製新增,以讓實體記憶體最大限度的空閒著。
另一個讓實體記憶體最大限度空閒著的機制,就是需求載入:
1)當mmap一個檔案時,作業系統並不會直接為這個檔案分配記憶體,並且把它的內容載入到記憶體裡,
2)而是當程式真去讀這個檔案的某一部分時,才給它申請實體記憶體頁,並且把這一部分內容從磁碟讀到記憶體。
copy on write,load on read.
不到火燒眉毛的時候, 是不會把實體記憶體給程式的
以上的這些機制都是OS核心裡的,應用程式的程式碼不需要管這些。
應用程式分配記憶體的最 底層函式,就是brk()系統呼叫。
brk()是一個系統呼叫,它的作用就是修改應用程式的資料段的結尾,從而分配或回收應用程式的堆空間。
C庫裡的把它封裝成了sbrk()和brk()兩個函式,讓它使用起來更符合人們的習慣:
sbrk()用於申請記憶體:void* sbrk(int increment);
brk()用於回收記憶體:int brk(void* addr);
實際上,Linux系統只有1個brk()系統呼叫,它既設定程式資料段的末尾,又會把這個值返回給應用程式。
Linux核心的標頭檔案裡,brk()系統呼叫的處理函式sys_brk()是這麼定義的,如上圖。
如果想直接使用系統呼叫,可以使用Linux的syscall()函式,依次傳入呼叫號和引數列表,就可以看到哪些是真實的系統呼叫,哪些是C庫的封裝。
syscall()函式的宣告是:long syscall(long number, ...);
它的引數是可變的,系統呼叫的引數最多隻有6個,因為暫存器的個數有限。
在sbrk() 和 brk()的基礎上再封裝,就是人們經常使用的malloc() 和 free()了。
malloc() 申請的記憶體是一塊塊的,可以不按次序釋放,而不影響使用。
brk() 和 sbrk() 申請的記憶體必須按次序釋放,因為它會修改程式的資料段結尾:
資料段結尾(brk)之外的堆空間如果被使用,就屬於段錯誤。
所以,Linux man手冊裡說明了,應用程式不要用sbrk()和brk()申請和釋放記憶體。
brk()的作用也只是通知Linux核心哪個範圍的堆記憶體是可用的,真正的實體記憶體頁是在程式實際讀寫記憶體的時候才會申請,而且是由核心根據寫時複製/需求載入自動完成的,應用程式感知不到這點。
Linux還會把不常用的實體記憶體頁交換到磁碟上(即swap分割槽),以騰出更多的記憶體。
所以,在記憶體不足時,磁碟的讀寫頻次也會升高。
原文來自:
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69955379/viewspace-2929380/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 作業系統-記憶體管理作業系統記憶體
- 作業系統——記憶體管理作業系統記憶體
- 作業系統的虛擬記憶體作業系統記憶體
- 作業系統記憶體管理概述作業系統記憶體
- 作業系統記憶體管理-原理作業系統記憶體
- 【作業系統】記憶體管理概述作業系統記憶體
- HP-UX作業系統的記憶體UX作業系統記憶體
- windows作業系統支援的最大記憶體Windows作業系統記憶體
- 作業系統-記憶體、檔案管理作業系統記憶體
- MySQL記憶體管理,記憶體分配器和作業系統MySql記憶體作業系統
- 淺談作業系統對記憶體的管理作業系統記憶體
- 作業系統——記憶體管理學習筆記作業系統記憶體筆記
- Linux作業系統記憶體淺析Linux作業系統記憶體
- 計算機作業系統——虛擬記憶體與實體記憶體計算機作業系統記憶體
- 作業系統(八) -- 記憶體的分段與分頁作業系統記憶體
- Android 作業系統的記憶體回收機制Android作業系統記憶體
- 作業系統的記憶體對齊機制學習筆記作業系統記憶體筆記
- 作業系統——記憶體連續分配管理方式作業系統記憶體
- WindowsXP作業系統記憶體最佳化指南(轉)Windows作業系統記憶體
- Kafka 的這些原理你知道嗎Kafka
- 記憶體瘋狂換頁!CPU怒批作業系統記憶體作業系統
- 作業系統思考 第六章 記憶體管理作業系統記憶體
- linux查詢作業系統資訊(CPU、記憶體、硬碟)Linux作業系統記憶體硬碟
- SQL Server記憶體遭遇作業系統程式壓榨案例SQLServer記憶體作業系統
- Linux作業系統:快速記憶體操作技術(轉)Linux作業系統記憶體
- 作業系統HugePage配置導致記憶體驟降探究作業系統記憶體
- Linux作業系統中記憶體buffer和cache的區別Linux作業系統記憶體
- linux作業系統修改共享記憶體的簡單方法(轉)Linux作業系統記憶體
- Linux作業系統記憶體管理的原始碼實現(轉)Linux作業系統記憶體原始碼
- SSL證書的工作原理你知道嗎?
- 管導與流作業系統作業系統
- 讀懂作業系統之虛擬記憶體頁表(五)作業系統記憶體
- 探索Linux 作業系統記憶體模型和管理-1(轉)Linux作業系統記憶體模型
- 探索Linux 作業系統記憶體模型和管理8(轉)Linux作業系統記憶體模型
- 作業系統(十) -- 段頁結合的實際記憶體管理模型作業系統記憶體模型
- 檢視CentOS伺服器的cpu、記憶體、作業系統版本資訊CentOS伺服器記憶體作業系統
- [zt] 如何使作業系統使用超過4G的記憶體作業系統記憶體
- 作業系統:x86下記憶體分頁機制 (1)作業系統記憶體