Sqlite3原始碼學習(5)OS的介面VFS
之前講了那麼多的環境搭建,現在終於可以學習原始碼了。官方有一篇講解VFS的文件,對理解sqlite3的VFS有很大的幫助:
1.VFS簡介
VFS也就是所謂的虛擬檔案系統,因為sqlite3執行在不同的平臺上會有不同的檔案系統,VFS就是對不同的檔案系統做一個統一的介面。
先來看一下一張圖:
這張圖展示了sqlite3的軟體層次結構,主要分為前端和後端,OS層位於最底層,是和系統的磁碟檔案儲存直接打交道,在OS層裡封裝了VFS。
Sqlit3的VFS體現了跨平臺的特性,雖然不同檔案系統的實現是不一樣的,但是介面都是相同的。
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_vfs和sqlite3_io_methods結構體裡的一系列方法,即函式指標的實體函式的實現,sqlite3_file結構體作為一個檔案控制代碼的傳入引數,在open時會將sqlite3_file指標強制轉為特定的file指標,該file結構體從sqlite3_file繼承,感覺有點像C++,sqlite3_file相當於一個基類,在不同VFS下有不同的派生類。
3.VFS的型別
在win下的vfs有win32、win32-longpath、win32-none和win32-longpath-none,預設使用的是win32 vfs;在類unix系統下的vfs有unix、unix-dotfil、unix-excl、unix-none等,預設使用的是unix vfs。sqlite3核心在初始化時呼叫sqlite3_os_init()函式來註冊vfs,sqlite3_os_init()在不同系統下有不同的實現。
除了上面這些在test檔案裡還實現了一些其他的vfs如
test_demovfs.c:最簡單的vfs,可以學習vfs的基本實現
test_onefile.c:用於嵌入式裝置的vfs
test_quota.c:實現了一個名為quota的vfs、功能暫時不清楚,具體見官方文件
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地址pVfs和makeDflt,將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
關於sqlite3中tcl的使用參考以下文件