sqlite源码之pcache1.c

/*
Free slots in the allocator used to divide up the global page cache
buffer provided using the SQLITE_CONFIG_PAGECACHE mechanism.
*/
struct PgFreeslot {
  PgFreeslot *pNext;  /* Next free slot */
};

/*
Global data used by this cache.
*/
static SQLITE_WSD struct PCacheGlobal {
  PGroup grp;                    /* The global PGroup for mode (2) */

  /* Variables related to SQLITE_CONFIG_PAGECACHE settings.  The
szSlot, nSlot, pStart, pEnd, nReserve, and isInit values are all
fixed at sqlite3_initialize() time and do not require mutex protection.
The nFreeSlot and pFree values do require mutex protection.
  */
  int isInit;                    /* True if initialized */
  int separateCache;             /* Use a new PGroup for each PCache */
  int nInitPage;                 /* Initial bulk allocation size */   
  int szSlot;                    /* Size of each free slot */
  int nSlot;                     /* The number of pcache slots */
  int nReserve;                  /* Try to keep nFreeSlot above this */
  void *pStart, *pEnd;           /* Bounds of global page cache memory */
  /* Above requires no mutex.  Use mutex below for variable that follow. */
  sqlite3_mutex *mutex;          /* Mutex for accessing the following: */
  PgFreeslot *pFree;             /* Free page blocks */
  int nFreeSlot;                 /* Number of unused pcache slots */
  /* The following value requires a mutex to change.  We skip the mutex on
reading because (1) most platforms read a 32-bit integer atomically and
(2) even if an incorrect value is read, no great harm is done since this
is really just an optimization. */
  int bUnderPressure;            /* True if low on PAGECACHE memory */
} pcache1_g;

sqlite3PCacheBufferSetup(为控制缓存的PgHdr1分配内存,可以理解为初始化所有的PgHdr1)

/*
This function is called during initialization if a static buffer is 
supplied to use for the page-cache by passing the SQLITE_CONFIG_PAGECACHE
verb to sqlite3_config(). Parameter pBuf points to an allocation large
enough to contain 'n' buffers of 'sz' bytes each.

如果静态缓冲区可以被page-cache通过SQLITE_CONFIG_PAGECACHE的方式调用,这个方法在初始化的时候被调用。参数pBuf指向一个足够大的分配:内部有n个buffer(page),每个都是sz字节大小。

This routine is called from sqlite3_initialize() and so it is guaranteed
to be serialized already.  There is no need for further mutexing.
*/

nSlot和nFreeSlot在刚开始的时候是相等的。一个Slot可以理解为是一个PgHdr1

void sqlite3PCacheBufferSetup(void *pBuf, int sz, int n){//这个sz和PgHdr1的大小是一致的
  if( pcache1.isInit ){
    PgFreeslot *p;
    if( pBuf==0 ) sz = n = 0;
    if( n==0 ) sz = 0;
    sz = ROUNDDOWN8(sz);
    pcache1.szSlot = sz;
    pcache1.nSlot = pcache1.nFreeSlot = n;
    pcache1.nReserve = n>90 ? 10 : (n/10 + 1);
    pcache1.pStart = pBuf;
    pcache1.pFree = 0;
    pcache1.bUnderPressure = 0;
    while( n-- ){//相当于是n个页面
      p = (PgFreeslot*)pBuf;
      p->pNext = pcache1.pFree;
      pcache1.pFree = p;
      pBuf = (void*)&((char*)pBuf)[sz];//这一步很关键,其实是让每个页面跨越sz字节的数据
    }
    pcache1.pEnd = pBuf;
  }
}

pcacheInitBulk(为实际的缓存(各个pcache line)初始化内存)

pCache->szPage表示 database page content占用的内存大小,pCache->szAlloc表示整个pcache line占用的内存大小

Try to initialize the pCache->pFree and pCache->pBulk fields.  Return
true if pCache->pFree ends up containing one or more free pages.
开始为pcache-local分配内存

所以,szAlloc的大小 等于 database page content + PgHdr1 + MemPage + PgHdr占用的内存总和。

static int pcache1InitBulk(PCache1 *pCache){
  i64 szBulk;
  char *zBulk;
  if( pcache1.nInitPage==0 ) return 0;
  /* Do not bother with a bulk allocation if the cache size very small */
  if( pCache->nMax<3 ) return 0;
  sqlite3BeginBenignMalloc();
  if( pcache1.nInitPage>0 ){//N>0
    szBulk = pCache->szAlloc * (i64)pcache1.nInitPage;
  }else{//N<=0
    szBulk = -1024 * (i64)pcache1.nInitPage;
  }//计算szBulk的大小,即将要分配szBulk大小的内存
  if( szBulk > pCache->szAlloc*(i64)pCache->nMax ){
    szBulk = pCache->szAlloc*(i64)pCache->nMax;//调整szBulk大小
  }
  zBulk = pCache->pBulk = sqlite3Malloc( szBulk );//分配szBulk大小的内存,返回值是一个(void *)的指针
  sqlite3EndBenignMalloc();
  if( zBulk ){
    int nBulk = sqlite3MallocSize(zBulk)/pCache->szAlloc;//计算页面的个数
    do{//更新每个pcache line中的PgHdr1的信息
      PgHdr1 *pX = (PgHdr1*)&zBulk[pCache->szPage];
      pX->page.pBuf = zBulk;//pBuf指针指向一个pcache line的开始
      pX->page.pExtra = &pX[1];//pExtra指向的内存区域紧挨着PgHdr1占用的内存
      pX->isBulkLocal = 1;
      pX->isAnchor = 0;
      pX->pNext = pCache->pFree;//将px加入到未使用的pcache-local pages
      pX->pLruPrev = 0;           /* Initializing this saves a valgrind error */
      pCache->pFree = pX;
      zBulk += pCache->szAlloc;//zBulk指向下一个pcache line
    }while( --nBulk );
  }
  return pCache->pFree!=0;
}

pcache1Alloc(分配一个PgHdr1)

/*
Malloc function used within this file to allocate space from the buffer
configured using sqlite3_config(SQLITE_CONFIG_PAGECACHE) option. If no 
such buffer exists or there is no space left in it, this function falls 
back to sqlite3Malloc().

这个方法使用从buffer中通过sqlite3_config(SQLITE_CONFIG_PAGECACHE)分配的内存。如果buffer不存在或者没有剩余空间了,那么就通过sqlite3Malloc()方法来分配内存。

Multiple threads can run this routine at the same time.  Global variables
in pcache1 need to be protected via mutex.

多线程可以在这个例程中同时运行,pcache1中的全局变量需要通过互斥变量来进行访问。
*/

要么从pcache1中分配(nByte<szSlot并且pcache1中有剩余的freeSlot),要么通过sqlite3Malloc()来分配(nByte)。

static void *pcache1Alloc(int nByte){
  void *p = 0;
  assert( sqlite3_mutex_notheld(pcache1.grp.mutex) );
  if( nByte<=pcache1.szSlot ){
    sqlite3_mutex_enter(pcache1.mutex);
    p = (PgHdr1 *)pcache1.pFree;//转换为PgHdr1类型是为了便于表示,不代表真的想要的是PgHdr1类型的数据
    if( p ){
      pcache1.pFree = pcache1.pFree->pNext;
      pcache1.nFreeSlot--;
      pcache1.bUnderPressure = pcache1.nFreeSlot<pcache1.nReserve;
      assert( pcache1.nFreeSlot>=0 );
      sqlite3StatusHighwater(SQLITE_STATUS_PAGECACHE_SIZE, nByte);
      sqlite3StatusUp(SQLITE_STATUS_PAGECACHE_USED, 1);
    }
    sqlite3_mutex_leave(pcache1.mutex);
  }
  if( p==0 ){//如果buffer中没有剩余空间了,使用sqlite3Malloc分配内存
    /* Memory is not available in the SQLITE_CONFIG_PAGECACHE pool.  Get
it from sqlite3Malloc instead.
    */
    p = sqlite3Malloc(nByte);
#ifndef SQLITE_DISABLE_PAGECACHE_OVERFLOW_STATS
    if( p ){
      int sz = sqlite3MallocSize(p);
      sqlite3_mutex_enter(pcache1.mutex);
      sqlite3StatusHighwater(SQLITE_STATUS_PAGECACHE_SIZE, nByte);
      sqlite3StatusUp(SQLITE_STATUS_PAGECACHE_OVERFLOW, sz);
      sqlite3_mutex_leave(pcache1.mutex);
    }
#endif
    sqlite3MemdebugSetType(p, MEMTYPE_PCACHE);
  }
  return p;
}

/*
Free an allocated buffer obtained from pcache1Alloc().
*/
释放经过pcache1Alloc()分配的buffer

static void pcache1Free(void *p){
  if( p==0 ) return;
  if( SQLITE_WITHIN(p, pcache1.pStart, pcache1.pEnd) ){//如果当时是从pcache1中分配的
    PgFreeslot *pSlot;
    sqlite3_mutex_enter(pcache1.mutex);
    sqlite3StatusDown(SQLITE_STATUS_PAGECACHE_USED, 1);//减1
    pSlot = (PgFreeslot*)p;//需要将类型强制转换为PgFreeslot类型(分配的时候被转换成了PgHdr1类型)
    pSlot->pNext = pcache1.pFree;
    pcache1.pFree = pSlot;
    pcache1.nFreeSlot++;
    pcache1.bUnderPressure = pcache1.nFreeSlot<pcache1.nReserve;
    assert( pcache1.nFreeSlot<=pcache1.nSlot );
    sqlite3_mutex_leave(pcache1.mutex);
  }else{//如果当时是通过sqlite3Malloc分配的
    assert( sqlite3MemdebugHasType(p, MEMTYPE_PCACHE) );
    sqlite3MemdebugSetType(p, MEMTYPE_HEAP);
#ifndef SQLITE_DISABLE_PAGECACHE_OVERFLOW_STATS
    {
      int nFreed = 0;
      nFreed = sqlite3MallocSize(p);
      sqlite3_mutex_enter(pcache1.mutex);
      sqlite3StatusDown(SQLITE_STATUS_PAGECACHE_OVERFLOW, nFreed);
      sqlite3_mutex_leave(pcache1.mutex);
    }
#endif
    sqlite3_free(p);//释放通过sqlite3Malloc()例程分配的内存
  }
}
/*
Free memory previously obtained from sqlite3Malloc().
*/
void sqlite3_free(void *p){
  if( p==0 ) return;  /* IMP: R-49053-54554 */
  assert( sqlite3MemdebugHasType(p, MEMTYPE_HEAP) );
  assert( sqlite3MemdebugNoType(p, (u8)~MEMTYPE_HEAP) );
  if( sqlite3GlobalConfig.bMemstat ){
    sqlite3_mutex_enter(mem0.mutex);
    sqlite3StatusDown(SQLITE_STATUS_MEMORY_USED, sqlite3MallocSize(p));
    sqlite3StatusDown(SQLITE_STATUS_MALLOC_COUNT, 1);
    sqlite3GlobalConfig.m.xFree(p);
    sqlite3_mutex_leave(mem0.mutex);
  }else{
    sqlite3GlobalConfig.m.xFree(p);
  }
}

#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
/*
Return the size of a pcache allocation
*/

static int pcache1MemSize(void *p){
  if( p>=pcache1.pStart && p<pcache1.pEnd ){
    return pcache1.szSlot;
  }else{
    int iSize;
    assert( sqlite3MemdebugHasType(p, MEMTYPE_PCACHE) );
    sqlite3MemdebugSetType(p, MEMTYPE_HEAP);
    iSize = sqlite3MallocSize(p);
    sqlite3MemdebugSetType(p, MEMTYPE_PCACHE);
    return iSize;
  }
}

#endif /* SQLITE_ENABLE_MEMORY_MANAGEMENT */
/*
Allocate a new page object initially associated with cache pCache.
*/

static PgHdr1 *pcache1AllocPage(PCache1 *pCache, int benignMalloc){
  PgHdr1 *p = 0;
  void *pPg;

  assert( sqlite3_mutex_held(pCache->pGroup->mutex) );
  if( pCache->pFree || (pCache->nPage==0 && pcache1InitBulk(pCache)) ){//如果pCache中还有空闲的pcache line,就可以直接拿来用
    p = pCache->pFree;
    pCache->pFree = p->pNext;
    p->pNext = 0;
  }else{//如果pCache中没有空闲的pcache line,这时需要为pcache_line分配内存,有两种方式,
取决于是否定义了SWLITE_ENABLE_MEMORY_MANAGEMENT
#ifdef SQLITE_PCACHE_SEPARATE_HEADER
    /* The group mutex must be released before pcache1Alloc() is called. This
is because it might call sqlite3_release_memory(), which assumes that 
this mutex is not held. */
    assert( pcache1.separateCache==0 );
    assert( pCache->pGroup==&pcache1.grp );
    pcache1LeaveMutex(pCache->pGroup);
#endif
    if( benignMalloc ){ sqlite3BeginBenignMalloc(); }
#ifdef SQLITE_PCACHE_SEPARATE_HEADER
    pPg = pcache1Alloc(pCache->szPage);//为database page content分配内存
    p = sqlite3Malloc(sizeof(PgHdr1) + pCache->szExtra);//为剩余的三项(PgHdr1,MemPage,PgHdr)分配内存
    if( !pPg || !p ){
      pcache1Free(pPg);
      sqlite3_free(p);
      pPg = 0;
    }
#else
    pPg = pcache1Alloc(pCache->szAlloc);//为整个pcache line分配内存
    p = (PgHdr1 *)&((u8 *)pPg)[pCache->szPage];//首先将pPg转为(u8 *)类型,然后转为(PgHdr1 *)类型
#endif
    if( benignMalloc ){ sqlite3EndBenignMalloc(); }
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
    pcache1EnterMutex(pCache->pGroup);
#endif
    if( pPg==0 ) return 0;
    p->page.pBuf = pPg;//指向database page content的起始位置
    p->page.pExtra = &p[1];//指向后两项的起始位置
    p->isBulkLocal = 0;//不是从bulk local storage分配的(因为pCache中没有空闲的了)
    p->isAnchor = 0;
  }
  (*pCache->pnPurgeable)++;//分配之后,pnPurgeable增加
  return p;
}

/*
Free a page object allocated by pcache1AllocPage().
*/
通过isBulkLocal标记,可以保证从哪地方分配的还给哪。

static void pcache1FreePage(PgHdr1 *p){
  PCache1 *pCache;
  assert( p!=0 );
  pCache = p->pCache;
  assert( sqlite3_mutex_held(p->pCache->pGroup->mutex) );
  if( p->isBulkLocal ){//还给pCache
    p->pNext = pCache->pFree;
    pCache->pFree = p;
  }else{//之前通过pcache1Alloc分配的,现在释放
    pcache1Free(p->page.pBuf);//如果定义了SQLITE_PCACHE_SEPARATE_HEADER,那么仅仅释放database page content占用的内存,如果没有定义SQLITE_PCACHE_SEPARATE_HEADER,那么释放pcache line占用的整个内存
#ifdef SQLITE_PCACHE_SEPARATE_HEADER//database page content和剩余的三项单独分配。database page content通过pcache1Alloc进行分配,而剩余的三项通过sqlite3Malloc进行分配的
    sqlite3_free(p);//如果定义了separate_header,那么需要释放其他三项(PgHdr1+PgHdr+MemPage)占用的内容(这三项是通过sqlite3Malloc分配的)
#endif
  }
  (*pCache->pnPurgeable)--;
}

/*
This function is used to resize the hash table used by the cache passed
as the first argument.The PCache mutex must be held when this function is called.
*/

static void pcache1ResizeHash(PCache1 *p){
  PgHdr1 **apNew;
  unsigned int nNew;
  unsigned int i;

  assert( sqlite3_mutex_held(p->pGroup->mutex) );

  nNew = p->nHash*2;
  if( nNew<256 ){
    nNew = 256;//从这里可以看出,hash表有最小的元素个数限制
  }

  pcache1LeaveMutex(p->pGroup);
  if( p->nHash ){ sqlite3BeginBenignMalloc(); }
  apNew = (PgHdr1 **)sqlite3MallocZero(sizeof(PgHdr1 *)*nNew);
  if( p->nHash ){ sqlite3EndBenignMalloc(); }
  pcache1EnterMutex(p->pGroup);
  if( apNew ){
    for(i=0; i<p->nHash; i++){//注意:如果是新的PCache,那么p->nHash就是0,那么这个for循环就不会执行
      PgHdr1 *pPage;
      PgHdr1 *pNext = p->apHash[i];
      while( (pPage = pNext)!=0 ){
        unsigned int h = pPage->iKey % nNew;//计算新的hash值
        pNext = pPage->pNext;//pNext用于保存下一个待处理的PgHdr1实例
        pPage->pNext = apNew[h];//pPage就是已经找到的正在处理的PgHdr1实例
        apNew[h] = pPage;//向新的哈希表中加入元素
      }
    }
    sqlite3_free(p->apHash);//释放旧的哈希表占用的内存
    p->apHash = apNew;
    p->nHash = nNew;//在最后会对新的哈希表的nHash值进行设置
  }
}

/*
This function is used internally to remove the page pPage from the 
PGroup LRU list, if is part of it. If pPage is not part of the PGroup
LRU list, then this function is a no-op.

The PGroup mutex must be held when this function is called.
*/

static PgHdr1 *pcache1PinPage(PgHdr1 *pPage){
  assert( pPage!=0 );
  assert( PAGE_IS_UNPINNED(pPage) );
  assert( pPage->pLruNext );
  assert( pPage->pLruPrev );
  assert( sqlite3_mutex_held(pPage->pCache->pGroup->mutex) );
  pPage->pLruPrev->pLruNext = pPage->pLruNext;//从双向循环链表(LRU列表)中删除
  pPage->pLruNext->pLruPrev = pPage->pLruPrev;
  pPage->pLruNext = 0;
  /* pPage->pLruPrev = 0;
No need to clear pLruPrev as it is never accessed if pLruNext is 0 */
  assert( pPage->isAnchor==0 );
  assert( pPage->pCache->pGroup->lru.isAnchor==1 );
  pPage->pCache->nRecyclable--;//LRU列表中可用的页面的个数减一
  return pPage;
}

/*
Remove the page supplied as an argument from the hash table 
(PCache1.apHash structure) that it is currently stored in.
Also free the page if freePage is true.The PGroup mutex must be held when this function is called.
*/

static void pcache1RemoveFromHash(PgHdr1 *pPage, int freeFlag){
  unsigned int h;
  PCache1 *pCache = pPage->pCache;
  PgHdr1 **pp;

  assert( sqlite3_mutex_held(pCache->pGroup->mutex) );
  h = pPage->iKey % pCache->nHash;
  for(pp=&pCache->apHash[h]; (*pp)!=pPage; pp=&(*pp)->pNext);
  *pp = (*pp)->pNext;//相当于直接就删除了×pp当前指向的元素

  pCache->nPage--;//apHash哈希表中页面个数减1
  if( freeFlag ) pcache1FreePage(pPage);//如过定义了清除标志,则调用,pcache1FreePage函数在前面有定义
}

pcache1EnforceMaxPage

/*
If there are currently more than nMaxPage pages allocated, try
to recycle pages to reduce the number allocated to nMaxPage.
*/

static void pcache1EnforceMaxPage(PCache1 *pCache){
  PGroup *pGroup = pCache->pGroup;
  PgHdr1 *p;
  assert( sqlite3_mutex_held(pGroup->mutex) );
  while( pGroup->nPurgeable>pGroup->nMaxPage
      && (p=pGroup->lru.pLruPrev)->isAnchor==0//isAnchor表示的是PGroup.lru元素(除了第一个元素是,其他的LRU列表中的元素都不是,该while循环每次都是从双向循环链表的最后一个元素进行处理)
  ){
    assert( p->pCache->pGroup==pGroup );
    assert( PAGE_IS_UNPINNED(p) );
    pcache1PinPage(p);
    pcache1RemoveFromHash(p, 1);//不仅仅从apHash表中去除,而且还从LRU表中去除,并且释放pcache line占用的内存
  }
  if( pCache->nPage==0 && pCache->pBulk ){//以apHash表中为准,如果apHash表中page的个数为0并且pcache-local还占用内存的话,就释放这些内存
    sqlite3_free(pCache->pBulk);
    pCache->pBulk = pCache->pFree = 0;//pcache-local和pcache-local中未使用的内存赋值为空
  }
}

pcache1TruncateUnsafe

/*
Discard all pages from cache pCache with a page number (key value) 
greater than or equal to iLimit. Any pinned pages that meet this 
criteria are unpinned before they are discarded.

The PCache mutex must be held when this function is called.
*/

该函数造成的结果是:(1)apHash表中对应的符合条件的元素被释放,(2)LRU列表中对应的元素被释放,(3)内存中对应的pcache line被释放。

static void pcache1TruncateUnsafe(
  PCache1 *pCache,             /* The cache to truncate */
  unsigned int iLimit          /* Drop pages with this pgno or larger */
){
  TESTONLY( int nPage = 0; )  /* To assert pCache->nPage is correct */
  unsigned int h, iStop;
  assert( sqlite3_mutex_held(pCache->pGroup->mutex) );
  assert( pCache->iMaxKey >= iLimit );
  assert( pCache->nHash > 0 );
  if( pCache->iMaxKey - iLimit < pCache->nHash ){//从iLimit开始,走了n步到达iMaxKey,则n<pCache->nHash
    /* If we are just shaving the last few pages off the end of the
cache, then there is no point in scanning the entire hash table.
Only scan those hash slots that might contain pages that need to
be removed. */
    h = iLimit % pCache->nHash;//从h开始走,走到iStop,期间可能会走到末尾,这时会拐回头循环
    iStop = pCache->iMaxKey % pCache->nHash;
    TESTONLY( nPage = -10; )  /* Disable the pCache->nPage validity check */
  }else{
    /* This is the general case where many pages are being removed.
It is necessary to scan the entire hash table */
    h = pCache->nHash/2;//不明白为什么要除以2(我感觉都一样,无论h在哪,从h开始,然后是h+1,H+2,...0,1,2...最后在h-1处结束)
    iStop = h - 1;
  }
  for(;;){
    PgHdr1 **pp;
    PgHdr1 *pPage;
    assert( h<pCache->nHash );
    pp = &pCache->apHash[h]; 
    while( (pPage = *pp)!=0 ){//遍历这个存储桶
      if( pPage->iKey>=iLimit ){//只有页号大于等于iLimit时才进行处理
        pCache->nPage--;
        *pp = pPage->pNext;
        if( PAGE_IS_UNPINNED(pPage) ) pcache1PinPage(pPage);//从LRU中去除
        pcache1FreePage(pPage);//释放对应的pcache line占用的内存
      }else{
        pp = &pPage->pNext;
        TESTONLY( if( nPage>=0 ) nPage++; )
      }
    }
    if( h==iStop ) break;
    h = (h+1) % pCache->nHash;
  }
  assert( nPage<0 || pCache->nPage==(unsigned)nPage );
}

/*
Implementation of the sqlite3_pcache.xCreate method.Allocate a new cache.
*/

在pcache1ResizeHash中,对Hash表的nHash就行了设置,nHash的最小值是256

static sqlite3_pcache *pcache1Create(int szPage, int szExtra, int bPurgeable){
  PCache1 *pCache;      /* The newly created page cache */
  PGroup *pGroup;       /* The group the new page cache will belong to */
  int sz;               /* Bytes of memory required to allocate the new cache */

  assert( (szPage & (szPage-1))==0 && szPage>=512 && szPage<=65536 );
  assert( szExtra < 300 );

  sz = sizeof(PCache1) + sizeof(PGroup)*pcache1.separateCache;//separateCache等于0或者等于1,当separateCache=0时,说明多个Cache共享PGroup,此时无需为PGroup建立内存。如果separateCace=1,说明各个Cache的PGroup是独立的,所以需要为每个Cache的PGroup都要分配内存
  pCache = (PCache1 *)sqlite3MallocZero(sz);//为新的PCache1分配内存
  if( pCache ){//如果内存分配成功了
    if( pcache1.separateCache ){
      pGroup = (PGroup*)&pCache[1];//找到PGroup
      pGroup->mxPinned = 10;//因为是新建的PGroup,所以初始化mxPinned为10
    }else{
      pGroup = &pcache1.grp;//从全局变量pcache1中获取PGroup类型的实例化变量
    }
    pcache1EnterMutex(pGroup);//多线程互斥进行访问
    if( pGroup->lru.isAnchor==0 ){
      pGroup->lru.isAnchor = 1;
      pGroup->lru.pLruPrev = pGroup->lru.pLruNext = &pGroup->lru;
    }
    pCache->pGroup = pGroup;
    pCache->szPage = szPage;
    pCache->szExtra = szExtra;
    pCache->szAlloc = szPage + szExtra + ROUND8(sizeof(PgHdr1));
    pCache->bPurgeable = (bPurgeable ? 1 : 0);//该缓存页是否可以擦除
    pcache1ResizeHash(pCache);//初始化哈希表
    if( bPurgeable ){//如果可以擦除
      pCache->nMin = 10;
      pGroup->nMinPage += pCache->nMin;
      pGroup->mxPinned = pGroup->nMaxPage + 10 - pGroup->nMinPage;
      pCache->pnPurgeable = &pGroup->nPurgeable;
    }else{
      pCache->pnPurgeable = &pCache->nPurgeableDummy;
    }
    pcache1LeaveMutex(pGroup);//释放互斥变量
    if( pCache->nHash==0 ){
      pcache1Destroy((sqlite3_pcache*)pCache);
      pCache = 0;
    }
  }
  return (sqlite3_pcache *)pCache;
}

fault.c

This file contains code to support the concept of "benign" 
malloc failures (when the xMalloc() or xRealloc() method of the
sqlite3_mem_methods structure fails to allocate a block of memory
and returns 0). 

这个文件包含的代码来支持“良性”分配错误的概念(当sqlite3_mem_methods结构的xMalloc()或者xRealloc()方法失败时,仍然分配内存并且返回0)

Most malloc failures are non-benign. After they occur, SQLite
abandons the current operation and returns an error code (usually
SQLITE_NOMEM) to the user. However, sometimes a fault is not necessarily
fatal. For example, if a malloc fails while resizing a hash table, this 
is completely recoverable simply by not carrying out the resize. The 
hash table will continue to function normally.  So a malloc failure 
during a hash table resize is a benign fault.
*/

大部分的分配错误是分配操作还没有开始的时候发生的,这是SQLite丢弃当前的操作并且返回一个错误码(通常是SQLITE_NOMEM)给用户。可是,有时一个错误是微小的,例如,当一个哈希表正在resizing的时候分配内存失败了,这个通过不执行resizing的方式完全可以恢复。哈希表也会继续正常工作。所以,当哈希表resize的时候分配内存失败是一个可以修复的错误。

#include "sqliteInt.h"

#ifndef SQLITE_UNTESTABLE

/*
Global variables.
*/
typedef struct BenignMallocHooks BenignMallocHooks;
static SQLITE_WSD struct BenignMallocHooks {
  void (*xBenignBegin)(void);
  void (*xBenignEnd)(void);
} sqlite3Hooks = { 0, 0 };

/* The "wsdHooks" macro will resolve to the appropriate BenignMallocHooks
structure.  If writable static data is unsupported on the target,
we have to locate the state vector at run-time.  In the more common
case where writable static data is supported, wsdHooks can refer directly
to the "sqlite3Hooks" state vector declared above.
*/

通过"wsdHooks"宏定义可以得到合适的BenignMallocHooks。如果目标机不支持SWD,那么就不得不在运行时访问栈内存来获取。在大部分情况下WSD是被支持的,那么"wsdHooks"可以直接通过"sqlite3Hooks"来访问。
#ifdef SQLITE_OMIT_WSD
# define wsdHooksInit \
  BenignMallocHooks *x = &GLOBAL(BenignMallocHooks,sqlite3Hooks)
# define wsdHooks x[0]
#else
# define wsdHooksInit
# define wsdHooks sqlite3Hooks
#endif
/*
Register hooks to call when sqlite3BeginBenignMalloc() and
sqlite3EndBenignMalloc() are called, respectively.
*/
void sqlite3BenignMallocHooks(//注册
  void (*xBenignBegin)(void),
  void (*xBenignEnd)(void)
){
  wsdHooksInit;
  wsdHooks.xBenignBegin = xBenignBegin;
  wsdHooks.xBenignEnd = xBenignEnd;
}

/*
This (sqlite3EndBenignMalloc()) is called by SQLite code to indicate that
subsequent malloc failures are benign. A call to sqlite3EndBenignMalloc()
indicates that subsequent malloc failures are non-benign.
*/
void sqlite3BeginBenignMalloc(void){
  wsdHooksInit;
  if( wsdHooks.xBenignBegin ){//如果之前已经注册了xBenignBegin方法,就执行
    wsdHooks.xBenignBegin();
  }
}
void sqlite3EndBenignMalloc(void){//
  wsdHooksInit;
  if( wsdHooks.xBenignEnd ){//如果之前已经注册了xBenignEnd方法,就执行
    wsdHooks.xBenignEnd();
  }
}

#endif   /* #ifndef SQLITE_UNTESTABLE */
 

typedef struct sqlite3_mutex_methods sqlite3_mutex_methods;
struct sqlite3_mutex_methods {
  int (*xMutexInit)(void);
  int (*xMutexEnd)(void);
  sqlite3_mutex *(*xMutexAlloc)(int);
  void (*xMutexFree)(sqlite3_mutex *);
  void (*xMutexEnter)(sqlite3_mutex *);
  int (*xMutexTry)(sqlite3_mutex *);
  void (*xMutexLeave)(sqlite3_mutex *);
  int (*xMutexHeld)(sqlite3_mutex *);
  int (*xMutexNotheld)(sqlite3_mutex *);
};

/******************************************************************************/
/******sqlite3_pcache Methods **********************************************/

pcache1Init

/*
Implementation of the sqlite3_pcache.xInit method.
*/

static int pcache1Init(void *NotUsed){
  UNUSED_PARAMETER(NotUsed);
  assert( pcache1.isInit==0 );
  memset(&pcache1, 0, sizeof(pcache1));
  /*
  The pcache1.separateCache variable is true if each PCache has its own
  private PGroup (mode-1).  pcache1.separateCache is false if the single
  PGroup in pcache1.grp is used for all page caches (mode-2).
  
    *  Always use a unified cache (mode-2) if ENABLE_MEMORY_MANAGEMENT
  
    *  Use a unified cache in single-threaded applications that have
       configured a start-time buffer for use as page-cache memory using
       sqlite3_config(SQLITE_CONFIG_PAGECACHE, pBuf, sz, N) with non-NULL 
       pBuf argument.
  
    *  Otherwise use separate caches (mode-1)
  */
#if defined(SQLITE_ENABLE_MEMORY_MANAGEMENT)
  pcache1.separateCache = 0;
#elif SQLITE_THREADSAFE
  pcache1.separateCache = sqlite3GlobalConfig.pPage==0
                          || sqlite3GlobalConfig.bCoreMutex>0;
#else
  pcache1.separateCache = sqlite3GlobalConfig.pPage==0;
#endif

#if SQLITE_THREADSAFE
  if( sqlite3GlobalConfig.bCoreMutex ){
    pcache1.grp.mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_LRU);
    pcache1.mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_PMEM);
  }
#endif
  if( pcache1.separateCache
   && sqlite3GlobalConfig.nPage!=0
   && sqlite3GlobalConfig.pPage==0
  ){
    pcache1.nInitPage = sqlite3GlobalConfig.nPage;
  }else{
    pcache1.nInitPage = 0;
  }
  pcache1.grp.mxPinned = 10;
  pcache1.isInit = 1;
  return SQLITE_OK;
}

/*
Structure containing global configuration data for the SQLite library.

This structure also contains some state information.
*/

struct Sqlite3Config {
  int bMemstat;                     /* True to enable memory status */
  int bCoreMutex;                   /* True to enable core mutexing */
  int bFullMutex;                   /* True to enable full mutexing */
  int bOpenUri;                     /* True to interpret filenames as URIs */
  int bUseCis;                      /* Use covering indices for full-scans */
  int bSmallMalloc;                 /* Avoid large memory allocations if true */
  int mxStrlen;                     /* Maximum string length */
  int neverCorrupt;                 /* Database is always well-formed */
  int szLookaside;                  /* Default lookaside buffer size */
  int nLookaside;                   /* Default lookaside buffer count */
  int nStmtSpill;                   /* Stmt-journal spill-to-disk threshold */
  sqlite3_mem_methods m;            /* Low-level memory allocation interface */
  sqlite3_mutex_methods mutex;      /* Low-level mutex interface */
  sqlite3_pcache_methods2 pcache2;  /* Low-level page-cache interface */
  void *pHeap;                      /* Heap storage space */
  int nHeap;                        /* Size of pHeap[] */
  int mnReq, mxReq;                 /* Min and max heap requests sizes */
  sqlite3_int64 szMmap;             /* mmap() space per open file */
  sqlite3_int64 mxMmap;             /* Maximum value for szMmap */
  void *pPage;                      /* Page cache memory */
  int szPage;                       /* Size of each page in pPage[] */
  int nPage;                        /* Number of pages in pPage[] */
  int mxParserStack;                /* maximum depth of the parser stack */
  int sharedCacheEnabled;           /* true if shared-cache mode enabled */
  u32 szPma;                        /* Maximum Sorter PMA size */
  /* The above might be initialized to non-zero.  The following need to always
  initially be zero, however. */
  int isInit;                       /* True after initialization has finished */
  int inProgress;                   /* True while initialization in progress */
  int isMutexInit;                  /* True after mutexes are initialized */
  int isMallocInit;                 /* True after malloc is initialized */
  int isPCacheInit;                 /* True after malloc is initialized */
  int nRefInitMutex;                /* Number of users of pInitMutex */
  sqlite3_mutex *pInitMutex;        /* Mutex used by sqlite3_initialize() */
  void (*xLog)(void*,int,const char*); /* Function for logging */
  void *pLogArg;                       /* First argument to xLog() */
#ifdef SQLITE_ENABLE_SQLLOG
  void(*xSqllog)(void*,sqlite3*,const char*, int);
  void *pSqllogArg;
#endif
#ifdef SQLITE_VDBE_COVERAGE
  /* The following callback (if not NULL) is invoked on every VDBE branch
  operation.  Set the callback using SQLITE_TESTCTRL_VDBE_COVERAGE.
  */
  void (*xVdbeBranch)(void*,unsigned iSrcLine,u8 eThis,u8 eMx);  /* Callback */
  void *pVdbeBranchArg;                                     /* 1st argument */
#endif
#ifdef SQLITE_ENABLE_DESERIALIZE
  sqlite3_int64 mxMemdbSize;        /* Default max memdb size */
#endif
#ifndef SQLITE_UNTESTABLE
  int (*xTestCallback)(int);        /* Invoked by sqlite3FaultSim() */
#endif
  int bLocaltimeFault;              /* True to fail localtime() calls */
  int bInternalFunctions;           /* Internal SQL functions are visible */
  int iOnceResetThreshold;          /* When to reset OP_Once counters */
  u32 szSorterRef;                  /* Min size in bytes to use sorter-refs */
};

/*
Implementation of the sqlite3_pcache.xCachesize method. 

Configure the cache_size limit for a cache.
*/

在pcache.c中的sqlite3PcacheSetPageSize例程中会调用该例程

static void pcache1Cachesize(sqlite3_pcache *p, int nMax){
  PCache1 *pCache = (PCache1 *)p;//将p强制类型转换为PCache1类型
  if( pCache->bPurgeable ){//如果缓存可以擦除(缓存在后背队列中)
    PGroup *pGroup = pCache->pGroup;//获取PGroup实例化对象
    pcache1EnterMutex(pGroup);//进入互斥变量
    pGroup->nMaxPage += (nMax - pCache->nMax);
    pGroup->mxPinned = pGroup->nMaxPage + 10 - pGroup->nMinPage;
    pCache->nMax = nMax;
    pCache->n90pct = pCache->nMax*9/10;
    pcache1EnforceMaxPage(pCache);
    pcache1LeaveMutex(pGroup);
  }
}

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值