0x01:簡介
在堆中的記憶體申請和釋放中,為了減少使用系統呼叫函式對記憶體操作,malloc_state(分配區)結構中使用了fastbinsY陣列和bins陣列。當chunk被free後,bins鏈會將這些free chunk組織起來。當下次malloc時,會先對bins鏈中的free chunk進行遍歷,有適合的則使用,無合適的再進行下一步操作。
在申請和釋放操作時,一般是有一個arena(分配區,其為malloc_state結構型別)。通過巨集定義bin_at(m,i)即可獲得對相應bins鏈進行操作,m為分配區,i為索引,當i=1是為unsorted bin,i=2~63是small bin,i=64~126為large bin。
bin_at可以這樣使用: p = bin_at(m,1) //unsorted bin FD = p -> fd //FD指向該鏈的第一個free chunk BK = p -> bk //BK指向該鏈的最後一個free chunk
0x02:個人理解
該巨集定義為這樣子,主要是為了操作方便(可直接fd、bk指向)和在節省空間之間取平衡。既操作方便又不浪費空間。
bin_at(m,i)巨集定義原型:
#define bin_at(m, i) \ (mbinptr) (((char *) &((m)->bins[((i) - 1) * 2])) \ - offsetof (struct malloc_chunk, fd))
解釋:
1、&((m)->bins[((i)-1)*2]),該式子根據bin的索引i,i為1時,即最開始的bin(unsorted bin)。
獲得bins[0]的地址,乘於2主要是因為fd和bk是一對儲存的。
2、offsetof(struct malloc_chunk,fd):得到fd成員在malloc_chunk結構中的偏移量。在64位系統系統下為16。
把第一步得到地址(令其為pt)轉化為char*型,這樣子減偏移值: pt - offsetof = pt - offsetof*(sizeof(char))。
3、重點:
在第二步下,第一步得到的地址指向往後推移了兩個單元,比如圖中的bin_at(m,1)得到的是bins[0]前兩個單元所在的地址。
然後經過最後的(mbinptr)轉化為malloc_chunk*型別,這樣就可以有->fd、->bk操作。
相當於是糊弄了作業系統,讓作業系統誤以為是個chunk結構。
語言表達不夠,畫圖來湊~~
0x03:測試
1 測試程式碼: 2 #include<stdio.h> 3 #include<malloc.h> 4 5 int main() 6 { 7 void* p1 = malloc(0x100); 8 void* f1 = malloc(0x10); //防止合併 9 void* p2 = malloc(0x100); 10 void* f2 = malloc(0x10); 11 void* p3 = malloc(0x100); 12 void* f3 = malloc(0x10); 13 14 sleep(0); //方便下斷點 15 16 free(p1); 17 free(p2); 18 free(p3); 19 20 sleep(0); //下圖斷在了這裡 21 return 0; 22 }
可以看出unsorted bin的第一個單元是指向bin鏈的第一個free chunk,另外一個單元指向bin鏈的最後一個free chunk。圖中我已經標出了指向top chunk的那個單元,也有:bin_at(m,1)指向該單元 ,或者更本質一點:bin_at(m,1) = 0x7ffffdd1b78。
0x04:思考
在剛開始看這個巨集定義時,一臉懵逼,覺得這個巨集定義是有問題吧,減去16不是超出了範圍了麼?後來繼續分析原始碼時發現了->fd和->bk的操作,陷入了沉思。巧的是,群裡有師傅剛好提到這個問題,所以和該師傅交流了一下,最後得到了一個比較合理的解釋。
但是令我疑惑的是,為什麼在後面會留兩個空白單元嘞?留在前面不是更恰當麼?bin_at(m,1)指向了bins陣列外面的單元(存top chunk地址的單元),這樣會不會存有遺患呢?有想法的歡迎與我交流^_^,QQ:1623093551。
tolele 2022-03-16