Small. Fast. Reliable.
Choose any three.
SQLite OS接口或“ VFS”

1.简介

本文介绍了SQLite OS可移植性层或“ VFS”-SQLite实现堆栈底部的模块,该模块提供了跨操作系统的可移植性。

2.与其他SQLite有关的VFS

SQLite库的内部组织可以视为如右图所示的模块堆栈。令牌生成器,解析器和代码生成器组件用于处理SQL语句,并将其转换为虚拟机语言或字节码的可执行程序。粗略地说,这三层实现了 sqlite3_prepare_v2()。前三层生成的字节码是准备好的语句。虚拟机模块负责运行SQL语句字节代码。B-Tree模块将数据库文件组织到具有有序键和对数性能的多个键/值存储中。Pager模块负责将数据库文件的页面加载到内存中,以实现和控制事务,并创建和维护日记文件,以防止崩溃或电源故障后数据库损坏。OS Interface是一个精简的抽象,它提供了一组通用的例程,以使SQLite适应在不同的操作系统上运行。粗略地说,最下面的四层实现 sqlite3_step()

本文是关于底层的。

OS Interface(也称为“ VFS”)是使SQLite可跨操作系统移植的原因。每当SQLite中的任何其他模块需要与操作系统进行通信时,它们都会调用VFS中的方法。然后,VFS调用满足请求所需的特定于操作的代码。因此,将SQLite移植到新的操作系统只需编写一个新的OS接口层或“ VFS”即可。

3.多个VFS

标准的SQLite源代码树包含用于UNIX和Windows的内置VFS。可以使用sqlite3_vfs_register()接口在启动时或运行时添加备用VFS 。

可以同时注册多个VFS。每个VFS都有一个唯一的名称。同一进程内的单独数据库连接可以同时使用不同的VFS。因此,如果单个数据库连接使用ATTACH命令打开了多个数据库文件,则每个连接的数据库可能正在使用不同的VFS。

3.1。标准Unix VFS

Unix构建带有多个内置VFS。Unix的默认VFS称为“ unix”,并且在大多数应用程序中使用。可能在unix中找到的其他VFS(取决于编译时选项)包括:

  1. unix-dotfile-使用点文件锁定而不是POSIX咨询锁定。

  2. unix-excl-获取并持有数据库文件的排他锁,以防止其他进程访问数据库。还将wal-index保留在堆中,而不是共享内存中。

  3. unix-none-所有文件锁定操作均为无操作。

  4. unix-namedsem-使用命名信号量进行文件锁定。仅限于VXWorks。

各种UNIX VFS的区别仅在于它们处理文件锁定的方式不同-它们彼此共享大多数实现,并且都位于同一SQLite源文件 os_unix.c中。请注意,除了“ unix”和“ unix-excl”以外,各种UNIX VFS都使用不兼容的锁定实现。如果两个进程正在使用不同的UNIX VFS访问同一SQLite数据库,则它们可能看不到彼此的锁,并且最终可能会相互干扰,从而导致数据库损坏。特别是“ unix-none” VFS根本不锁定,如果同时由两个或多个数据库连接使用,则很容易导致数据库损坏。鼓励程序员仅使用“ unix”或“ unix-excl”

3.2。标准Windows VFS

Windows版本还带有多个内置VFS。默认的Windows VFS称为“ win32”,并在大多数应用程序中使用。在Windows版本中可能找到的其他VFS包括:

  1. win32-longpath-类似于“ win32”,除了路径名的最大长度为65534字节,而“ win32”中的路径名最大为1040字节。

  2. win32-none-所有文件锁定操作均为无操作。

  3. win32-longpath-none- “ win32-longpath”和“ win32-none”的组合-支持长路径名,并且所有锁定操作均为无操作。

与unix一样,各种Windows VFS的大多数代码都是共享的。

3.3。指定要使用的VFS

始终有一个VFS,它是默认的VFS。在unix系统上,“ unix” VFS作为默认值出现,在Windows上为“ win32”。如果未执行其他任何操作,则新的数据库连接将使用默认的VFS。

可以通过使用带有第二个参数1的sqlite3_vfs_register()接口注册或重新注册VFS来更改默认VFS 。因此,如果(unix)进程希望始终使用“ unix-nolock” VFS代替“ unix”,则以下代码将起作用:

sqlite3_vfs_register(sqlite3_vfs_find(“ unix-nolock”),1);

也可以将备用VFS指定为sqlite3_open_v2()函数的第4个参数 。例如:

int rc = sqlite3_open_v2(“ demo.db”,&db,SQLITE_OPEN_READWRITE,“ unix-nolock”);

最后,如果启用了URI文件名,则可以使用URI上的“ vfs =“参数来指定备用VFS。这种技术与工程sqlite3_open() sqlite3_open16() sqlite3_open_v2() ,并在新的数据库ATTACH -ed到现有的数据库连接。例如:

ATTACH'file:demo2.db?vfs = unix-none'AS demo2;

URI指定的VFS具有最高优先级。之后,将一个VFS指定为sqlite3_open_v2()的第四个参数。如果未指定其他VFS,则使用默认VFS。

3.4。VFS垫片

从SQLite堆栈的较高层的角度来看,每个打开的数据库文件仅使用一个VFS。但是实际上,特定的VFS可能只是完成实际工作的另一个VFS的薄包装。我们将包装VFS​​称为“填充程序”。

填充程序的一个简单示例是“ vfstrace” VFS。这是一个VFS(在 test_vfstrace.c 源文件中实现),它将与每个VFS方法调用关联的消息写入日志文件,然后将控制权转交给另一个VFS进行实际工作。

3.5。其他示例VFS

以下是公共SQLite源代码树中可用的其他VFS实现:

核心SQLite源代码库和可用扩展中都有其他VFS实现。上面的列表并不意味着详尽无遗,而只是代表可以使用VFS接口实现的功能种类。

4. VFS实施

通过子类化三个对象来实现新的VFS:

一个sqlite3_vfs对象定义VFS和芯的方法实现该接口到操作系统,如检查的文件的存在,删除文件,创建文件和开口和用于读取和/或写入,转换文件名到其规范的名称形式。所述sqlite3_vfs对象还包含用于从所述操作系统获得随机性,用于悬挂的处理(睡眠)和用于确定当前日期和时间的方法。

sqlite3_file对象代表一个打开的文件。 打开文件时,sqlite3_vfs的xOpen方法构造一个sqlite3_file对象。该sqlite3_file保持它被打开,而跟踪文件的状态。

The sqlite3_io_methods object holds the methods used to interact with an open file. Each sqlite3_file contains a pointer to an sqlite3_io_methods object that is appropriate for the file it represents. The sqlite3_io_methods object contains methods to do things such as read and write from the file, to truncate the file, to flush any changes to persistent storage, to find the size of the file, to lock and unlock the file, and to close file and destroy the sqlite3_file object.

为新的VFS编写代码涉及为sqlite3_vfs对象构造一个子类,然后使用对sqlite3_vfs_register()的调用来注册该VFS对象。VFS实现还为sqlite3_filesqlite3_io_methods提供了子类,但这些对象未直接在SQLite中注册。相反, sqlite3_file对象从的XOPEN方法返回 sqlite3_vfssqlite3_file对象指向的一个实例sqlite3_io_methods对象。