小巧、快速、可靠。
選擇其中三項。
SQLite 作業系統介面或「VFS」

1. 簡介

本文描述 SQLite 作業系統可攜性層或「VFS」- 位於 SQLite 實作堆疊底部的模組,提供跨作業系統的可攜性。

2. VFS 與 SQLite 其他部分的關係

SQLite 函式庫的內部組織可以視為右側顯示的模組堆疊。Tokenizer、Parser 和 Code Generator 元件用於處理 SQL 陳述式,並將它們轉換為虛擬機器語言或位元組碼的可執行程式。大致來說,這前三層實作了 sqlite3_prepare_v2()。前三層產生的位元組碼是一個 已準備好的陳述式。虛擬機器模組負責執行 SQL 陳述式位元組碼。B-Tree 模組將資料庫檔案組織成多個具有排序鍵和對數效能的鍵/值儲存庫。Pager 模組負責將資料庫檔案的頁面載入到記憶體中,實作和控制交易,以及建立和維護日誌檔案,以防止當機或電源故障後資料庫損毀。作業系統介面是一個精簡的抽象層,提供一組通用的常式,以調整 SQLite 在不同作業系統上運行。大致來說,底部的四層實作了 sqlite3_step()

本文將探討最底層。

作業系統介面 - 也稱為「VFS」- 讓 SQLite 能夠跨作業系統移植。每當 SQLite 中的任何其他模組需要與作業系統通訊時,它們就會呼叫 VFS 中的方法。然後,VFS 會呼叫滿足請求所需的特定作業系統程式碼。因此,將 SQLite 移植到新的作業系統只需編寫一個新的作業系統介面層或「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 - 所有檔案鎖定操作皆為無效操作 (no-op)。

  3. win32-longpath-none - "win32-longpath" 和 "win32-none" 的組合 ── 支援長路徑名稱,且所有鎖定操作皆為無效操作。

與 Unix 類似,各種 Windows VFS 的大部分程式碼是共用的。

3.3. 指定要使用的 VFS

始終有一個預設的 VFS。在 Unix 系統上,"unix" VFS 為預設值;在 Windows 上則為 "win32"。如果不採取其他動作,新的資料庫連線將使用預設的 VFS。

可以使用 sqlite3_vfs_register() 介面,並將第二個參數設為 1 來註冊或重新註冊 VFS,以更改預設的 VFS。因此,如果一個 (Unix) 行程想要始終使用 "unix-nolock" VFS 來取代 "unix",可以使用以下程式碼:

sqlite3_vfs_register(sqlite3_vfs_find("unix-nolock"), 1);

也可以將替代的 VFS 指定為 sqlite3_open_v2() 函式的第四個參數。例如:

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 Shim

從 SQLite 堆疊上層的角度來看,每個開啟的資料庫檔案都只使用一個 VFS。但在實務上,特定的 VFS 可能只是另一個實際執行工作的 VFS 的薄包裝層。我們稱這樣的包裝層 VFS 為「shim」。

一個簡單的 shim 範例是 "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 會在檔案開啟期間追蹤檔案的狀態。

sqlite3_io_methods 物件包含與已開啟檔案互動的方法。每個 sqlite3_file 都包含一個指向 sqlite3_io_methods 物件的指標,該物件適用於它所代表的檔案。 sqlite3_io_methods 物件包含執行諸如從檔案讀寫、截斷檔案、將任何更改刷新到永久儲存體、查找檔案大小、鎖定和解鎖檔案,以及關閉檔案並銷毀 sqlite3_file 物件等操作的方法。

編寫新的 VFS 程式碼涉及為 sqlite3_vfs 物件建構一個子類別,然後使用 sqlite3_vfs_register() 呼叫來註冊該 VFS 物件。 VFS 的實作也提供了 sqlite3_filesqlite3_io_methods 的子類別,但這些物件不會直接向 SQLite 註冊。相反地,sqlite3_file 物件是由 sqlite3_vfs 的 xOpen 方法返回,並且 sqlite3_file 物件指向 sqlite3_io_methods 物件的實例。

本頁面最後修改時間:世界協調時間 2024-02-22 15:53:45