【Linux應用開發】malloc記憶體分配原理
如何檢視程序發生缺頁中斷的次數?
用ps -o majflt,minflt -C program命令檢視。
majflt代表major fault,中文名叫大錯誤,minflt代表minor fault,中文名叫小錯誤。
這兩個數值表示一個程序自啟動以來所發生的缺頁中斷的次數。
發成缺頁中斷後,執行了那些操作?
當一個程序發生缺頁中斷的時候,程序會陷入核心態,執行以下操作:
1、檢查要訪問的虛擬地址是否合法
2、查詢/分配一個物理頁
3、填充物理頁內容(讀取磁碟,或者直接置0,或者啥也不幹)
4、建立對映關係(虛擬地址到實體地址)
如果第3步,需要讀取磁碟,那麼這次缺頁中斷就是majflt,否則就是minflt。
從作業系統角度來看,程序分配記憶體有兩種方式,分別由兩個系統呼叫完成:brk和mmap(不考慮共享記憶體)。
1、brk是將資料段(.data)的最高地址指標_edata往高地址推;
2、mmap是在程序的虛擬地址空間中(堆和棧中間,稱為檔案對映區域的地方)找一塊空閒的虛擬記憶體。
這兩種方式分配的都是虛擬記憶體,沒有分配實體記憶體。在第一次訪問已分配的虛擬地址空間的時候,發生缺頁中斷,作業系統負責分配實體記憶體,然後建立虛擬記憶體和實體記憶體之間的對映關係。上面兩種都是在glibc(應用層)中實現的,改變的都是應用層中堆的大小。
在標準C庫中,提供了malloc/free函式分配釋放記憶體,而這兩個函式底層是由brk,mmap,munmap這些系統呼叫實現的。核心中沒有上面這些概念,後面會提到。
下面以一個例子來說明記憶體分配的原理:
情況一、malloc小於128k的記憶體,使用brk分配記憶體,將_edata往高地址推(只分配虛擬空間,不對應實體記憶體(因此沒有初始化),第一次讀/寫資料時,引起核心缺頁中斷,核心才分配對應的實體記憶體,然後虛擬地址空間建立對映關係),如下圖:
1、程序啟動的時候,其(虛擬)記憶體空間的初始佈局如圖1所示。 其中,mmap記憶體對映檔案是在堆和棧的中間(例如libc-2.2.93.so,其它資料檔案等),為了簡單起見,2、程序呼叫A=malloc(30K)以後,記憶體空間如圖2: malloc函式會呼叫brk系統呼叫,將_edata指標往高地址推30K,就完成虛擬記憶體分配。 你可能會問:只要把_edata+30K就完成記憶體分配了? 事實是這樣的,_edata+30K只是完成虛擬地址的分配,A這塊記憶體現在還是沒有物理頁與之對應的,等到程序第一次讀寫A這塊記憶體的時候,發生缺頁中斷,這個時候,核心才分配A這塊記憶體對應的物理頁。也就是說,如果用malloc分配了A這塊內容,然後從來不訪問它,那麼,A對應的物理頁是不會被分配的。
3、程序呼叫B=malloc(40K)以後,記憶體空間如圖3。
情況二、malloc大於128k的記憶體,使用mmap分配記憶體,在堆和棧之間找一塊空閒記憶體分配(對應獨立記憶體,而且初始化為0),如下圖:
5、程序呼叫D=malloc(100K)以後,記憶體空間如圖5;
6、程序呼叫free(C)以後,C對應的虛擬記憶體和實體記憶體一起釋放。 7、程序呼叫free(B)以後,如圖7所示: B對應的虛擬記憶體和實體記憶體都沒有釋放,因為只有一個_edata指標,如果往回推,那麼D這塊記憶體怎麼辦呢? 當然,B這塊記憶體,是可以重用的,如果這個時候再來一個40K的請求,那麼malloc很可能就把B這塊記憶體返回回去了。
8、程序呼叫free(D)以後,如圖8所示: B和D連線起來,變成一塊140K的空閒記憶體。 9、預設情況下: 當最高地址空間的空閒記憶體超過128K(可由M_TRIM_THRESHOLD選項調節)時,執行記憶體緊縮操作(trim)。在上一個步驟free的時候,發現最高地址空閒記憶體超過128K,於是記憶體緊縮,變成圖9所示。