1. 程式人生 > >Sqlite3原始碼學習(5)OS的介面VFS

Sqlite3原始碼學習(5)OS的介面VFS

    之前講了那麼多的環境搭建,現在終於可以學習原始碼了。官方有一篇講解VFS的文件,對理解sqlite3VFS有很大的幫助:

1.VFS簡介

VFS也就是所謂的虛擬檔案系統,因為sqlite3執行在不同的平臺上會有不同的檔案系統,VFS就是對不同的檔案系統做一個統一的介面。

先來看一下一張圖:

clip_image001[4]

這張圖展示了sqlite3的軟體層次結構,主要分為前端和後端,OS層位於最底層,是和系統的磁碟檔案儲存直接打交道,在OS層裡封裝了VFS

Sqlit3VFS體現了跨平臺的特性,雖然不同檔案系統的實現是不一樣的,但是介面都是相同的。

2.VFS的組成

VFS最基本的物件是sqlite3_vfs

結構體:

typedef struct sqlite3_vfs sqlite3_vfs;
typedef void (*sqlite3_syscall_ptr)(void);
struct sqlite3_vfs {
  int iVersion;            /* Structure version number (currently 3) */
  int szOsFile;            /* Size of subclassed sqlite3_file */
  int mxPathname;          /* Maximum file pathname length */
  sqlite3_vfs *pNext;      /* Next registered VFS */
  const char * zName;       /* Name of this virtual file system */
  void *pAppData;          /* Pointer to application-specific data */
  int (*xOpen)(sqlite3_vfs*, const char *zName, sqlite3_file*,
               int flags, int *pOutFlags);
  int (*xDelete)(sqlite3_vfs*, const char *zName, int syncDir);
  int (*xAccess)(sqlite3_vfs*, const char *zName, int flags, int *pResOut);
  int (*xFullPathname)(sqlite3_vfs*, const char *zName, int nOut, char *zOut);
  void *(*xDlOpen)(sqlite3_vfs*, const char *zFilename);
  void (*xDlError)(sqlite3_vfs*, int nByte, char *zErrMsg);
  void (*(*xDlSym)(sqlite3_vfs*,void*, const char *zSymbol))(void);
  void (*xDlClose)(sqlite3_vfs*, void*);
  int (*xRandomness)(sqlite3_vfs*, int nByte, char *zOut);
  int (*xSleep)(sqlite3_vfs*, int microseconds);
  int (*xCurrentTime)(sqlite3_vfs*, double*);
  int (*xGetLastError)(sqlite3_vfs*, int, char *);
  /*
  ** The methods above are in version 1 of the sqlite_vfs object
  ** definition.  Those that follow are added in version 2 or later
  */
  int (*xCurrentTimeInt64)(sqlite3_vfs*, sqlite3_int64*);
  /*
  ** The methods above are in versions 1 and 2 of the sqlite_vfs object.
  ** Those below are for version 3 and greater.
  */
  int (*xSetSystemCall)(sqlite3_vfs*, const char *zName, sqlite3_syscall_ptr);
  sqlite3_syscall_ptr (*xGetSystemCall)(sqlite3_vfs*, const char *zName);
  const char *(*xNextSystemCall)(sqlite3_vfs*, const char *zName);
  /*
  ** The methods above are in versions 1 through 3 of the sqlite_vfs object.
  ** New fields may be appended in future versions.  The iVersion
  ** value will increment whenever this happens.
  */
};

結構體裡定義了一系列函式指標,幾個關鍵的地方是:
int szOsFile: file結構體的大小,繼承自sqlite3_file,分配空間時要用
sqlite3_vfs *pNext;:指向下一個vfs節點的指標
const char * zName:vfs的名字
void *pAppData:這個指標指向一個儲存了各種檔案io操作方法的結構體地址,如win下是

typedef struct winVfsAppData winVfsAppData;
struct winVfsAppData {
  const sqlite3_io_methods *pMethod; /* The file I/O methods to use. */
  void *pAppData;                    /* The extra pAppData, if any. */
  BOOL bNoLock;                      /* Non-zero if locking is disabled. */
};
static winVfsAppData winAppData = {
  &winIoMethod,       /* pMethod */
  0,                  /* pAppData */
  0                   /* bNoLock */
};

要實現一個VFS實體,就是實現sqlite3_vfssqlite3_io_methods結構體裡的一系列方法,即函式指標的實體函式的實現,sqlite3_file結構體作為一個檔案控制代碼的傳入引數,在open時會將sqlite3_file指標強制轉為特定的file指標,該file結構體從sqlite3_file繼承,感覺有點像C++sqlite3_file相當於一個基類,在不同VFS下有不同的派生類。

3.VFS的型別

win下的vfswin32win32-longpathwin32-nonewin32-longpath-none,預設使用的是win32 vfs;在類unix系統下的vfsunixunix-dotfilunix-exclunix-none等,預設使用的是unix vfssqlite3核心在初始化時呼叫sqlite3_os_init()函式來註冊vfssqlite3_os_init()在不同系統下有不同的實現。

除了上面這些在test檔案裡還實現了一些其他的vfs

test_demovfs.c:最簡單的vfs,可以學習vfs的基本實現

test_onefile.c:用於嵌入式裝置的vfs

test_quota.c:實現了一個名為quotavfs、功能暫時不清楚,具體見官方文件

test_multiplex.c:這個vfs好像是用來處理大檔案,具體見官方文件

test_journal.c:這個vfs主要測試回滾日誌的儲存

test_vfs.c:這個檔案主要實現檔案系統出錯的模擬

4.VFS的註冊和使用

如果vfs沒有在sqlite3_os_init()裡註冊,那麼就要使用sqlite3_vfs_register函式來註冊,其實現如下:

int sqlite3_vfs_register(sqlite3_vfs *pVfs, int makeDflt){
  MUTEX_LOGIC(sqlite3_mutex *mutex;)
#ifndef SQLITE_OMIT_AUTOINIT
  int rc = sqlite3_initialize();
  if( rc ) return rc;
#endif
#ifdef SQLITE_ENABLE_API_ARMOR
  if( pVfs==0 ) return SQLITE_MISUSE_BKPT;
#endif
 
  MUTEX_LOGIC( mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER); )
  sqlite3_mutex_enter(mutex);
  vfsUnlink(pVfs);
  if( makeDflt || vfsList==0 ){
    pVfs->pNext = vfsList;
    vfsList = pVfs;
  }else{
    pVfs->pNext = vfsList->pNext;
    vfsList->pNext = pVfs;
  }
  assert(vfsList);
  sqlite3_mutex_leave(mutex);
  return SQLITE_OK;
}

這個函式的傳入引數為要註冊的vfs地址pVfsmakeDflt,將pVfsv儲存到連結串列中,vfsList為頭節點,如果makeDflt不為0,那麼pVfs作為頭節點,否則pVfs指向頭節點的下一個節點,這裡新增節點時需要加鎖,防止多執行緒時被重入。

註冊後就可以通過呼叫sqlite3_open_v2()來替換vfs,下面以demo vfs為示例

    sqlite3_vfs_register(sqlite3_demovfs(), 1);//註冊
    int rc = sqlite3_open_v2("demo.db", &db, SQLITE_OPEN_READWRITE, "demo");//使用demo vfs替換預設的vfs
  在tcl下的使用時,先執行上篇講的tcl擴充套件程式,輸入以下命令
    register_demovfs #註冊demo vfs
    sqlite3 db example1.db -vfs demo #開啟example1.db資料庫,並選擇demo vfs替換預設的vfs。
    db eval {CREATE TABLE t1(a TEXT, b INTEGER)} #新建表
db eval {
   INSERT INTO t1 VALUES('one',1);
   INSERT INTO t1 VALUES('two',2); 
   INSERT INTO t1 VALUES(NULL,3);
}  #插入行
    puts [db eval {SELECT * FROM t1}] #顯示錶的所有行,最後輸出結果:
    one 1 two 2 {} 3
關於sqlite3tcl的使用參考以下文件