簡體   English   中英

Kernighan&Ritchie malloc自由邏輯

[英]Kernighan & Ritchie malloc free logic

我在free()實現中花了幾個小時處理一個特定的條件。 我搜索了網絡和stackoverflow,看看是否有其他人討論過這個,但我沒有找到。 我理解第二版(ANSI)第187頁和第188頁中描述的3個功能背后的一般概念。 我是一名專業人士,大部分代碼對我來說都很有意義,但是當我弄清楚所有3個函數如何在第一次調用時工作時(換句話說,將* freep初始化為某些東西),我感到難過。

以下是我從代碼中得出的步驟:步驟1.有兩個全局變量定義如下

typedef long Align;
union header { /* block header */
      struct {
      union header *ptr; /* next block if on free list */
      unsigned size; /* size of this block */
      } s;
      Align x; /* force alignment of blocks */
};
typedef union header Header;

   static Header base; /* empty list to get started */
   static Header *freep = NULL; /* start of free list */

第2步:首先通過malloc()函數,因為第一次,當用戶調用malloc()時,上面給出的2個全局變量將不包含任何可用數據。 我在本節下面的書中附上了malloc()的代碼。 我理解'nunits'是如何計算的。 如果freep == NULL,則將freep&prevp指向&base。 因此,首次輸入for循環,其中p == prevp-> s.ptr == base.s.ptr == freep == prevp ==&base(sic)。 下一個if條件為false,因為base.s.size在第一次調用期間已設置為零。 現在,下一個if條件(p == freep)為真,因為它們都指向'base'的地址。 因此,此時我們調用morecore(),這將在下面的步驟3中描述。

/* malloc: general-purpose storage allocator */
void *malloc(unsigned nbytes)
{
    Header *p, *prevp;
    Header *morecore(unsigned);
    unsigned nunits;
    nunits = (nbytes+sizeof(Header)-1)/sizeof(header) + 1;
    if ((prevp = freep) == NULL) { /* no free list yet */
          base.s.ptr = freeptr = prevptr = &base;
          base.s.size = 0;
    }
    for (p = prevp->s.ptr; ; prevp = p, p = p->s.ptr) {
         if (p->s.size >= nunits) { /* big enough */
               if (p->s.size == nunits) /* exactly */
                     prevp->s.ptr = p->s.ptr;
               else { /* allocate tail end */
                     p->s.size -= nunits;
                     p += p->s.size;
                     p->s.size = nunits
               }
               freep = prevp;
               return (void *)(p+1);
          }
          if (p == freep) /* wrapped around free list */
               if ((p = morecore(nunits)) == NULL)
                      return NULL; /* none left */
    } // end of for loop
}

第3步:morecore()

到目前為止,所有指針都指向&base(全局),我們輸入morecore()調用OS以獲得可用內存空間。 morecore()然后將此內存的開頭初始化為Header結構,如下所示:

   Header *up;
   up = the memory from OS
   up->s.size =  nu; (number of units, each of size = sizeof(Header))
   free( (void*) (up+1) );
   return freep;

在init之后,morecore()使用指向下一個地址的參數(在初始Header結構之后)調用free()。 morecore()不會改變freep global,它仍然指向&base。

第4步:輸入free()。

我的問題是自由功能,所以我只給出下面的相關行。

    void free( void* ap)
    {
         Header *bp, *p; // a pointer to base (ie. Header) and a temporary pointer p.
         bp = (Header *)ap - 1;  // point to block header
         for (p = freep;  !(bp > p && bp < p->s.ptr) ;  p = p->s.ptr)

              if ( p >= p->s.ptr && (bp > p || bp < p->s.ptr) )
                   break; // freed block at start or end of arena.

輸入for循環時,p == freep ==&base still。 在此之后我在條件方面遇到麻煩。 將首先執行for循環條件 - 將基本(全局)地址(p == freep ==&base still)與來自OS的新獲取空間的地址進行比較(注意 - 此地址通過free的參數來進行)。 在malloc中,我們將freep == p-> s.ptr初始化為&base。 因此,此時p和p-> s.ptr都指向相同的地址。 因此,for條件將為TRUE,因為bp不能同時為>和<相同的地址。 注意否定,這使得循環條件為TRUE,如果條件,我們輸入。

第5步:

if條件的第一部分是p> = p-> s.ptr。 這是正確的,因為p == freep ==&base == p-> s.ptr(在malloc中設置)。 if條件的第二部分為TRUE,因為bp必須是>或<相同的地址(p == p-> s.ptr)。 因此,我們打破for循環因為釋放的塊是競技場的開始(或競技場的結束,如果我們通過malloc維護的循環鏈接的免費記憶列表達到這一點)。

第6步:我的問題在這里

if(bp + bp->s.size == p->s.ptr) {          // join to upper nbr
     bp->s.size  += p->s.ptr->s.size;
     bp->s.ptr  =   p->s.ptr->s.ptr;  
}
else  bp->s.ptr = p->s.ptr;

繼續... for循環之后的第一個if條件為假,因為新分配的內存空間不等於p-> s.ptr,這是'base'的地址。 所以,執行else,並將新分配的頭設置為p-> s.ptr == freep-> s.ptr == freep ==&base !!! 為什么這個新指針被設置為'base'全局地址?

我的第二個問題是為什么2級間接(上面第3行代碼)設置bp-> s.ptr,以防條件滿足? (我知道這是加入連續的塊。)

STEP 7:加入較低的nbr(是書中的評論)

 if (p + p->s.size == bp) {
     p->s.size  += bp->s.size;
     p->s.ptr  =  bp->s.ptr;
 }
 else   p->s.ptr = bp;
 freep = p;

由於同樣的原因,在第一次傳遞期間,下一個if條件也將為false,而else子句將p-> s.ptr == base.s.ptr == freep-> s.ptr設置為新分配的內存。 那么,在我們第一次通過malloc / morecore / free序列時,下一個語句freep = p是多余的? 在所有這些步驟結束時,freep仍然指向'base'的地址? 沒有變化 ? 感覺像我在邏輯上犯了一些錯誤。

關於ANSI標准C關於工會的說法的一點澄清。 這可以追溯到上面第一個帶有'typedef'的代碼snipet。 我認為靜態聯合中的所有成員(比如靜態Header基礎)都被編譯器初始化為零? 如果我沒有將它們聲明為靜態怎么辦? 在這種情況下struct s.header ptr是否為NULL?

如果我在第7步的邏輯中犯了任何錯誤,請指出第一遍。 我還要感謝,如果有人花時間寫清楚(我已經完成的方式)free()如何工作以及free()中各種條件的偽代碼。 這個線程對學生/新手非常有用。

感謝您的時間和解釋。

為什么這個新指針被設置為'base'全局地址?

在malloc實現的描述中,據說最后一個塊具有指向第一個塊的指針。 並且由於第一次調用函數,你沒有任何塊,因此這個塊指向自身。

那么,在我們第一次通過malloc / morecore / free序列時,下一個語句freep = p是多余的? 在所有這些步驟結束時,freep仍然指向'base'的地址? 沒有變化 ? 感覺像我在邏輯上犯了一些錯誤。

我相信,在第一次調用malloc時,你的推理中沒有關於freep = p = &base錯誤。 我想,通常這段代碼非常棘手。 在這個問題中( 從K&R書中解釋malloc的這種實現 ),您可以找到更多關於k&r malloc()實現的批評

但對於你的情況,我認為名字free()讓你感到困惑。 因為函數free()實際上有兩個目的:初始化全新的自由列表並顯然釋放以前分配的內存。 S.Macconnell的“代碼完整”說,具有多種用途的功能並不是一個好主意。

所以在第一遍中,實際上你不需要一些額外的任務(初始化free()制度)。 但是在下一次free調用(實際釋放先前分配的內存的方式)對於指向最后一塊空閑列表內存以便合並內存很有用。

關於ANSI標准C關於工會的說法的一點澄清。 這可以追溯到上面第一個帶有'typedef'的代碼snipet。 我認為靜態聯合中的所有成員(比如靜態Header基礎)都被編譯器初始化為零?

是的,引用c89草案 (3.5.7。初始化):

如果沒有顯式初始化具有靜態存儲持續時間的對象,則會隱式初始化它,就好像每個具有算術類型的成員都被賦值為0,並且每個具有指針類型的成員都被賦予空指針常量。 如果未顯式初始化具有自動存儲持續時間的對象,則其值不確定。


如果我沒有將它們聲明為靜態怎么辦? 在這種情況下struct s.header ptr是否為NULL?

我相信這將是垃圾。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM