之前讲了那么多的环境搭建,现在终于可以学习源码了。官方有一篇讲解VFS的文档,对理解sqlite3的VFS有很大的帮助:
http://www.sqlite.org/vfs.html
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的使用参考以下文档
http://nut.sourceforge.net/drh.html
http://www.sqlite.org/tclsqlite.html