函数代码解析

Mem3.c中主要涉及到两个结构体,这里介绍一下第一个比较重要的结构体Mem3Block,该结构体定义如下:

    typedef struct Mem3Block Mem3Block;
    struct Mem3Block {
      union {
        struct {
          u32 prevSize;   /* Size of previous chunk in Mem3Block elements */
          u32 size4x;     /* 4x the size of current chunk in Mem3Block elements */
        } hdr;
        struct {
          u32 next;       /* Index in mem3.aPool[] of next free chunk */
          u32 prev;       /* Index in mem3.aPool[] of previous free chunk */
        } list;
      } u;
    };

前面已经提到过,一个chunk有两个或两个以上的block组成,这个结构体就是用来形成chunk链表的。可以看到,第一个block有8个字节,其中,前四个字节(u32 prevSive)用来存储链表中前一个chunk块的大小,后四个字节(u32 size4x)用来存储当前块大小的四倍,为什么size4x是当前chunk大小的四倍呢?原因是最后一个bit和倒数第二个bit有特殊的含义,最后一个bit是否为1, 用来保存当前块是否已经分配出去了,倒数第二个bit 是否为1 上一个块是否被分配出去了。除此之外的另外一个结构体也是很重要的,它定义了整个mem3.c中用到的全局变量,这里就不再具体介绍了。
下面是两个关于内存分配的实现函数部分代码:
实现内存分配:

    static void *memsys3FromMaster(u32 nBlock){
      assert( sqlite3_mutex_held(mem3.mutex) );
      assert( mem3.szMaster>=nBlock );
      if( nBlock>=mem3.szMaster-1 ){
        /* Use the entire master */  使用整个master
        void *p = memsys3Checkout(mem3.iMaster, mem3.szMaster);
        mem3.iMaster = 0;
        mem3.szMaster = 0;
        mem3.mnMaster = 0;
        return p;
      }else{
        /* Split the master block.  Return the tail. */切割master进行分配
        u32 newi, x;
        newi = mem3.iMaster + mem3.szMaster - nBlock;
        assert( newi > mem3.iMaster+1 );
        mem3.aPool[mem3.iMaster+mem3.szMaster-1].u.hdr.prevSize = nBlock;
        mem3.aPool[mem3.iMaster+mem3.szMaster-1].u.hdr.size4x |= 2;
        mem3.aPool[newi-1].u.hdr.size4x = nBlock*4 + 1;
        mem3.szMaster -= nBlock;
        mem3.aPool[newi-1].u.hdr.prevSize = mem3.szMaster;
        x = mem3.aPool[mem3.iMaster-1].u.hdr.size4x & 2;
        mem3.aPool[mem3.iMaster-1].u.hdr.size4x = mem3.szMaster*4 | x;
        if( mem3.szMaster < mem3.mnMaster ){
          mem3.mnMaster = mem3.szMaster;
        }
        return (void*)&mem3.aPool[newi];
      }
    }

这个函数主要实现的功能是内存分配。Mem3.c又叫实验性内存分配器,在系统初始化前就分配了一块内存,之后这块内存就被固定下来。这块固定的内存就是master,每一次用户需要使用内存时,最开始都要从master上切割对应大小的块,上面这个函数实现的功能就是从master上分配内存的过程。不管什么时候,总保证master是最大的块。用过后的块根据其大小,连接成空闲块链表,用户需要时直接从空闲双链表中取。
因此就涉及到对双向链表的删除和插入操作,其中,删除对应的真正意义是,该块被用户申请走了,被利用了,就要将其拿出链表。对应实现函数代码如下:

    static void memsys3UnlinkFromList(u32 i, u32 *pRoot){
      u32 next = mem3.aPool[i].u.list.next;
      u32 prev = mem3.aPool[i].u.list.prev;
      assert( sqlite3_mutex_held(mem3.mutex) );
      if( prev==0 ){
        *pRoot = next;
      }else{
        mem3.aPool[prev].u.list.next = next;
      }
      if( next ){
        mem3.aPool[next].u.list.prev = prev;
      }
      mem3.aPool[i].u.list.next = 0;
      mem3.aPool[i].u.list.prev = 0;
    }

同样,插入就意味着用户使用完内存后,要将其插入到空闲链表,这样就防止了内存泄漏问题,具体实现代码如下:

    static void memsys3LinkIntoList(u32 i, u32 *pRoot){
      assert( sqlite3_mutex_held(mem3.mutex) );
      mem3.aPool[i].u.list.next = *pRoot;
      mem3.aPool[i].u.list.prev = 0;
      if( *pRoot ){
        mem3.aPool[*pRoot].u.list.prev = i;
      }
      *pRoot = i;
    }

除了以上几个主要函数外,其他函数代码我就不继续介绍了,在源码阅读里面已经有详细注释。

results matching ""

    No results matching ""