本文描述 SQLite 作業系統可攜性層或「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」。
標準的 SQLite 原始碼樹包含適用於 Unix 和 Windows 的內建 VFS。可以使用 sqlite3_vfs_register() 介面在啟動時或執行時新增其他 VFS。
可以同時註冊多個 VFS。每個 VFS 都有一個唯一的名稱。同一個程序中的不同 資料庫連線 可以同時使用不同的 VFS。就此而言,如果單個資料庫連線使用 ATTACH 命令開啟了多個資料庫檔案,則每個附加的資料庫可能使用不同的 VFS。
Unix 版本帶有多個內建 VFS。Unix 的預設 VFS 稱為「unix」,在大多數應用程式中使用。其他可能在 Unix 中找到的 VFS(取決於編譯時選項)包括:
unix-dotfile - 使用點檔案鎖定,而不是 POSIX 諮詢鎖定。
unix-excl - 取得並持有資料庫檔案的獨佔鎖定,防止其他程序存取資料庫。同時將 wal-index 保留在堆積中,而不是在共享記憶體中。
unix-none - 所有檔案鎖定操作皆為無操作。
unix-namedsem - 使用具名信號量進行檔案鎖定。僅適用於 VXWorks。
各種 Unix VFS 唯一的差別在於它們處理檔案鎖定的方式 ── 它們大部分的實作是共通的,並且都位於同一個 SQLite 原始碼檔案中:os_unix.c。請注意,除了 "unix" 和 "unix-excl" 之外,各種 Unix VFS 都使用不相容的鎖定實作。如果兩個行程使用不同的 Unix VFS 存取同一個 SQLite 資料庫,它們可能無法看到彼此的鎖定,並可能互相干擾,導致資料庫損毀。"unix-none" VFS 尤其完全不執行鎖定,如果同時被兩個或多個資料庫連線使用,很容易導致資料庫損毀。建議程式設計師只使用 "unix" 或 "unix-excl",除非有不得已的理由。
Windows 版本也內建了多個 VFS。預設的 Windows VFS 稱為 "win32",在大多數應用程式中使用。在 Windows 版本中可能找到的其他 VFS 包括:
win32-longpath - 類似 "win32",差別在於路徑名稱的長度可達 65534 位元組,而 "win32" 的路徑名稱最多為 1040 位元組。
win32-none - 所有檔案鎖定操作皆為無效操作 (no-op)。
win32-longpath-none - "win32-longpath" 和 "win32-none" 的組合 ── 支援長路徑名稱,且所有鎖定操作皆為無效操作。
與 Unix 類似,各種 Windows 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。
從 SQLite 堆疊上層的角度來看,每個開啟的資料庫檔案都只使用一個 VFS。但在實務上,特定的 VFS 可能只是另一個實際執行工作的 VFS 的薄包裝層。我們稱這樣的包裝層 VFS 為「shim」。
一個簡單的 shim 範例是 "vfstrace" VFS。這是一個 VFS(實作於 test_vfstrace.c 原始碼檔案中),它會將與每個 VFS 方法呼叫相關聯的訊息寫入日誌檔,然後將控制權交給另一個 VFS 執行實際工作。
以下是在公開的 SQLite 原始碼樹中可用的其他 VFS 實作:
appendvfs.c - 這個 VFS 允許將 SQLite 資料庫附加到其他檔案的末尾。例如,可以用它將 SQLite 資料庫附加到可執行檔的末尾,以便在執行時輕鬆找到附加的資料庫。如果使用 --append 選項啟動 命令列 shell,它將使用此 VFS;如果在其 .archive 命令中指定 --append 旗標,它也會使用此 VFS。
test_demovfs.c - 這個檔案實作了一個非常簡單的虛擬檔案系統 (VFS),名為「demo」,它使用 POSIX 函式,例如 open()、read()、write()、fsync()、close()、fsync()、sleep()、time() 等等。這個 VFS 只能在 Unix 系統上運作。但它並非用來取代 Unix 平台上預設使用的標準「unix」VFS。「demo」VFS 特意保持非常簡單,以便它可以用作學習輔助工具,或作為構建其他 VFS 或將 SQLite 移植到新作業系統的範本。
test_quota.c - 這個檔案實作了一個名為「quota」的shim,它對數據庫檔案集合強制執行累積檔案大小限制。輔助界面用於定義「配額群組」。配額群組是一組檔案(數據庫檔案、日誌和臨時檔案),它們的名稱都符合 GLOB 模式。系統會追蹤每個配額群組中所有檔案大小的總和,如果該總和超過為配額群組定義的閾值,則會呼叫回呼函式。該回呼函式可以增加閾值,或導致原本會超過配額的操作失敗並出現 SQLITE_FULL 錯誤。這個 shim 的其中一個用途是在 Firefox 中對應用程式數據庫強制執行資源限制。
test_multiplex.c - 這個檔案實作了一個 shim,允許數據庫檔案超過底層檔案系統的最大檔案大小。這個 shim 向 SQLite 的上六層呈現一個界面,使其看起來像是在使用非常大的檔案,而實際上每個這樣的大檔案都被分割成底層系統上的許多較小檔案。例如,這個 shim 已被用於允許數據庫在 FAT16 檔案系統上增長到超過 2 GiB。
test_onefile.c - 這個檔案實作了一個名為「fs」的示範 VFS,展示了如何在缺少檔案系統的嵌入式裝置上使用 SQLite。內容會直接寫入底層媒體。具有有限快閃記憶體的裝置可以使用從此示範程式碼衍生的 VFS,使 SQLite 作為裝置上快閃記憶體的檔案系統。
test_journal.c - 這個檔案實作了一個在 SQLite 測試期間使用的 shim,用於驗證數據庫和回滾日誌以正確的順序寫入,並在適當的時間進行「同步」,以確保數據庫可以從斷電或硬重置中恢復。該 shim 檢查數據庫和回滾日誌操作的幾個不變量,如果違反任何這些不變量,則會引發例外狀況。這些不變量反過來確保數據庫始終可恢復。使用這個 shim 執行大量的測試案例,可以進一步確保 SQLite 數據庫不會因意外斷電或裝置重置而損壞。
test_vfs.c - 這個檔案實作了一個 shim,可用於模擬檔案系統錯誤。這個 shim 在測試期間用於驗證 SQLite 對硬體故障或其他錯誤情況(例如檔案系統空間不足)的合理反應,這些情況在真實系統上難以測試。
在 SQLite 核心原始程式碼庫和可用擴充功能中還有其他 VFS 實作。以上列表並非詳盡無遺,僅代表可以使用 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_file 和 sqlite3_io_methods 的子類別,但這些物件不會直接向 SQLite 註冊。相反地,sqlite3_file 物件是由 sqlite3_vfs 的 xOpen 方法返回,並且 sqlite3_file 物件指向 sqlite3_io_methods 物件的實例。
本頁面最後修改時間:世界協調時間 2024-02-22 15:53:45