【Linux】malloc和free底層的簡單實現!!!
從作業系統角度來看,程序分配記憶體有兩種方式,分別由兩個系統呼叫完成:brk
和mmap
(當然在這裡是不考慮共享記憶體)
- brk是將資料段(.data)的最高地址指標_edata往高地址推;
mmap是在程序的虛擬地址空間中(堆和棧中間,稱為檔案對映區域的地方)找一塊空閒的虛擬記憶體。
注:在開闢空間的時候只是在程序的虛擬地址空間內開闢指定大小的記憶體,但是並沒有實際在實體記憶體上面開闢空間,只有當對開闢的記憶體空間進行訪問的時候才會在實體記憶體上面開闢實際的空間。例如:假如開闢的空間一直沒有使用,那麼在實體記憶體上面始終沒有實際的空間。
在這之間當對開闢的記憶體進行第一次訪問的時候會發生缺頁中斷,這時候系統就會在指定的空間進行開闢記憶體,然後再將實體記憶體的地址對映到虛擬地址上面。
當發生缺頁中斷的時候系統會幹什麼?
1、檢查要訪問的虛擬地址是否合法
2、查詢/分配一個物理頁
3、填充物理頁內容(讀取磁碟,或者直接置0,或者啥也不幹)
4、建立對映關係(虛擬地址到實體地址)
情況1:
當使用malloc小於128k的記憶體,使用brk分配記憶體,將_edata往高地址推(只分配虛擬空間,不對應實體記憶體(因此沒有初始化),第一次讀/寫資料時,引起核心缺頁中斷,核心才分配對應的實體記憶體,然後虛擬地址空間建立對映關係)
在開闢較小的空間的時候會直接將_edata暫存器的指向向髙地址一定指定大小的位置,然後將這個區域對映到虛擬記憶體地址空間內。但是這時候這個位置並沒有實際的實體記憶體,只有當使用到這塊記憶體的時候才會在實體記憶體上面開闢空間。
情況2:
當開闢的空間大小大於128K的時候並不是使用brk系統來移動_edata指向來開闢空間,而是使用mmap系統在堆疊之間的共享區直接開闢指定大小的空間(虛擬空間)。
同樣,在開闢空間之後並沒有實際的實體記憶體空間,當使用的時候才會初始化。
free
當需要銷燬的時候,使用mmap開闢的空間會直接釋放,如果在實體記憶體上面開闢的有實際的記憶體的話也會一起釋放。當時使用brk開闢的記憶體只能依次釋放,比如:釋放b的時候可以直接釋放虛擬記憶體和實體記憶體,但是在釋放a的時候如果在a的上面還有開闢的空間,那麼a這塊空間並不會被釋放,因為在a的上面還有開闢的空間,但是a這塊空間是可以複用的,那麼如果在下次開闢空間的時候又開闢了和啊大小的空間,那麼很有可能系統就會直接將a這塊空間對映到虛擬空間內。
因為使用brk開闢的記憶體必須等髙地址的記憶體釋放之後才會釋放,這也是記憶體碎片產生的原因。
釋放a:
釋放b:
當c也釋放的時候,那麼_edata指向就會重新回退到含有開闢空間的位置上面。