进程是如何使用内存的?
程序運行概述
程序(我們這里只討論單進程情況,存在多進程的程序如淘寶微信等不展開討論)鏡像存在磁盤中,運行時將鏡像加載至內存RAM中,然后開始執行。
先來看一下CPU的多級存儲結構,CPU通用寄存器訪問速度最快,其次是Cache,再次是內存,磁盤訪問速度最慢。
CPU的多級存儲結構
對于進程而言,可使用的地址空間為2^32=4G,那么對于只有2G內存甚至只有256M內存的嵌入式設備怎么辦?這個時候就需要MMU負責將進程的虛擬地址轉換為內存的真實物理地址。如何管理這種映射關系呢?內核中為每個進程分配了進程頁表。對應下圖可以看到,只有正在使用的虛擬地址空間才會真正分配到物理頁框;同時對于內核空間地址映射對于不同進程物理地址一樣。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?進程地址映射
TLB工作原理
上圖中不同進程映射到物理內存使用到了TLB表(地址變換高速緩存),流程為:CPU獲取數據或指令:獲取進程頁表,拿到物理地址;訪問內存物理地址拿到真實數據。
每次執行指令,先從TLB表中獲取,如果未命中,則從內存中獲取,同時根據LRU方法更新TLB表。這里提一下,LRU更新方法在很多地方都用得到,像CDN頁面緩存、CPU cache緩存等,如何消除cache顛簸的影響,是另一個話題。
局部性原理:時間及空間局部性,即CPU訪問某個邏輯地址的數據時,大概率會繼續訪問該虛擬地址相鄰的地址。因此為了保證cache的命中率,一般CPU采用多級流水線設計,將預取指令及相鄰的地址空間存放至TLB表中。
每個核都有自己的TLB,由MMU內存管理單元模塊執行,流程為:CPU發送執行進程的虛擬地址給MMU模塊,MMU對應的硬件電路獲取TLB表物理地址并訪問數據。
如果進程使用了4G虛擬地址,那么所需要的頁表項條目為:
4G/4K=1M
每個頁表項為4Byte,因此進程頁表占據了4M物理內存空間。這種狀態下可以使用多級頁表減少內存占用,且可以離散存儲。
?
====
從malloc說起
使用malloc分配16k空間,這16k不會立即占用16k真實內存,而是采用寫時復制的方式使用。如果物理內存已經寫滿數據怎么辦(可能被多個進程占用)?這個時候就繼續使用上面提到的LRU方式進行頁表置換。置換過程中會將換出的頁表真實寫入磁盤中,一般由daemon守護進程完成。
前面使用malloc分配空間之后,linux內核并未真正給該進程分配物理頁,如果對該地址進行寫動作,會由MMU觸發缺頁中斷,這時進入內核終端處理程序,將數據從磁盤加載至內存。
?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? CPU尋址流程
下面總結一下CPU尋址流程:CPU將進程虛擬地址通過地址總線發送給MMU,由MMU硬件電路轉換成物理地址,然后通過數據總線訪問內存獲取數據。
CPU獲取物理地址流程如下:
1、CPU發送虛擬地址,MMU查詢自身TLB表,如果命中:根據物理地址訪問數據頁;如果不命中,通過cr3寄存器取出進程頁表物理地址訪問進程頁表。
2、這里訪問進程頁表先是從Cache中獲取緩存頁框,如果Cache命中:從Cache中獲取得到物理地址,更新TLB表。如果不命中,直接訪問內存頁表。
3、進程頁表保存了進程使用過程中所有的虛擬地址對應的表項,因此通過頁表地址偏移可直接獲取到頁表項,并更新至Cache中。
4、繼續第一步,MMU從Cache中獲取頁表項,并查看虛擬地址是否已分配物理頁框。若分配,則使用LRU算法更新TLB表并通過內存物理地址訪問數據;若沒分配物理頁框,則觸發Page Fault缺頁中斷,進入并執行中斷接管程序。
5、中斷處理程序為發送的虛擬地址分配真實物理頁框,如果內存數據已滿,根據LRU算法淘汰最久未用的頁面,置換磁盤的進程映像至物理頁框,并更新進程頁表。(用戶空間的缺頁中斷還會判斷是否非法訪問等權限校驗,這里不展開)
6、中斷處理程序返回,CPU獲取執行權,繼續執行指令。
獲取到了物理地址,根據物理地址獲取實際數據流程為:MMU通過物理地址查詢Cache,如果緩存命中,CPU直接獲取Cache中數據并繼續執行;如果不命中,那么根據物理地址獲取內存中的數據,硬件電路將物理頁框存入Cache中,CPU從Cache中獲取數據。
?
CPU獲取數據流程
理想狀態下,當進程局部性較高時,如執行while循環,MMU獲取TLB表命中拿到物理地址,通過物理地址訪問Cache命中拿到真實數據。
?
具體示例代碼分析代碼數據流及執行流
char* ptr =malloc(1*1024*1024); //1、分配內存 memcpy(ptr, 'a',10); //2、寫入數據 ptr[100] = 'b'; //3、賦值第一行:char* ptr =malloc(1*1024*1024);
假定malloc分配的虛擬地址是0x00000040,表示【0x00000040-0x00100040】這1M進程虛擬空間被分配成功。接下來我們看一下進程頁表情況。
0x00000040? ->??64
0x00100040? ->??1048640
也就是這段虛擬空間占了1048576塊頁表項。這里注意,如果是一級頁表,不管有沒有執行malloc,這些頁表項都存在,但如果是多級頁表,只有真實訪問數據的時候這些頁表才會占用物理內存空間。接下來計算頁號和頁內偏移:
起始地址虛擬頁號:64/(4*1024)= 0,頁內偏移為64%(4*1024)= 0x40;
結束地址虛擬頁號:1048640/(4*1024)= 256,頁內偏移為1048640%(4*1024)= 0x40;
第二行:memcpy (ptr,'g', 10);
根據ptr虛擬地址找到頁表項,發現并沒有分配物理頁框,觸發缺頁中斷后分配得到第100頁框。然后CPU將ptr對應虛擬地址往后的10字節空間寫為‘a’。這里說一下,雖然進程頁號對應的物理頁框序號不一定相同,因為頁框大小為4k,所以虛擬地址在進程頁號中的偏移等于映射的物理地址在物理頁框中的偏移。
CPU根據虛擬地址按照上一章的流程找到對應物理地址,將第100物理頁框加載至Cache中,CPU將10字節’a’寫入Cache。
第三行:ptr[100] = 'b';
根據局部性原理,這里訪問的就是同一個虛擬地址附近的數據,因此TLB和Cache都命中。
可以看到,進程在執行malloc時是將物理頁框直接分配給虛擬地址,里面的初始數據有內存的電氣特性決定,是隨機值,因此maoolc出來的空間使用前需要賦值或者執行初始化操作。
推薦閱讀:
專輯|Linux文章匯總
專輯|程序人生
專輯|C語言
我的知識小密圈
關注公眾號,后臺回復「1024」獲取學習資料網盤鏈接。
歡迎點贊,關注,轉發,在看,您的每一次鼓勵,我都將銘記于心~
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的进程是如何使用内存的?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 你可能对电灯泡一无所知
- 下一篇: Android 编程经典200例 (pd