24 Sep 2019

tcache

tcache

  • 结构体

    • tcache_entry

      /* We overlay this structure on the user-data portion of a chunk when
         the chunk is stored in the per-thread cache.  */
      typedef struct tcache_entry
      {
        struct tcache_entry *next;
      } tcache_entry;
      

      链接chunk,next指向下一个size相同的chunk

    • tcache_perthread_struct

      /* There is one of these for each thread, which contains the
         per-thread cache (hence "tcache_perthread_struct").  Keeping
         overall size low is mildly important.  Note that COUNTS and ENTRIES
         are redundant (we could have just counted the linked list each
         time), this is for performance reasons.  */
      typedef struct tcache_perthread_struct
      {
        char counts[TCACHE_MAX_BINS];
        tcache_entry *entries[TCACHE_MAX_BINS];
      } tcache_perthread_struct;
      
      # define TCACHE_MAX_BINS		64
      

      管理tcache,有64个entries,counts记录每条链上的chunk数量,每条链上最多有7个chunk

  • malloc

    void *
    __libc_malloc (size_t bytes)
    {
    //...
    #if USE_TCACHE
      /* int_free also calls request2size, be careful to not pad twice.  */
      size_t tbytes;
      checked_request2size (bytes, tbytes);
      size_t tc_idx = csize2tidx (tbytes);
      
      MAYBE_INIT_TCACHE ();
      
      DIAG_PUSH_NEEDS_COMMENT;
      if (tc_idx < mp_.tcache_bins
          /*&& tc_idx < TCACHE_MAX_BINS*/ /* to appease gcc */
          && tcache
          && tcache->entries[tc_idx] != NULL)
        {
          return tcache_get (tc_idx);
        }
      DIAG_POP_NEEDS_COMMENT;
    #endif
    //...
    }
    libc_hidden_def (__libc_malloc)
    

    MAYBE_INIT_TCACHE (),第一次malloc时调用tcache_init

    static void
    tcache_init(void)
    {
      mstate ar_ptr;
      void *victim = 0;
      const size_t bytes = sizeof (tcache_perthread_struct);
      
      if (tcache_shutting_down)
        return;
      
      arena_get (ar_ptr, bytes);
      victim = _int_malloc (ar_ptr, bytes);
      if (!victim && ar_ptr != NULL)
        {
          ar_ptr = arena_get_retry (ar_ptr, bytes);
          victim = _int_malloc (ar_ptr, bytes);
        }
      
      
      if (ar_ptr != NULL)
        __libc_lock_unlock (ar_ptr->mutex);
      
      /* In a low memory situation, we may not be able to allocate memory
         - in which case, we just keep trying later.  However, we
         typically do this very early, so either there is sufficient
         memory, or there isn't enough memory to do non-trivial
         allocations anyway.  */
      if (victim)
        {
          tcache = (tcache_perthread_struct *) victim;
          memset (tcache, 0, sizeof (tcache_perthread_struct));
        }
      
    }
    

    申请大小sizeof(tcache_perthread_struct)的chunk,创建tcache_perthread_struct

      if (tc_idx < mp_.tcache_bins
          /*&& tc_idx < TCACHE_MAX_BINS*/ /* to appease gcc */
          && tcache
          && tcache->entries[tc_idx] != NULL)
        {
          return tcache_get (tc_idx);
        }
      DIAG_POP_NEEDS_COMMENT;
    

    tc_idx合法,tcache->entries[tc_idx]不为空时,tcache_get (tc_idx)获取chunk

    /* Caller must ensure that we know tc_idx is valid and there's
       available chunks to remove.  */
    static __always_inline void *
    tcache_get (size_t tc_idx)
    {
      tcache_entry *e = tcache->entries[tc_idx];
      assert (tc_idx < TCACHE_MAX_BINS);
      assert (tcache->entries[tc_idx] > 0);
      tcache->entries[tc_idx] = e->next;
      --(tcache->counts[tc_idx]);
      return (void *) e;
    }
    

    tc_idx取出chunk,count减1

  • free

    void
    __libc_free (void *mem)
    {
      mstate ar_ptr;
      mchunkptr p;                          /* chunk corresponding to mem */
      
      
    //...
      MAYBE_INIT_TCACHE ();
      
      ar_ptr = arena_for_chunk (p);
      _int_free (ar_ptr, p, 0);
    }
    libc_hidden_def (__libc_free)
    

    只多了一个MAYBE_INIT_TCACHE ();

    _int_free()

    static void
    _int_free (mstate av, mchunkptr p, int have_lock)
    {
    //...
      
      size = chunksize (p);
    //...
    #if USE_TCACHE
      {
        size_t tc_idx = csize2tidx (size);
      
        if (tcache
      && tc_idx < mp_.tcache_bins
      && tcache->counts[tc_idx] < mp_.tcache_count)
          {
      tcache_put (p, tc_idx);
      return;
          }
      }
    #endif
    //...
    }
    

    tc_idx合法,tcache->counts[tc_idx]小于7时,执行tcache_put (p, tc_idx);

    p为将要free的chunk,tc_idx为它对应的size在tcache中的索引

    /* Caller must ensure that we know tc_idx is valid and there's room
       for more chunks.  */
    static __always_inline void
    tcache_put (mchunkptr chunk, size_t tc_idx)
    {
      tcache_entry *e = (tcache_entry *) chunk2mem (chunk);
      assert (tc_idx < TCACHE_MAX_BINS);
      e->next = tcache->entries[tc_idx];
      tcache->entries[tc_idx] = e;
      ++(tcache->counts[tc_idx]);
    }
    

    tcache_put()把释放的chunk插入到tcache->entries[tc_idx]链表头部

    p没有清零


Tags:
0 comments



本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议CC BY-NC-ND 4.0)进行许可。

This work is licensed under the Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License (CC BY-NC-ND 4.0).