SQLite 4.9的 OS 接口或“VFS”(十三)

65 篇文章 0 订阅
62 篇文章 2 订阅

返回:SQLite—系列文章目录   

上一篇:SQLite字节码引擎(十二)

下一篇:SQLite 4.9的虚拟表机制(十四)

1. 引言

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

VFS是Virtual File System(虚拟文件系统)的缩写,是一个计算机文件系统的概念。它允许用户在操作系统中通过不同的协议和存储形式访问文件,而不必考虑底层文件系统的物理实现。这些协议可以是本地文件系统、网络文件系统、FTP、WebDAV、数据库等等。它的作用是为不同的文件系统提供一个统一的接口,使得开发人员能够更轻松地处理和使用文件系统。许多操作系统和软件都使用VFS来管理文件系统,例如Linux操作系统、Windows操作系统等。

2. VFS 与 SQLite 其余部分的关系

SQLite库的内部组织可以看作是 右侧显示的模块堆栈。 Tokenizer、Parser 和 Code Generator 组件用于 处理 SQL 语句并将其转换为可执行程序 使用虚拟机语言或字节码。 粗略地说,这前三层实现了 sqlite3_prepare_v2()。前三名生成的字节码 layers 是一个准备好的语句。 虚拟机模块负责运行 SQL 语句 字节码。B-Tree 模块将数据库文件组织成多个 具有有序键和对数性能的键/值存储。 Pager 模块负责加载数据库的页面 文件存入内存,用于实现和控制事务,以及 用于创建和维护阻止数据库的日志文件 崩溃或电源故障后的损坏。 操作系统接口是一个精简抽象,它提供了一组通用的 用于调整 SQLite 以在不同操作系统上运行的例程。 粗略地说,最底层的四层实现了sqlite3_step()。

这篇文章是关于底层的。

操作系统接口 - 也称为“VFS” - 是SQLite的组成部分 跨操作系统可移植。每当任何其他模块 在SQLite中需要与操作进行通信 系统,它们调用 VFS 中的方法。然后,VFS 调用 满足请求所需的特定于操作的代码。 因此,将 SQLite 移植到新的 操作系统只是编写一个新的操作系统接口层的问题 或“VFS”。

3. 多个 VFS

标准 SQLite 源代码树包含用于 unix 的内置 VFS 和窗户。替代 VFS 可以是 使用 sqlite3_vfs_register() 接口在 start-time 或 run-time 添加。

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

3.1. 标准 Unix VFS

Unix 构建带有多个内置 VFS。默认 VFS 因为 UNIX 被称为“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。

可以通过注册或重新注册 VFS 使用带有第二个参数的 sqlite3_vfs_register() 接口 的 1.因此,如果 (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 连接到现有数据库连接时。 例如:

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

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

3.4. VFS垫片

从 SQLite 堆栈的上层来看,每个 打开数据库文件仅使用一个 VFS。 但在实践中,特定的 VFS 可能会 只是成为另一个做真正工作的 VFS 的薄包装器。 我们将包装器 VFS 称为“填充码”。

填充码的一个简单示例是“vfstrace”VFS。这是一个 VFS (在 test_vfstrace.c 源文件中实现),用于写入与每个 VFS 方法调用关联的消息 到日志文件中,然后将控制权传递给另一个 VFS 以执行实际操作 工作。

3.5. 其他示例 VFS

以下是公开提供的其他 VFS 实现 SQLite源代码树:

  • appendvfs.c - 此 VFS 允许将 SQLite 数据库附加到某些 其他文件。例如,这可用于追加 SQLite 数据库 到可执行文件的末尾,这样,当运行时,它可以很容易地 找到追加的数据库。命令行 shell 将使用此 VFS(如果使用 --append 选项启动)及其 .archive 命令 将在给定 --append 标志的情况下使用它。

  • test_demovfs.c - 此文件实现了一个名为“demo”的非常简单的 VFS,它使用 POSIX 功能,例如 open(), read(), write(), fsync(), close(), fsync(), fsync(), sleep(), time(), 等等。此 VFS 仅适用于 unix 系统。但事实并非如此 旨在替代默认使用的标准“unix”VFS 在 UNIX 平台上。“演示”VFS刻意保持非常简单 这样它就可以用作学习辅助工具或构建模板 其他 VFS 或用于将 SQLite 移植到新的操作系统。

  • test_quota.c - 此文件实现一个名为“quota”的填充码,该填充码强制执行累积 数据库文件集合的文件大小限制。辅助 接口用于定义“配额组”。配额组是一个 文件集(数据库文件、日志和临时文件),其 名称都与 GLOB 模式匹配。所有文件大小的总和 在每个配额组中跟踪,以及该总和是否超过阈值 为配额组定义后,将调用回调函数。那 回调可以增加阈值,也可以导致操作 这将超过配额,并因SQLITE_FULL错误而失败。此填充码的用途之一用于强制执行 Firefox 中应用程序数据库的资源限制。

  • test_multiplex.c - 此文件实现一个填充码,该填充码允许数据库文件超过 底层文件系统的最大文件大小。这个垫片呈现 SQLite 上六层的接口,使其看起来像 正在使用非常大的文件,而实际上每个这样的大文件 在底层系统上拆分为许多较小的文件。 例如,此填充码已用于允许数据库增长 FAT16 文件系统上大于 2 GB。

  • test_onefile.c - 此文件实现了一个名为“fs”的演示 VFS,它显示了 SQLite 如何 可以在缺少文件系统的嵌入式设备上使用。内容是 直接写入基础媒体。派生自 VFS 演示代码可以由数量有限的小工具使用 闪存,使 SQLite 充当闪存的文件系统 在设备上。

  • test_journal.c - 此文件实现了 SQLite 测试期间使用的填充码,用于验证 数据库和回滚日志按正确的顺序写入,并且 在适当的时间“同步”,以保证数据库 可以随时从断电中恢复硬复位。垫片 检查数据库操作和回滚的几个不变量 日志,并在违反任何这些不变量时引发异常。 反过来,这些不变量确保数据库始终是可恢复的。 使用此填充码运行大量测试用例可提供额外的 保证SQLite数据库不会因意外而损坏 电源故障或设备重置。

  • test_vfs.c - 此文件实现了可用于模拟文件系统故障的填充码。 此填充码在测试期间用于验证 SQLite 的响应是否合理 硬件故障或其他错误情况,例如用完 的文件系统空间,难以在实际系统上进行测试。

在核心 SQLite 源代码中还有其他 VFS 实现 库和可用的扩展。上面的列表并不意味着 详尽无遗,但仅代表可以 使用VFS接口实现。

4. VFS实现

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

sqlite3_vfs对象定义 VFS 和内核的名称 实现操作系统接口的方法,例如 如检查文件是否存在、删除文件、创建文件 以及打开和用于读取和/或写入、转换文件名 变成他们的规范形式。sqlite3_vfs对象还包含 从操作系统获取随机性的方法,用于 暂停进程(休眠)并查找当前日期和 时间。

sqlite3_file 对象表示打开的文件。 sqlite3_vfs 的 xOpen 方法在打开文件时构造一个 sqlite3_file 对象。sqlite3_file跟踪 打开文件时的状态。

sqlite3_io_methods对象保存用于交互的方法 使用打开的文件。每个sqlite3_file都包含一个指向 适合于文件的 sqlite3_io_methods 对象 代表。sqlite3_io_methods对象包含要执行的方法 诸如从文件中读取和写入,以截断文件之类的操作, 要刷新对持久性存储的任何更改,请查找 file,以锁定和解锁文件,以及关闭文件并销毁 sqlite3_file对象。

为新 VFS 编写代码涉及构造 sqlite3_vfs对象,然后使用 对 sqlite3_vfs_register() 的调用。VFS 实现还 为 sqlite3_file 和 sqlite3_io_methods 提供子类但 这些对象不会直接注册到 SQLite。相反,sqlite3_file 对象是从 sqlite3_vfs 的 xOpen 方法返回的,并且 sqlite3_file 对象指向实例 sqlite3_io_methods对象。

  • 25
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
以下是一个示例代码,可以将多个数据批量写入SQLite数据库: ``` #include <stdio.h> #include <sqlite3.h> int main() { sqlite3 *db; sqlite3_stmt *stmt; char *errmsg; int rc; // 打开数据库 rc = sqlite3_open("test.db", &db); if (rc != SQLITE_OK) { fprintf(stderr, "无法打开数据库: %s\n", sqlite3_errmsg(db)); sqlite3_close(db); return 1; } // 准备SQL语句 const char *sql = "INSERT INTO user (name, age) VALUES (?, ?)"; rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL); if (rc != SQLITE_OK) { fprintf(stderr, "准备SQL语句失败: %s\n", sqlite3_errmsg(db)); sqlite3_close(db); return 1; } // 批量写入数据 const char *names[] = {"Alice", "Bob", "Charlie"}; int ages[] = {20, 25, 30}; int count = 3; for (int i = 0; i < count; i++) { // 绑定参数 sqlite3_bind_text(stmt, 1, names[i], -1, SQLITE_TRANSIENT); sqlite3_bind_int(stmt, 2, ages[i]); // 执行SQL语句 rc = sqlite3_step(stmt); if (rc != SQLITE_DONE) { fprintf(stderr, "执行SQL语句失败: %s\n", sqlite3_errmsg(db)); sqlite3_finalize(stmt); sqlite3_close(db); return 1; } // 重置语句 sqlite3_reset(stmt); } // 释放资源 sqlite3_finalize(stmt); sqlite3_close(db); printf("数据写入成功!\n"); return 0; } ``` 上面的代码中,我们使用了`sqlite3_prepare_v2`函数来准备SQL语句,使用`sqlite3_bind_text`和`sqlite3_bind_int`函数来绑定参数,然后使用`sqlite3_step`函数来执行SQL语句。 由于我们要写入多条数据,所以我们使用了一个循环来重复执行这些操作。在每次循环中,我们都需要先绑定参数,然后执行SQL语句,最后重置语句,以便下一次绑定参数和执行SQL语句。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

坊垚

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值