SQLite源码之pcache.c续

pcacheFetchFinishWithInit

/*
This is a helper routine for sqlite3PcacheFetchFinish()

In the uncommon case where the page being fetched has not been
initialized, this routine is invoked to do the initialization.
This routine is broken out into a separate function since it
requires extra stack manipulation that can be avoided in the common
case.
*/

这个例程是sqlite3PcacheFetchFinish例程的协助例程。在一些罕见的例子中,一些被fetch的页面还没有被初始化,那么这个例程就会被调用来完成初始化。此例程被分解为单独的函数,因为它需要额外的堆栈操作,在常见情况下可以避免。

获取某个结构的某个字段的偏移量

static SQLITE_NOINLINE PgHdr *pcacheFetchFinishWithInit(
  PCache *pCache,             /* Obtain the page from this cache */
  Pgno pgno,                  /* Page number obtained */
  sqlite3_pcache_page *pPage  /* Page obtained by prior PcacheFetch() call */
){
  PgHdr *pPgHdr;
  assert( pPage!=0 );
  pPgHdr = (PgHdr*)pPage->pExtra;
  assert( pPgHdr->pPage==0 );
  memset(&pPgHdr->pDirty, 0, sizeof(PgHdr) - offsetof(PgHdr,pDirty));//把PgHdr结构中中,pDirty字段以前的内存都变为0
  pPgHdr->pPage = pPage;//设置pcache line
  pPgHdr->pData = pPage->pBuf;//设置database page content
  pPgHdr->pExtra = (void *)&pPgHdr[1];//设置MemPage
  memset(pPgHdr->pExtra, 0, 8);//给MemPage的前8字节都变为0
  pPgHdr->pCache = pCache;//设置PgHdr只想的pCache
  pPgHdr->pgno = pgno;//更新页号
  pPgHdr->flags = PGHDR_CLEAN;
  return sqlite3PcacheFetchFinish(pCache,pgno,pPage);
}

sqlite3PcacheFetchFinish

/*
This routine converts the sqlite3_pcache_page object returned by
sqlite3PcacheFetch() into an initialized PgHdr object.  This routine
must be called after sqlite3PcacheFetch() in order to get a usable
result.这个例程将sqlite3PcacheFetch例程返回的sqlite3_pcache_page对象转换为初始化以后的PgHdr对象。这个例程一定要在sqlite3PcacheFetch例程被调用之后被调用,从而获得可用的结果。
*/

PgHdr *sqlite3PcacheFetchFinish(
  PCache *pCache,             /* Obtain the page from this cache */
  Pgno pgno,                  /* Page number obtained */
  sqlite3_pcache_page *pPage  /* Page obtained by prior PcacheFetch() call */
){
  PgHdr *pPgHdr;

  assert( pPage!=0 );
  pPgHdr = (PgHdr *)pPage->pExtra;

  if( !pPgHdr->pPage ){
    return pcacheFetchFinishWithInit(pCache, pgno, pPage);
  }
  pCache->nRefSum++;/* Sum of ref counts over all pages */
  pPgHdr->nRef++;/* Number of users of this page */
  assert( sqlite3PcachePageSanity(pPgHdr) );
  return pPgHdr;
}

sqlite3PcacheRelease

/*
Decrement the reference count on a page. If the page is clean and the
reference count drops to 0, then it is made eligible for recycling.
*/减少一个页面的引用数目。如果这个页面是干净的并且页面的引用数减小到了0,那么这个页面就可以被复用了。

void SQLITE_NOINLINE sqlite3PcacheRelease(PgHdr *p){
  assert( p->nRef>0 );
  p->pCache->nRefSum--;
  if( (--p->nRef)==0 ){
    if( p->flags&PGHDR_CLEAN ){
      pcacheUnpin(p);//放入到LRU双向循环链表中(这就表示可以重用了)
    }else{
      pcacheManageDirtyList(p, PCACHE_DIRTYLIST_FRONT);/* Doing both moves pPage to the front of the dirty list. */
    }
  }
}

sqlite3PcacheRef

/*
Increase the reference count of a supplied page by 1.
*/

void sqlite3PcacheRef(PgHdr *p){
  assert(p->nRef>0);
  assert( sqlite3PcachePageSanity(p) );
  p->nRef++;
  p->pCache->nRefSum++;
}

sqlite3PcacheDrop

/*
Drop a page from the cache. There must be exactly one reference to the
page. This function deletes that reference, so after it returns the
page pointed to by p is invalid.从缓存中去除一个页面。这个页面必定在被drop之前只有一个引用。这个方法删除这个引用(因为reuserUnlikely参数为1,所以从apHash表中将引用(PgHdr1)去除,然后还要释放这个pcache line占用的内存)。
*/

void sqlite3PcacheDrop(PgHdr *p){
  assert( p->nRef==1 );
  assert( sqlite3PcachePageSanity(p) );
  if( p->flags&PGHDR_DIRTY ){
    pcacheManageDirtyList(p, PCACHE_DIRTYLIST_REMOVE);
  }
  p->pCache->nRefSum--;
  sqlite3GlobalConfig.pcache2.xUnpin(p->pCache->pCache, p->pPage, 1);
}

sqlite3PcacheMakeDirty

/*
Make sure the page is marked as dirty. If it isn't dirty already,
make it so.确保page已经dirty标志标记过了,如果还没有被标记为dirty,那么就开始标记。
*/

void sqlite3PcacheMakeDirty(PgHdr *p){
  assert( p->nRef>0 );
  assert( sqlite3PcachePageSanity(p) );
  if( p->flags & (PGHDR_CLEAN|PGHDR_DONT_WRITE) ){    /*OPTIMIZATION-IF-FALSE*/
    p->flags &= ~PGHDR_DONT_WRITE;
    if( p->flags & PGHDR_CLEAN ){
      p->flags ^= (PGHDR_DIRTY|PGHDR_CLEAN);//^符号的作用是异或。假设有两个比特位,a和b,现在计算:a=a^b,分析:当b为1时,a取反,当b为0时,a保持
      pcacheTrace(("%p.DIRTY %d\n",p->pCache,p->pgno));
      assert( (p->flags & (PGHDR_DIRTY|PGHDR_CLEAN))==PGHDR_DIRTY );
      pcacheManageDirtyList(p, PCACHE_DIRTYLIST_ADD);//加入到脏页列表中
    }
    assert( sqlite3PcachePageSanity(p) );
  }
}

sqlite3PcacheMakeClean

/*
Make sure the page is marked as clean. If it isn't clean already,
make it so.
*/

void sqlite3PcacheMakeClean(PgHdr *p){
  assert( sqlite3PcachePageSanity(p) );
  assert( (p->flags & PGHDR_DIRTY)!=0 );
  assert( (p->flags & PGHDR_CLEAN)==0 );//确保是脏页时才继续往下执行
  pcacheManageDirtyList(p, PCACHE_DIRTYLIST_REMOVE);//将该页从脏页列表中移除(pDirty指针)
  p->flags &= ~(PGHDR_DIRTY|PGHDR_NEED_SYNC|PGHDR_WRITEABLE);//清零flags的标志:PGHDR_DIRTY、PGHDR_NEED_SYNC和PGHDR_WRITABLE
  p->flags |= PGHDR_CLEAN;//给flags设置PGHDR_CLEAN标志
  pcacheTrace(("%p.CLEAN %d\n",p->pCache,p->pgno));
  assert( sqlite3PcachePageSanity(p) );
  if( p->nRef==0 ){
    pcacheUnpin(p);//加入到LRU列表中,以后还可能被重用(没有从apHash表中去除,也没有清除pcache line占用的内存)
  }
}

sqlite3PcacheCleanAll

/*
Make every page in the cache clean.
*/

void sqlite3PcacheCleanAll(PCache *pCache){
  PgHdr *p;
  pcacheTrace(("%p.CLEAN-ALL\n",pCache));
  while( (p = pCache->pDirty)!=0 ){//每次获取一个pCache->pDirty
    sqlite3PcacheMakeClean(p);//当sqlite3PcacheMakeClean执行之后,
就将pCache->pDirty指向的PgHdr清除了,然后pCache->pDirty就指向了新的元素
  }
}

sqlite3PcacheClearWritable

/*
Clear the PGHDR_NEED_SYNC and PGHDR_WRITEABLE flag from all dirty pages.
*/对所有的脏页清除PGHDR_NEED_SYNC标志和PGHDR_WRITABLE标志

void sqlite3PcacheClearWritable(PCache *pCache){
  PgHdr *p;
  pcacheTrace(("%p.CLEAR-WRITEABLE\n",pCache));
  for(p=pCache->pDirty; p; p=p->pDirtyNext){
    p->flags &= ~(PGHDR_NEED_SYNC|PGHDR_WRITEABLE);//清除PGHDR_NEED_SYNC标志和PGHDR_WRITABLE标志
  }
  pCache->pSynced = pCache->pDirtyTail;
}

sqlite3PcacheClearSyncFlags

/*
Clear the PGHDR_NEED_SYNC flag from all dirty pages.
*/

void sqlite3PcacheClearSyncFlags(PCache *pCache){
  PgHdr *p;
  for(p=pCache->pDirty; p; p=p->pDirtyNext){
    p->flags &= ~PGHDR_NEED_SYNC;//清除PGHDR_NEED_SYNC标志
  }
  pCache->pSynced = pCache->pDirtyTail;//难道是因为pCache->pDirtyTail是上面那个for循环最后一个处理的
}

sqlite3PcacheMove

/*
Change the page number of page p to newPgno. 
*/

void sqlite3PcacheMove(PgHdr *p, Pgno newPgno){
  PCache *pCache = p->pCache;
  assert( p->nRef>0 );
  assert( newPgno>0 );
  assert( sqlite3PcachePageSanity(p) );
  pcacheTrace(("%p.MOVE %d -> %d\n",pCache,p->pgno,newPgno));
  sqlite3GlobalConfig.pcache2.xRekey(pCache->pCache, p->pPage, p->pgno,newPgno);//将页面p->pgno的页号变为新的页号(newPgno)
  p->pgno = newPgno;//换位新的页号
  if( (p->flags&PGHDR_DIRTY) && (p->flags&PGHDR_NEED_SYNC) ){
    pcacheManageDirtyList(p, PCACHE_DIRTYLIST_FRONT);//如果PgHdr设置了PGHDR_DIRTY和PGHDR_NEED,把脏页p移动到p->pCache->pDirty的位置(to front)
  }
}

sqlite3PcacheTruncate

/*
Drop every cache entry whose page number is greater than "pgno". The
caller must ensure that there are no outstanding references to any pages
other than page 1 with a page number greater than pgno.

If there is a reference to page 1 and the pgno parameter passed to this
function is 0, then the data area associated with page 1 is zeroed, but
the page object is not dropped.
*/

把页号大于pgno的每个缓存entry都drop。这个调用者要确保没有为任何例外的页面,除了页号为1的页面。

如果如果对1号页面存在一个引用,并且传递到这个这个函数的参数是0,那么1号页面的数据区域将会被清零,但是这个页面的结构没有被drop。

void sqlite3PcacheTruncate(PCache *pCache, Pgno pgno){
  if( pCache->pCache ){
    PgHdr *p;
    PgHdr *pNext;
    pcacheTrace(("%p.TRUNCATE %d\n",pCache,pgno));
    for(p=pCache->pDirty; p; p=pNext){//现在处理所有的脏页
      pNext = p->pDirtyNext;
      /* This routine never gets call with a positive pgno except right
      after sqlite3PcacheCleanAll().  So if there are dirty pages,
      it must be that pgno==0.
      */
      assert( p->pgno>0 );
      if( p->pgno>pgno ){
        assert( p->flags&PGHDR_DIRTY );
        sqlite3PcacheMakeClean(p);//从apHash表中去除,并放入到LRU列表中,pcache line占用的内存不释放
      }
    }
    if( pgno==0 && pCache->nRefSum ){
      sqlite3_pcache_page *pPage1;
      pPage1 = sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache,1,0);//从apHash表中查找,第二个参数1代表页号。
      if( ALWAYS(pPage1) ){  /* Page 1 is always available in cache, because
                             pCache->nRefSum>0 */
        memset(pPage1->pBuf, 0, pCache->szPage);//把database page content部分变为0
        pgno = 1;
      }
    }
    sqlite3GlobalConfig.pcache2.xTruncate(pCache->pCache, pgno+1);//页号>=pgno+1的都将被(从LRU中、apHash中,pcache line内存中)释放
  }
}

sqlite3PcacheClose

/*
Close a cache.
*/

void sqlite3PcacheClose(PCache *pCache){
  assert( pCache->pCache!=0 );
  pcacheTrace(("%p.CLOSE\n",pCache));
  sqlite3GlobalConfig.pcache2.xDestroy(pCache->pCache);//调用的是pcache1.c中的pcache1Destroy例程
}

sqlite3PcacheClear

/* 
Discard the contents of the cache.
*/

void sqlite3PcacheClear(PCache *pCache){
  sqlite3PcacheTruncate(pCache, 0);//将所有页号大于1的页面进行处理,把所有脏页都变为干净的,并刚在LRU双向循环链表中。把所有页面的database page content的内容使用memset函数读设置为0
}

pcacheMergeDirtyList

/*
Merge two lists of pages connected by pDirty and in pgno order.
Do not bother fixing the pDirtyPrev pointers.  将两个列表中的被pDirty标志连接起来的页面以页号的顺序合并在一起。不要费心修复 pDirtyPrev 指针。
*/

static PgHdr *pcacheMergeDirtyList(PgHdr *pA, PgHdr *pB){
  //当用到一个指针的时候,需要分配内存。如果能够找到一个该指针指向的变量,那么直接对该变量使用取地址符号就可以避免分配内存的操作了
  PgHdr result, *pTail;//定义了一个result,就不用为指针pTail动态分配内存了,因为直接使用取地址即可
  pTail = &result;
  assert( pA!=0 && pB!=0 );
  for(;;){
    if( pA->pgno<pB->pgno ){//pA的页号<pB的页号(处理比较小的哪一个:pA)
      pTail->pDirty = pA;
      pTail = pA;//pTail移动到尾部
      pA = pA->pDirty;//pA向后面移动一个位置
      if( pA==0 ){//如果pA为空了
        pTail->pDirty = pB;//那么把让pTail指针指向pB
        break;
      }
    }else{//pA的页号>=pB的页号(这时候处理pB)
      pTail->pDirty = pB;//pTail指向pB
      pTail = pB;//pTail移动到pB
      pB = pB->pDirty;//pB向后需要一个新的待比较的元素
      if( pB==0 ){//如果pB先走到头了
        pTail->pDirty = pA;//那么让pTail指向pA
        break;
      }
    }
  }
  return result.pDirty;//返回result的pDirty指针
}

pcacheSortDirtyList

/*
Sort the list of pages in accending order by pgno.  Pages are
connected by pDirty pointers.  The pDirtyPrev pointers are
corrupted by this sort.

Since there cannot be more than 2^31 distinct pages in a database,
there cannot be more than 31 buckets required by the merge sorter.
One extra bucket is added to catch overflow in case something
ever changes to make the previous sentence incorrect.
*/

通过页号的升序对所有页面进行排序。所有页面通过pDirty指针连在一起。在这种顺序下,pDirtyPrev指针被破坏。因为在数据库中不可能有大于2^31个不同的页面,所以进行合并排序的时候不会有多于31个存储桶。一个例外的存储桶是被用来监察溢出的,以免有一些曾经的变更而带来的之前的语句错误。(不太明白,为什么就按照页号排序了?)

#define N_SORT_BUCKET  32
static PgHdr *pcacheSortDirtyList(PgHdr *pIn){
  PgHdr *a[N_SORT_BUCKET], *p;
  int i;
  memset(a, 0, sizeof(a));
  while( pIn ){
    p = pIn;//这个p是为了记录当前pIn所在的状态
    pIn = p->pDirty;//pIn指向下一个状态
    p->pDirty = 0;//把刚刚使用p记录下来的pIn孤立出来,然后下面开始处理这个孤立出来的p
    for(i=0; ALWAYS(i<N_SORT_BUCKET-1); i++){
      if( a[i]==0 ){//处理i对应的那个存储桶,如果这个存储桶为空(i第一次映射到这个存储桶)
        a[i] = p;//给这个存储桶的第一个元素赋值
        break;//跳出for循环
      }else{
        p = pcacheMergeDirtyList(a[i], p);//融合在一块,使用页号进行排序
        a[i] = 0;//将第
      }
    }
    if( NEVER(i==N_SORT_BUCKET-1) ){
      /* To get here, there need to be 2^(N_SORT_BUCKET) elements in
      the input list.  But that is impossible.如果要走到这个地方,那么输入列表中就需要有2的N_SORT_BUCKET个元素,但这是不可能的
      */
      a[i] = pcacheMergeDirtyList(a[i], p);
    }
  }
  p = a[0];//接下来开始对各个存储桶进行合并操作
  for(i=1; i<N_SORT_BUCKET; i++){
    if( a[i]==0 ) continue;//直到找到一个非空的存储桶然后继续向下执行而不是直接进行下一次循环
    p = p ? pcacheMergeDirtyList(p, a[i]) : a[i];//p存在的话就进行合并,p不存在的话就直接令p等于这个存储桶(显然,很多次都是合并操作,因为p只会越来越满)
  }
  return p;
}

sqlite3PcacheDirtyList

/*
Return a list of all dirty pages in the cache, sorted by page number.
*/返回一个缓存中包含所有脏页的列表,通过页号进行排序。

PgHdr *sqlite3PcacheDirtyList(PCache *pCache){
  PgHdr *p;
  for(p=pCache->pDirty; p; p=p->pDirtyNext){
    p->pDirty = p->pDirtyNext;
  }
  return pcacheSortDirtyList(pCache->pDirty);//调用前面刚刚定义的函数
}

sqlite3PcacheRefCount

/* 
Return the total number of references to all pages held by the cache.

This is not the total number of pages referenced, but the sum of the
reference count for all pages.
*/

返回缓存中所有的页面被引用的总数。这并不是被引用的所有页面的个数,而是所有页面被引用的次数的总和。

int sqlite3PcacheRefCount(PCache *pCache){
  return pCache->nRefSum;
}

sqlite3PcachePageRefcount

/*
Return the number of references to the page supplied as an argument.返回对指定页面的引用的数目。
*/也就是在apHash中的页面的总数(因为最终还是调用pcache1Pagecount例程)

int sqlite3PcachePageRefcount(PgHdr *p){
  return p->nRef;
}

sqlite3PcacheGetCachesize

#ifdef SQLITE_TEST
/*
Get the suggested cache-size value.
*/

int sqlite3PcacheGetCachesize(PCache *pCache){
  return numberOfCachePages(pCache);
}
#endif

其中p->szPage代表的是database page content占用的内存,p->szExtra代表的是该页面的存有该页面管理信息的MemPage占用的内存。

sqlite3PcacheSetCachesize

/*
Set the suggested cache-size value.
*/

void sqlite3PcacheSetCachesize(PCache *pCache, int mxPage){
  assert( pCache->pCache!=0 );
  pCache->szCache = mxPage;//设置szCache
  sqlite3GlobalConfig.pcache2.xCachesize(pCache->pCache,
                                         numberOfCachePages(pCache));//numberOfCachePages返回的是PCache的总共的页面的个数(nMax)
}

sqlite3PcacheSetSpillsize

/*
Set the suggested cache-spill value.  Make no changes if if the
argument is zero.  Return the effective cache-spill size, which will
be the larger of the szSpill and szCache.
*/
设置建议的缓存溢出值。如果参数是0,那么这个操作将是空操作。返回有效的缓存溢出大小,这将是szSpill和szCache中最大的那一个。

int sqlite3PcacheSetSpillsize(PCache *p, int mxPage){
  int res;
  assert( p->pCache!=0 );
  if( mxPage ){
    if( mxPage<0 ){
      mxPage = (int)((-1024*(i64)mxPage)/(p->szPage+p->szExtra));
    }
    p->szSpill = mxPage;
  }
  res = numberOfCachePages(p);//这个获取到的是nMax,是总共的数量
  if( res<p->szSpill ) res = p->szSpill; 
  return res;
}

sqlite3PcacheShrink

/*
Free up as much memory as possible from the page cache.
*/
从缓存中释放尽可能多的内存。首先从LRU双向循环列表中的页面进行处理,pinPage的过程中进行removeFromHash操作。最后apHash表中的页面个数为0时,还要释放pcache-local占用的内存

void sqlite3PcacheShrink(PCache *pCache){
  assert( pCache->pCache!=0 );
  sqlite3GlobalConfig.pcache2.xShrink(pCache->pCache);//调用pcache1.c中的pcache1Shrink例程
}

sqlite3HeaderSizePcache

/*
Return the size of the header added by this middleware layer
in the page-cache hierarchy.
*/

int sqlite3HeaderSizePcache(void){ return ROUND8(sizeof(PgHdr)); }

sqlite3PCachePercentDirty

/*
Return the number of dirty pages currently in the cache, as a percentage
of the configured cache size.返回当前cache中的脏页的总数n,返回n/(configured cache size)的百分比
*/

int sqlite3PCachePercentDirty(PCache *pCache){
  PgHdr *pDirty;
  int nDirty = 0;
  int nCache = numberOfCachePages(pCache);//计算configured cache size
  for(pDirty=pCache->pDirty; pDirty; pDirty=pDirty->pDirtyNext) nDirty++;//循环遍历这个PCache的脏页列表
  return nCache ? (int)(((i64)nDirty * 100) / nCache) : 0;//返回脏页占所有configured cache size的百分比
}

sqlite3PCacheIsDirty

如果cache中存在脏页,就返回true,否则返回false

#ifdef SQLITE_DIRECT_OVERFLOW_READ
/* 
Return true if there are one or more dirty pages in the cache. Else false.
*/
int sqlite3PCacheIsDirty(PCache *pCache){
  return (pCache->pDirty!=0);
}
#endif

sqlite3PcacheIterateDirty

#if defined(SQLITE_CHECK_PAGES) || defined(SQLITE_DEBUG)
/*
For all dirty pages currently in the cache, invoke the specified
callback. This is only used if the SQLITE_CHECK_PAGES macro is
defined.对于所有的在缓存中的脏页,调用特定的回调函数。
*/

void sqlite3PcacheIterateDirty(PCache *pCache, void (*xIter)(PgHdr *)){
  PgHdr *pDirty;
  for(pDirty=pCache->pDirty; pDirty; pDirty=pDirty->pDirtyNext){
    xIter(pDirty);
  }
}
#endif

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值