简体   繁体   English

Kernighan&Ritchie malloc自由逻辑

[英]Kernighan & Ritchie malloc free logic

I have spent hours on one particular condition in free() implementation. 我在free()实现中花了几个小时处理一个特定的条件。 I have searched the web and stackoverflow to see if anyone else discussed this, but i found none. 我搜索了网络和stackoverflow,看看是否有其他人讨论过这个,但我没有找到。 I understand the general idea behind the 3 functions described in page 187 & 188 of the second edition (ANSI). 我理解第二版(ANSI)第187页和第188页中描述的3个功能背后的一般概念。 I am a professional and most of the code makes sense to me, but i am stumped when i work out how all 3 functions would work for the very first call (in other words, initialization of *freep to something). 我是一名专业人士,大部分代码对我来说都很有意义,但是当我弄清楚所有3个函数如何在第一次调用时工作时(换句话说,将* freep初始化为某些东西),我感到难过。

Here are the steps that i worked out from the code: Step 1. There are two global variables defined as below 以下是我从代码中得出的步骤:步骤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 */

STEP 2: Went through the malloc() function first, because the very first time, when the user calls malloc() the 2 globals given above will not contain any usable data. 第2步:首先通过malloc()函数,因为第一次,当用户调用malloc()时,上面给出的2个全局变量将不包含任何可用数据。 I have attached the code for malloc() from the book, below this section. 我在本节下面的书中附上了malloc()的代码。 I understand how 'nunits' is calculated. 我理解'nunits'是如何计算的。 If freep == NULL, freep & prevp are made to point to &base. 如果freep == NULL,则将freep&prevp指向&base。 So, the for loop is entered for the very first time with p == prevp->s.ptr == base.s.ptr == freep == prevp == &base (sic). 因此,首次输入for循环,其中p == prevp-> s.ptr == base.s.ptr == freep == prevp ==&base(sic)。 The next if condition is false because base.s.size has been set to zero during the very first call. 下一个if条件为false,因为base.s.size在第一次调用期间已设置为零。 Now, the next if condition (p == freep) is true because both are pointing still to the address of 'base'. 现在,下一个if条件(p == freep)为真,因为它们都指向'base'的地址。 So, at this point we make a call to morecore() which is described in step 3 below. 因此,此时我们调用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
}

STEP 3: morecore() 第3步:morecore()

So far, all the pointers are pointing to &base (global) and we enter morecore() which calls the OS to get free memory space. 到目前为止,所有指针都指向&base(全局),我们输入morecore()调用OS以获得可用内存空间。 morecore() then initializes the beginning of this memory to be the Header structure, as below: 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;

After init, morecore() calls free() with argument that points to the next address (after the initial Header structure). 在init之后,morecore()使用指向下一个地址的参数(在初始Header结构之后)调用free()。 morecore() does not change freep global, which still points to &base. morecore()不会改变freep global,它仍然指向&base。

STEP 4 : free() is entered. 第4步:输入free()。

My question is in free function, so i give only the relevant lines below. 我的问题是自由功能,所以我只给出下面的相关行。

    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.

The for loop is entered with p == freep == &base still. 输入for循环时,p == freep ==&base still。 I have trouble in the conditionals after this point. 在此之后我在条件方面遇到麻烦。 The for loop conditional will be executed first - base (global) address (p== freep == &base still) is compared with the address of newly obtained space from OS (Note - this address comes through the argument of free). 将首先执行for循环条件 - 将基本(全局)地址(p == freep ==&base still)与来自OS的新获取空间的地址进行比较(注意 - 此地址通过free的参数来进行)。 In malloc we initialized freep == p->s.ptr to &base. 在malloc中,我们将freep == p-> s.ptr初始化为&base。 So, at this point both p and p->s.ptr are pointing to the same address. 因此,此时p和p-> s.ptr都指向相同的地址。 So, the for condition will be TRUE because bp can not be both > and < the same address. 因此,for条件将为TRUE,因为bp不能同时为>和<相同的地址。 Note the negation, which makes the loop condition TRUE and we enter if condition. 注意否定,这使得循环条件为TRUE,如果条件,我们输入。

STEP 5: 第5步:

The first part of the if condition is p >= p->s.ptr. if条件的第一部分是p> = p-> s.ptr。 This is TRUE because p == freep == &base == p->s.ptr (which was set in malloc). 这是正确的,因为p == freep ==&base == p-> s.ptr(在malloc中设置)。 The second part of if condition is TRUE because bp has to either > or < the same address(p == p->s.ptr). if条件的第二部分为TRUE,因为bp必须是>或<相同的地址(p == p-> s.ptr)。 So, we break out of for loop because freed block is at start of arena (or end of arena, if we reach this point through the circularly linked list of free memories maintained by malloc). 因此,我们打破for循环因为释放的块是竞技场的开始(或竞技场的结束,如果我们通过malloc维护的循环链接的免费记忆列表达到这一点)。

STEP 6: my questions are here 第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;

Continuing on...the first if condition after for loop is false because the newly allocated memory space is not going to equal p->s.ptr, which is address of 'base'. 继续... for循环之后的第一个if条件为假,因为新分配的内存空间不等于p-> s.ptr,这是'base'的地址。 So, else is executed and the newly allocated header is set to p->s.ptr == freep->s.ptr == freep == &base !!! 所以,执行else,并将新分配的头设置为p-> s.ptr == freep-> s.ptr == freep ==&base !!! Why would this new pointer be set to the address of 'base' global ? 为什么这个新指针被设置为'base'全局地址?

My second question is why the 2 level indirection (3rd line of code above) to set bp->s.ptr, in case if condition is satisfied ? 我的第二个问题是为什么2级间接(上面第3行代码)设置bp-> s.ptr,以防条件满足? (I understand this is to join contiguous blocks.) (我知道这是加入连续的块。)

STEP 7: join the lower nbr (is the comment from book) 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;

The next if condition will also be false for the same reason, during the very first pass, and else clause sets p->s.ptr == base.s.ptr == freep->s.ptr to the newly alloced memory. 由于同样的原因,在第一次传递期间,下一个if条件也将为false,而else子句将p-> s.ptr == base.s.ptr == freep-> s.ptr设置为新分配的内存。 So, the next statement freep = p is redundant during the very first time we go through this sequence of malloc/morecore/free ? 那么,在我们第一次通过malloc / morecore / free序列时,下一个语句freep = p是多余的? At end of all these steps, freep still points to the address of 'base' ? 在所有这些步骤结束时,freep仍然指向'base'的地址? No changes ? 没有变化 ? Feels like i have made some mistake in logic. 感觉像我在逻辑上犯了一些错误。

One point of clarification on what ANSI standard C says about unions. 关于ANSI标准C关于工会的说法的一点澄清。 This goes back to the very first code snipet i have above with the 'typedef'. 这可以追溯到上面第一个带有'typedef'的代码snipet。 I think all the members inside a static union (like static Header base) are initialized by compiler to zero ? 我认为静态联合中的所有成员(比如静态Header基础)都被编译器初始化为零? What if i didn't declare them as static ? 如果我没有将它们声明为静态怎么办? Would the struct s.header ptr be NULL in this case ? 在这种情况下struct s.header ptr是否为NULL?

Please point out, if i made any errors in my logic of 7 steps, for the first pass. 如果我在第7步的逻辑中犯了任何错误,请指出第一遍。 I would also appreciate, if someone took the time to write down clearly (the way i have done) how free() works and the pseudo code for the various conditionals within free(). 我还要感谢,如果有人花时间写清楚(我已经完成的方式)free()如何工作以及free()中各种条件的伪代码。 This thread would then become very useful for students / newbies. 这个线程对学生/新手非常有用。

Thank you for your time and explanation. 感谢您的时间和解释。

Why would this new pointer be set to the address of 'base' global ? 为什么这个新指针被设置为'base'全局地址?

In description of malloc realization, it's said that last block has pointer to the first block. 在malloc实现的描述中,据说最后一个块具有指向第一个块的指针。 And because of first invocation of function you don't have any blocks so this block points on itself. 并且由于第一次调用函数,你没有任何块,因此这个块指向自身。

So, the next statement freep = p is redundant during the very first time we go through this sequence of malloc/morecore/free ? 那么,在我们第一次通过malloc / morecore / free序列时,下一个语句freep = p是多余的? At end of all these steps, freep still points to the address of 'base' ? 在所有这些步骤结束时,freep仍然指向'base'的地址? No changes ? 没有变化 ? Feels like i have made some mistake in logic. 感觉像我在逻辑上犯了一些错误。

I believe, that there is no mistake in your reasoning about freep = p = &base in the first invocation of malloc. 我相信,在第一次调用malloc时,你的推理中没有关于freep = p = &base错误。 I think, generally this code is pretty tricky. 我想,通常这段代码非常棘手。 In this question ( Explain this implementation of malloc from the K&R book ) you can find more critiques of k&r malloc() realization 在这个问题中( 从K&R书中解释malloc的这种实现 ),您可以找到更多关于k&r malloc()实现的批评

But for your case I think name free() confused you a little. 但对于你的情况,我认为名字free()让你感到困惑。 Because function free() actually has two purposes: initializing brand new free-list and obviously freeing previously allocated memory. 因为函数free()实际上有两个目的:初始化全新的自由列表并显然释放以前分配的内存。 Also "Code Complete" by S.Macconnell says that having function with several purposes is not good idea. S.Macconnell的“代码完整”说,具有多种用途的功能并不是一个好主意。

So in the first pass yes actually you don't need some additional assignments (initialize regime of free() ). 所以在第一遍中,实际上你不需要一些额外的任务(初始化free()制度)。 But in next invocations of free (regime of real freeing previously allocated memory) is useful to have pointer to last block of free-list memory for coalescing of memory. 但是在下一次free调用(实际释放先前分配的内存的方式)对于指向最后一块空闲列表内存以便合并内存很有用。

One point of clarification on what ANSI standard C says about unions. 关于ANSI标准C关于工会的说法的一点澄清。 This goes back to the very first code snipet i have above with the 'typedef'. 这可以追溯到上面第一个带有'typedef'的代码snipet。 I think all the members inside a static union (like static Header base) are initialized by compiler to zero ? 我认为静态联合中的所有成员(比如静态Header基础)都被编译器初始化为零?

Yes, cite from c89 draft (3.5.7. Initialization): 是的,引用c89草案 (3.5.7。初始化):

If an object that has static storage duration is not initialized explicitly, it is initialized implicitly as if every member that has arithmetic type were assigned 0 and every member that has pointer type were assigned a null pointer constant. 如果没有显式初始化具有静态存储持续时间的对象,则会隐式初始化它,就好像每个具有算术类型的成员都被赋值为0,并且每个具有指针类型的成员都被赋予空指针常量。 If an object that has automatic storage duration is not initialized explicitly, its value is indeterminate. 如果未显式初始化具有自动存储持续时间的对象,则其值不确定。


What if i didn't declare them as static ? 如果我没有将它们声明为静态怎么办? Would the struct s.header ptr be NULL in this case ? 在这种情况下struct s.header ptr是否为NULL?

I believe it will be garbage. 我相信这将是垃圾。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM