demovfs是sqlite3里最简单的一个vfs实现,代码在demovfs.c里。这个文件里的函数就demoWrite()函数稍微复杂点,其他函数基本都很简单。
1.demovfs注册
Sqlitetest_demovfs_Init():注册register_demovfs和unregister_demovfs命令
register_demovfs():将demovfs注册到vfs链表中
unregister_demovfs():将demovfs从vfs链表中移除
sqlite3_demovfs():定义demovfs中sqlite3_vfs结构体中函数指针的具体实现
sqlite3_vfs *sqlite3_demovfs(void){
static sqlite3_vfs demovfs = {
1, /* iVersion */
sizeof(DemoFile), /* szOsFile */
MAXPATHNAME, /* mxPathname */
0, /* pNext */
"demo", /* zName */
0, /* pAppData */
demoOpen, /* xOpen */
demoDelete, /* xDelete */
demoAccess, /* xAccess */
demoFullPathname, /* xFullPathname */
demoDlOpen, /* xDlOpen */
demoDlError, /* xDlError */
demoDlSym, /* xDlSym */
demoDlClose, /* xDlClose */
demoRandomness, /* xRandomness */
demoSleep, /* xSleep */
demoCurrentTime, /* xCurrentTime */
};
return &demovfs;
}
2.demovfs的具体实现
demoOpen():该函数实现如下
static int demoOpen(
sqlite3_vfs *pVfs, /* VFS */
const char *zName, /* File to open, or 0 for a temp file */
sqlite3_file *pFile, /* Pointer to DemoFile struct to populate */
int flags, /* Input SQLITE_OPEN_XXX flags */
int *pOutFlags /* Output SQLITE_OPEN_XXX flags (or NULL) */
){
static const sqlite3_io_methods demoio = {
1, /* iVersion */
demoClose, /* xClose */
demoRead, /* xRead */
demoWrite, /* xWrite */
demoTruncate, /* xTruncate */
demoSync, /* xSync */
demoFileSize, /* xFileSize */
demoLock, /* xLock */
demoUnlock, /* xUnlock */
demoCheckReservedLock, /* xCheckReservedLock */
demoFileControl, /* xFileControl */
demoSectorSize, /* xSectorSize */
demoDeviceCharacteristics /* xDeviceCharacteristics */
};
DemoFile *p = (DemoFile*)pFile; /* Populate this structure */
int oflags = 0; /* flags to pass to open() call */
char *aBuf = 0;
if( zName==0 ){
return SQLITE_IOERR;
}
if( flags&SQLITE_OPEN_MAIN_JOURNAL ){
aBuf = (char *)sqlite3_malloc(SQLITE_DEMOVFS_BUFFERSZ);
if( !aBuf ){
return SQLITE_NOMEM;
}
}
if( flags&SQLITE_OPEN_EXCLUSIVE ) oflags |= O_EXCL;
if( flags&SQLITE_OPEN_CREATE ) oflags |= O_CREAT;
if( flags&SQLITE_OPEN_READONLY ) oflags |= O_RDONLY;
if( flags&SQLITE_OPEN_READWRITE ) oflags |= O_RDWR;
memset(p, 0, sizeof(DemoFile));
p->fd = open(zName, oflags, 0600);
if( p->fd<0 ){
sqlite3_free(aBuf);
return SQLITE_CANTOPEN;
}
p->aBuffer = aBuf;
if( pOutFlags ){
*pOutFlags = flags;
}
p->base.pMethods = &demoio;
return SQLITE_OK;
}
该函数定义并初始化了sqlite3_io_methods 结构体变量demoio,该结构体里是一系列操作io的函数指针的具体实现。
接下来把输入的pFile指针强制转换成DemoFile类型并赋值给p,可以理解为DemoFile是sqlite3_file的派生类。DemoFile结构体声明如下:
typedef struct DemoFile DemoFile;
struct DemoFile {
sqlite3_file base; /* Base class. Must be first. */
//文件描述符
int fd; /* File descriptor */
// aBuffer在向写入文件时进行缓存,最多缓存8192个字节,缓冲去主要针对频率较高的对文件//写入少量数据,减小磁盘操作的开销
char *aBuffer; /* Pointer to malloc'd buffer */
//已经存入缓冲区的字节数
int nBuffer; /* Valid bytes of data in zBuffer */
//这个变量比较难理解,指缓存区aBuffer的第一个字节在文件中的偏移,这个变量在写缓存的//时候会用到
sqlite3_int64 iBufferOfst; /* Offset in file of zBuffer[0] */
};
标志变量flag指示打开的是日志文件,则给写缓存指针分配空间,接下来调用linux下最基本的文件io函数open(),参数oflags代表打开的模式如创建、只读、可读写等等,0600代表新建的文件的权限为可读可写,具体open函数的特性见以下网址
http://blog.csdn.net/linux_loajie/article/details/43916677
open函数返回一个描述符,将在文件读写时使用。
demoDelete():通过unlink函数删除传入的文件名,虽然已经看不到删除的文件,但可能还残留在磁盘中,通过获取该文件所在的目录,并打开文件目录,通过fsync函数将当前的文件目录更新到磁盘中。
demoAccess():确认文件是否存在,是否可读、可写
demoFullPathname():获取文件完整的路径,先通过getcwd()获取当前执行目录,并和文件名拼接成完整目录。
demoDlOpen():调用外部动态库用,暂未实现
demoDlError():调用外部动态库用,暂未实现
demoDlSym():调用外部动态库用,暂未实现
demoDlClose():调用外部动态库用,暂未实现
demoRandomness():和随机数相关,暂未实现
demoSleep():将线程挂起,单位us
demoCurrentTime():获取当前儒略日时间
3.demoio的具体实现
demoClose():先将文件缓存写到文件中,释放p->aBuffer,再关闭文件
demoRead():通过lseek函数定位文件当前的偏移,通过read函数读取内容
demoWrite():这个函数比较复杂,将详细分析
//这个函数根据输入的数据指针、数据长度、和偏移位置将数据写入到磁盘
static int demoDirectWrite(
DemoFile *p, /* File handle */
const void *zBuf, /* Buffer containing data to write */
int iAmt, /* Size of data to write in bytes */
sqlite_int64 iOfst /* File offset to write to */
){
off_t ofst; /* Return value from lseek() */
size_t nWrite; /* Return value from write() */
ofst = lseek(p->fd, iOfst, SEEK_SET);
if( ofst!=iOfst ){
return SQLITE_IOERR_WRITE;
}
nWrite = write(p->fd, zBuf, iAmt);
if( nWrite!=iAmt ){
return SQLITE_IOERR_WRITE;
}
return SQLITE_OK;
}
//这个函数把输入的文件指针中的写缓存写到磁盘里,并把缓冲区长度清0
static int demoFlushBuffer(DemoFile *p){
int rc = SQLITE_OK;
if( p->nBuffer ){
rc = demoDirectWrite(p, p->aBuffer, p->nBuffer, p->iBufferOfst);
p->nBuffer = 0;
}
return rc;
}
static int demoWrite(
sqlite3_file *pFile,
const void *zBuf,
int iAmt,
sqlite_int64 iOfst
){
DemoFile *p = (DemoFile*)pFile;
//向文件写数据时,如果写缓存的指针不为0,那么先把数据写到缓冲区,否则直接写入磁盘
if( p->aBuffer ){
char *z = (char *)zBuf; /* Pointer to remaining data to write */
int n = iAmt; /* Number of bytes at z */
sqlite3_int64 i = iOfst; /* File offset to write to */
while( n>0 ){
int nCopy; /* Number of bytes to copy into buffer */
/* If the buffer is full, or if this data is not being written directly
** following the data already buffered, flush the buffer. Flushing
** the buffer is a no-op if it is empty.
*/
if( p->nBuffer==SQLITE_DEMOVFS_BUFFERSZ || p->iBufferOfst+p->nBuffer!=i ){
int rc = demoFlushBuffer(p);
if( rc!=SQLITE_OK ){
return rc;
}
}
assert( p->nBuffer==0 || p->iBufferOfst+p->nBuffer==i );
p->iBufferOfst = i - p->nBuffer;
/* Copy as much data as possible into the buffer. */
nCopy = SQLITE_DEMOVFS_BUFFERSZ - p->nBuffer;
if( nCopy>n ){
nCopy = n;
}
memcpy(&p->aBuffer[p->nBuffer], z, nCopy);
p->nBuffer += nCopy;
n -= nCopy;
i += nCopy;
z += nCopy;
}
}else{
return demoDirectWrite(p, zBuf, iAmt, iOfst);
}
return SQLITE_OK;
}
通过缓存写入时,每次写满SQLITE_DEMOVFS_BUFFERSZ个字节再把缓存写入文件,通过缓存写入时,每次写数据必须是连续的,p->iBufferOfst+p->nBuffer!=i这个条件用来判断这次写入到文件中的偏移是否和上一次缓冲区写入到文件的偏移保持连续,如果不连续说明上一次向文件写数据已经完成,但缓冲区还没更新到磁盘,所以先把缓冲区的数据写入到磁盘,再开始这次的数据写入。
p->iBufferOfst = i - p->nBuffer;这句话也很难理解,可以这样解释:
p->iBufferOfst是当前缓冲区的第一个字节对应在文件中的偏移,一般情况下,如果是连续写入的,那么这句话不起作用,因为本来就满足p->iBufferOfst+p->nBuffer == i ,向缓冲区写入nCopy个字节后,p->nBuffer+=nCopy,i+=nCopy,所以p->iBufferOfst保持不变,但是如果缓冲区写满后,会调用demoFlushBuffer把缓冲区写入到磁盘,并且p->nBuffer清0,这时缓冲区对应磁盘的偏移就改变了,如果p->iBufferOfst上一次是0,那么写满一次缓冲区后,就变为8192,再接着写满一次缓冲区后就变为8192*2,同时如果第一次写入不是和上一次连续,那么p->iBufferOfst = i - p->nBuffer还保证了下一次连续写入时能够满足条件p->iBufferOfst+p->nBuffer == i,接下来就比较好理解了,每次向缓冲区写入数据的最大值是nCopy = SQLITE_DEMOVFS_BUFFERSZ - p->nBuffer,将输入数据写入到缓冲区。
demoio中的其他函数或是比较简单或是没有实现,就不一一说明了,至此我们大概知道了一个最基本的vfs在干什么,接下来先去学pager层,之后再回过头来看linux和win32平台下一个完善的vfs的实现。