VFS的IO接口里提供了文件的共享缓存机制,在test_vfs里内置了一个Shared memory模块用来模拟测试文件的共享缓存,而不是使用原来的VFS提供的接口。
1.结构定义
pFd结构
每一个数据库文件的连接都对应着一个连接句柄pFile,上层函数调用VFS接口时会传入pFile,在test_vfs里pFile会被强制转换为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
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里
结构关系
假设用多个连接打开同一个文件,其关系如下
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,