• <noscript id="e0iig"><kbd id="e0iig"></kbd></noscript>
  • <td id="e0iig"></td>
  • <option id="e0iig"></option>
  • <noscript id="e0iig"><source id="e0iig"></source></noscript>
  • SQLite3源碼學習(17) test_vfs的共享內存機制

    標簽: sqlite3  vfs

    VFSIO接口里提供了文件的共享緩存機制,在test_vfs里內置了一個Shared memory模塊用來模擬測試文件的共享緩存,而不是使用原來的VFS提供的接口。

    1.結構定義

    pFd結構

    每一個數據庫文件的連接都對應著一個連接句柄pFile,上層函數調用VFS接口時會傳入pFile,在test_vfspFile會被強制轉換為TestvfsFile*類型,相當于sqlite3_file*的一個繼承

    typedef struct sqlite3_file sqlite3_file;
    struct sqlite3_file {
      const struct sqlite3_io_methods *pMethods;  /* Methods for an open file */
    };
    sqlite3_file *pFile;// pFile作為連接句柄傳入
    
    /*強制轉換為TestvfsFile*類型*/
    typedef struct TestvfsFd TestvfsFd;
    struct TestvfsFile {
      sqlite3_file base;              /* Base class.  Must be first */
      TestvfsFd *pFd;                 /* File data */
    };
    #define tvfsGetFd(pFile) (((TestvfsFile *)pFile)->pFd)
    
    /*從連接句柄中提取pFd*/
    typedef struct TestvfsFd TestvfsFd;
    struct TestvfsFd {
      sqlite3_vfs *pVfs;              /* The VFS */
      const char *zFilename;          /* Filename as passed to xOpen() */
      sqlite3_file *pReal;            /* The real, underlying file descriptor */
      Tcl_Obj *pShmId;                /* Shared memory id for Tcl callbacks */
    
      TestvfsBuffer *pShm;            /* Shared memory buffer */
      u32 excllock;                   /* Mask of exclusive locks */
      u32 sharedlock;                 /* Mask of shared locks */
      TestvfsFd *pNext;               /* Next handle opened on the same file */
    };
    TestvfsFd *pFd = tvfsGetFd(pFile);//每個連接對應一個pFd

    clip_image002

    pBuffer結構

      pBuffer用來存儲共享緩存,每一個文件都有自己的緩存,這些緩存組成一個鏈表。如果有多個連接打開同一個文件,那么相同文件對應連接的pFd又會組成一個鏈表,pFile指向pFd的表頭。

    pBuffer的類型為TestvfsBuffer*,定義如下:

    struct TestvfsBuffer {
      char *zFile;                    /* Associated file name */
      int pgsz;                       /* Page size */
      u8 *aPage[TESTVFS_MAX_PAGES];   /* Array of ckalloc'd pages */
      TestvfsFd *pFile;               /* List of open handles */
      TestvfsBuffer *pNext;           /* Next in linked list of all buffers */
    };

    緩存鏈表的表頭pBuffer存在pFd->pVfs->pAppData

    clip_image004

    結構關系

      

    假設用多個連接打開同一個文件,其關系如下

    clip_image006

     

    2.具體實現

    tvfsShmMap():

    獲取共享緩存中的具體頁面空間

    static int tvfsShmMap(
      sqlite3_file *pFile,            /* Handle open on database file */
      int iPage,                      /* Page to retrieve */
      int pgsz,                       /* Size of pages */
      int isWrite,                    /* True to extend file if necessary */
      void volatile **pp              /* OUT: Mapped memory */
    ){
      int rc = SQLITE_OK;
      TestvfsFd *pFd = tvfsGetFd(pFile);
      Testvfs *p = (Testvfs *)(pFd->pVfs->pAppData);
    
     ……
    /*如果該連接的緩存不存在,為該連接分配緩存*/
      if( 0==pFd->pShm ){
        rc = tvfsShmOpen(pFile); 
        if( rc!=SQLITE_OK ){
          return rc;
        }
      }
    ……
    /*如果第iPage頁的緩存為空,那么分配pgsz 長度的空間*/
      if( rc==SQLITE_OK && isWrite && !pFd->pShm->aPage[iPage] ){
        tvfsAllocPage(pFd->pShm, iPage, pgsz);
      }
      *pp = (void volatile *)pFd->pShm->aPage[iPage];
    
      return rc;
    }

    tvfsShmOpen():

    為對應的連接分配緩存地址,如果緩存不存在,那么新建一個節點插入到pFd鏈表中。

    static int tvfsShmOpen(sqlite3_file *pFile){
      Testvfs *p;
      int rc = SQLITE_OK;             /* Return code */
      TestvfsBuffer *pBuffer;         /* Buffer to open connection to */
      TestvfsFd *pFd;                 /* The testvfs file structure */
    
      pFd = tvfsGetFd(pFile);
      p = (Testvfs *)pFd->pVfs->pAppData;
      assert( 0==p->isFullshm );
      assert( pFd->pShmId && pFd->pShm==0 && pFd->pNext==0 );
         ……
    
      /* Search for a TestvfsBuffer. Create a new one if required. */
      for(pBuffer=p->pBuffer; pBuffer; pBuffer=pBuffer->pNext){
        if( 0==strcmp(pFd->zFilename, pBuffer->zFile) ) break;
      }
      /*如果緩存鏈表中沒有該連接文件的緩存,新建一個節點插入到pBuffer鏈表中*/
      if( !pBuffer ){
        int szName = (int)strlen(pFd->zFilename);
        int nByte = sizeof(TestvfsBuffer) + szName + 1;
        pBuffer = (TestvfsBuffer *)ckalloc(nByte);
        memset(pBuffer, 0, nByte);
        pBuffer->zFile = (char *)&pBuffer[1];
        memcpy(pBuffer->zFile, pFd->zFilename, szName+1);
        pBuffer->pNext = p->pBuffer;
        p->pBuffer = pBuffer;
      }
      /*把新連接插入到pFd鏈表的表頭,并讓pBuffer->pFile指向表頭*/
      /* Connect the TestvfsBuffer to the new TestvfsShm handle and return. */
      pFd->pNext = pBuffer->pFile;
      pBuffer->pFile = pFd;
      pFd->pShm = pBuffer;
    
      return SQLITE_OK;
    }

    tvfsShmUnmap():

    把連接從pFd鏈表中移除,移除后對應的pBuffer如果沒有與之對應的連接,那么釋放pBuffer的所有空間

    static int tvfsShmUnmap(
      sqlite3_file *pFile,
      int deleteFlag
    ){
      int rc = SQLITE_OK;
      TestvfsFd *pFd = tvfsGetFd(pFile);
      Testvfs *p = (Testvfs *)(pFd->pVfs->pAppData);
      TestvfsBuffer *pBuffer = pFd->pShm;
      TestvfsFd **ppFd;
    
    /*把連接從pFd鏈表中移除, pBuffer->pFile存放頭節點地址*/
      for(ppFd=&pBuffer->pFile; *ppFd!=pFd; ppFd=&((*ppFd)->pNext));
      assert( (*ppFd)==pFd );
      *ppFd = pFd->pNext;//注意ppFd是二級指針,假如pPrev是pFd的上一個節點地址,那么*ppFd相當于pPrev->pNext
      pFd->pNext = 0;
    /*如果pFd是pBuffer的最后一個連接,那么釋放pBuffer->pFile */
      if( pBuffer->pFile==0 ){
        int i;
        TestvfsBuffer **pp;
        for(pp=&p->pBuffer; *pp!=pBuffer; pp=&((*pp)->pNext));
        *pp = (*pp)->pNext;
        for(i=0; pBuffer->aPage[i]; i++){
          ckfree((char *)pBuffer->aPage[i]);
        }
        ckfree((char *)pBuffer);
      }
      pFd->pShm = 0;
    
      return rc;
    }

    tvfsShmLock():

    如果多個線程的連接共享一個緩存,需要對共享緩存加鎖,如果已經有獨占鎖了那么返回busy

    如果加的是獨占鎖已經有共享鎖了,那么也返回busy,也就是說有共享鎖的情況下不能獲取獨占鎖,這是為了防止中途讀取了一半的數據的時候被修改。

    這里似乎有個漏洞,如果持續不斷地加共享鎖,那么會導致獨占鎖一直加不上,也就不能寫數據了。

    static int tvfsShmLock(
      sqlite3_file *pFile,
      int ofst,
      int n,
      int flags
    ){
      int rc = SQLITE_OK;
      TestvfsFd *pFd = tvfsGetFd(pFile);
      Testvfs *p = (Testvfs *)(pFd->pVfs->pAppData)
      ……
      if( rc==SQLITE_OK ){
        int isLock = (flags & SQLITE_SHM_LOCK);
        int isExcl = (flags & SQLITE_SHM_EXCLUSIVE);
        u32 mask = (((1<<n)-1) << ofst);//n為掩碼數量,ofst為掩碼偏移
        if( isLock ){
          TestvfsFd *p2;
          for(p2=pFd->pShm->pFile; p2; p2=p2->pNext){
            if( p2==pFd ) continue;
            if( (p2->excllock&mask) || (isExcl && p2->sharedlock&mask) ){
              rc = SQLITE_BUSY;
              break;
            }
          }
          /*獲得鎖資源后對緩存加鎖*/
          if( rc==SQLITE_OK ){
            if( isExcl )  pFd->excllock |= mask;
            if( !isExcl ) pFd->sharedlock |= mask;
          }
        }else{/*釋放鎖*/
          if( isExcl )  pFd->excllock &= (~mask);
          if( !isExcl ) pFd->sharedlock &= (~mask);
        }
      }
    
      return rc;
    }
    

     

    版權聲明:本文為pfysw原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接和本聲明。
    本文鏈接:https://blog.csdn.net/pfysw/article/details/79606348

    智能推薦

    SQLite3的基礎學習以及Python實踐讀寫db文件

    Python集成自帶的最精巧的數據庫管理方案。 基礎命令行操作 鏈接:https://pan.baidu.com/s/1hU1nYRSdm8K0JOeQZVMvJQ 密碼:2cyq為Xmind文件。這里的命令雖然全是大寫,但是sqlite3并不區分大小寫,都用小寫也可以。  python基礎操作 這里寫了一個將Name這一個列表進行寫入db文件的操作,首先創建,添加等。 然后將文件導入的...

    3. django的sqlite3設置

    1. 每次執行python manage.py runserver都會出現下圖(標紅的信息) 1.1 怎么把這個提示去掉 sqlite3需要先將服務運行一次后才會出現 python manage.py runserver 2. 然后輸入下面的語句 2.1 出現下圖的提示信息 3. 然后再重新運行服務 3.1 這樣就不會出現紅字了 4.1 因為執行了python manage.py migrate,...

    SQLite3源碼學習(27) Bitmap算法

    1. 算法背景              假如有100個不重復的數存放在文件里,怎么確定某個數是否在這100個數中?        一般可以這樣做,將這100個數讀取到內存并存放在char a[100]的數組里,只需遍歷這100個數即可。       &nbs...

    SQLite3源碼學習(22) Page Cache分析

    上一篇學習了pcache1的機制,這是pagecache管理的一個插件,在這基礎上又封裝了一層,主要是用來處理臟頁(就是修改過的緩存頁),如臟頁的添加刪除和回收利用等,這部分代碼的實現在pcache.c里。 1.數據結構 在pcache中,通過PCache結構對象作為連接句柄,每個緩存頁通過PgHdr來表示。 在pagecache中,所有的臟頁通過一個雙向鏈表來連接在一起,其結構關系如下圖所示: ...

    SQLite3源碼學習(14) 模擬靜態變量

           如果目標平臺不支持靜態變量和全局變量,所有變量都需要從棧或堆里分配,這時候全局變量的聲明都是const,sqlite以變量的地址作為關鍵字來映射堆中的一片地址空間,從而對這個地址的讀寫來代替對靜態變量的讀寫。 使用時需要打開SQLITE_OMIT_WSD宏定義,以全局變量sqlite3Config為例,首先把sql...

    猜你喜歡

    SQLite3源碼學習(9)Page Cache概述

            Page cache是進程分配的內存空間,用來緩存數據頁面。page cache的管理獨立于操作系統,當一個線程打開一個數據庫連接時就會建立一個page cache,對于一個進程中的多線程,它們可以有獨立的cache也可以共享一個cahce,下圖描述了page cache的結構: 在Pager初始化時為pPager->pPCa...

    HTML中常用操作關于:頁面跳轉,空格

    1.頁面跳轉 2.空格的代替符...

    freemarker + ItextRender 根據模板生成PDF文件

    1. 制作模板 2. 獲取模板,并將所獲取的數據加載生成html文件 2. 生成PDF文件 其中由兩個地方需要注意,都是關于獲取文件路徑的問題,由于項目部署的時候是打包成jar包形式,所以在開發過程中時直接安照傳統的獲取方法沒有一點文件,但是當打包后部署,總是出錯。于是參考網上文章,先將文件讀出來到項目的臨時目錄下,然后再按正常方式加載該臨時文件; 還有一個問題至今沒有解決,就是關于生成PDF文件...

    電腦空間不夠了?教你一個小秒招快速清理 Docker 占用的磁盤空間!

    Docker 很占用空間,每當我們運行容器、拉取鏡像、部署應用、構建自己的鏡像時,我們的磁盤空間會被大量占用。 如果你也被這個問題所困擾,咱們就一起看一下 Docker 是如何使用磁盤空間的,以及如何回收。 docker 占用的空間可以通過下面的命令查看: TYPE 列出了docker 使用磁盤的 4 種類型: Images:所有鏡像占用的空間,包括拉取下來的鏡像,和本地構建的。 Con...

    requests實現全自動PPT模板

    http://www.1ppt.com/moban/ 可以免費的下載PPT模板,當然如果要人工一個個下,還是挺麻煩的,我們可以利用requests輕松下載 訪問這個主頁,我們可以看到下面的樣式 點每一個PPT模板的圖片,我們可以進入到詳細的信息頁面,翻到下面,我們可以看到對應的下載地址 點擊這個下載的按鈕,我們便可以下載對應的PPT壓縮包 那我們就開始做吧 首先,查看網頁的源代碼,我們可以看到每一...

    精品国产乱码久久久久久蜜桃不卡