檢查 VMO 檔案格式

本文件將說明元件檢查檔案格式 (檢查格式)。

採用「檢查格式」設定的檔案稱為「檢查檔案」。 這類檔案的副檔名通常為 .inspect

關於如何變更格式的資訊。請參閱 擴充檢查檔案格式

總覽

元件檢查提供各種元件, 並在執行階段公開各個狀態的結構化資訊。

元件會使用「檢查」功能代管對應的虛擬記憶體物件 (VMO) 請採用此格式,公開包含此內部狀態的檢查階層

檢查階層由包含類型 Properties 的巢狀 Nodes 組成。

目標

本文件所述的檢查格式目標如下:

  • 對資料的低負擔變動

    檢查檔案格式可就地變更資料。舉例來說: 整數遞增的負擔為約 2 個原子遞增量。

  • 支援非靜態階層

    儲存在檢查檔案中的階層可在以下位置修改: 執行階段。您可以在階層架構中新增或移除子項 讓應用程式從可以最快做出回應的位置 回應使用者要求透過這種方式,階層可以密切反映 新增多個物件

  • 單一寫入者、多個讀取器並行,沒有明確同步

    與寫入者同時作業的讀取器對應 VMO,並嘗試 拍攝資料的快照寫作者表示自己位於重要部分 但不需要明確同步處理的產生計數器 才能獲得更棒的參與讀取器會使用生成計數器, VMO 的快照一致,且可在安全的情況下讀取

  • 元件終止後,資料可能還會繼續提供

    讀取器可能維持包含檢查資料的 VMO 控制代碼 停止。

術語

本節定義本文件中使用的常見術語。

  • 檢查檔案 - 使用本文件所描述格式的限定位元組序列。
  • 檢查 VMO - 儲存在虛擬記憶體物件 (VMO) 中的檢查檔案。
  • 封鎖 - 檢查檔案的大小部分。區塊會有索引和訂單。
  • 索引 - 特定區塊的專屬識別碼。byte_offset = index * 16
  • 順序 - 給定的區塊大小,與下限之間進行位元偏移 大小size_in_bytes = 16 << order。將區塊分為 類別。
  • 節點 - 階層中的具名值,其他值可能包含 多層次是巢狀結構只有節點可以是階層中的父項。
  • 屬性 - 包含輸入資料的已命名值 (例如「字串」、「 整數)。
  • 階層 - 節點的樹狀結構,從單一「根」依遞減順序排列節點, 都含有 Properties 物件檢查檔案內含 單一階層

本文件使用 RFC 2119 中定義的「必須」、「應/建議」和「可能」關鍵字

所有位元欄位的圖表皆按小端排列順序儲存。

版本

目前版本:2

阻攻次數

檢查檔案會分割成多個 Blocks,大小必須為 的 2 次方。

區塊大小下限必須為 16 個位元組 (MIN_BLOCK_SIZE),且 區塊大小上限須為 16 位元組的倍數。實作者是 建議指定小於網頁大小的區塊大小上限 (通常是 4096 個位元組)。在我們的參考實作中 區塊大小為 2048 個位元組 (MAX_BLOCK_SIZE)。

所有區塊都必須對齊 16 位元組的邊界,且不得在 VMO 會依據索引計算,並指定 16 位元組偏移 (offset = index * 16)。

我們使用 24 位元建立索引,因此檢查檔案的大小最多為 256 MiB。

block_header 包含 16 個位元組,如下所示:

.---------------------------------------------------------------.
|         |1|1|1|1|1|2|2|2|2|2|3|3|3|3|3|4|4|4|4|4|5|5|5|5|5|6|6|
|0|2|4|6|8|0|2|4|6|8|0|2|4|6|8|0|2|4|6|8|0|2|4|6|8|0|2|4|6|8|0|2|
|---+---+-------+-----------------------------------------------|
| O | R | Type  |                                               |
|---------------------------------------------------------------|
|                                                               |
'---------------------------------------------------------------'

O = Order
R = Reserved, must be 0
The rest (left blank) depends on the payload

每個區塊都有 order,並指定其大小。

如果區塊大小上限為 2048 個位元組,則會有 8 個可能的區塊 訂單 (NUM_ORDERS),編號 0...7,與大小區塊對應 16、32、64、128、256、512、1024 和 2048 個位元組。

每個區塊也都有類型,可用於決定 系統會解讀區塊中的位元組。

夥伴分配

這種區塊版面配置允許使用夥伴版面配置,有效率地分配區塊 分配。建議使用 Buddy 分配 但這不是使用檢查格式的必要條件

類型

所有支援的類型都已定義於 //zircon/system/ulib/inspect/include/lib/inspect/cpp/vmo/block.h 可分為以下類別:

列舉 value 類型名稱 類別
kFree 0 FREE 內部
kReserved 1 RESERVED 內部
kHeader 2 HEADER 標頭
kNodeValue 3 NODE_VALUE
kIntValue 4 INT_VALUE
kUintValue 5 UINT_VALUE
kDoubleValue 6 DOUBLE_VALUE
kBufferValue 7 BUFFER_VALUE
kExtent 8 EXTENT 範圍
kName 9 NAME 名稱
kTombstone 10 TOMBSTONE
kArrayValue 11 ARRAY_VALUE
kLinkValue 12 LINK_VALUE
kBoolValue 13 BOOL_VALUE
kStringReference 14 STRING_REFERENCE 參考資料
  • 內部 - 這些類型是用來導入封鎖分配功能。 讀者必須略過這些內容

  • 標頭:這個類型可讓讀者偵測「檢查檔案」和原因 像是快照一致性這個區塊必須存在於索引 0。

  • - 這些類型會直接顯示在階層中。值必須具有「Name」(名稱) 以及父項 (必須是 NODE_VALUE)。

  • Extent:這個類型會儲存可能無法在單一區塊中的長二進位資料。

  • 名稱 - 這個類型會儲存單一區塊內的二進位資料。 通常用於儲存值的名稱

  • 參照:這個類型會保留其他區塊可參照的單一標準值。

每種型別會以不同方式解讀酬載,如下所示:

免費

.---------------------------------------------------------------.
|         |1|1|1|1|1|2|2|2|2|2|3|3|3|3|3|4|4|4|4|4|5|5|5|5|5|6|6|
|0|2|4|6|8|0|2|4|6|8|0|2|4|6|8|0|2|4|6|8|0|2|4|6|8|0|2|4|6|8|0|2|
|---+---+-------+-----------------------+-----------------------|
| O | R | Type  | Next free block       |                       |
|---------------------------------------------------------------|
|                                                               |
'---------------------------------------------------------------'

O = Order
R = Reserved, must be 0
Type = 0
Next free block = index (optional)

可供分配的 FREE 區塊。值得注意的是 區塊 (16 位元組 \0) 會解讀為順序 0 的 FREE 區塊。 因此緩衝區只需歸零,即可釋放所有區塊。

寫入器實作時可能會使用 FREE 8..63 中未使用的位元 並基於任何目的加以封鎖寫入器實作時,必須設定所有其他未使用的項目 介於 0 至 0 之間。

建議撰寫者使用上述指定位置來儲存 相同訂單下一個免費區塊的索引。只要使用這個欄位 免費區塊可建立相互連結且各大小的免費區塊清單 以便快速分配當 NextFreeBlock 時可到達清單結尾 指向的地點不是「FREE」或非相同訂單 (通常是索引 0 的「標頭」區塊)。

已預約

.---------------------------------------------------------------.
|         |1|1|1|1|1|2|2|2|2|2|3|3|3|3|3|4|4|4|4|4|5|5|5|5|5|6|6|
|0|2|4|6|8|0|2|4|6|8|0|2|4|6|8|0|2|4|6|8|0|2|4|6|8|0|2|4|6|8|0|2|
|---+---+-------+-----------------------------------------------|
| O | R | Type  |                                               |
|---------------------------------------------------------------|
|                                                               |
'---------------------------------------------------------------'

O = Order
R = Reserved, must be 0
Type = 1

RESERVED 模塊可以輕鬆變更為不同的 類型。這是一種選擇的轉場狀態,也就是 封鎖及設定其型別,這有助於檢查 (確保即將使用的區塊) 未視為免費)。

.---------------------------------------------------------------.
|         |1|1|1|1|1|2|2|2|2|2|3|3|3|3|3|4|4|4|4|4|5|5|5|5|5|6|6|
|0|2|4|6|8|0|2|4|6|8|0|2|4|6|8|0|2|4|6|8|0|2|4|6|8|0|2|4|6|8|0|2|
|---+---+-------+---------------+-------------------------------|
| O | R | Type  | Version       | Magic number                  |
|---------------------------------------------------------------|
| Generation count                                              |
|-------------------------------+-------------------------------|
| Size in bytes                 | Unused                        |
|---------------------------------------------------------------|
|                                                               |
'---------------------------------------------------------------'

O = Order
R = Reserved, must be 0
Type = 2
Version = 2
Magic number = "INSP"

檔案開頭必須有一個 HEADER 區塊,流程 魔術數值 (以下簡稱「INSP」) 的「版本」 (目前為 2), 並行控制和 VMO 部分大小的產生次數 以位元組為單位分配資料標頭的第一個位元組必須是有效 ASCII 字元。

請參閱下一節,瞭解並行控制必須如何 以產生計算結果

NODE_VALUE 和 TOMBSTONE

.---------------------------------------------------------------.
|         |1|1|1|1|1|2|2|2|2|2|3|3|3|3|3|4|4|4|4|4|5|5|5|5|5|6|6|
|0|2|4|6|8|0|2|4|6|8|0|2|4|6|8|0|2|4|6|8|0|2|4|6|8|0|2|4|6|8|0|2|
|---+---+-------+-----------------------+-----------------------|
| O | R | Type  | Parent index          | Name index            |
|---------------------------------------------------------------|
| Reference count (optional)                                    |
'---------------------------------------------------------------'

O = Order
R = Reserved, must be 0
Type = {3,10}

節點是進一步巢狀的錨點,以及 ParentID 欄位 的值只能參照 NODE_VALUE 類型的區塊。

NODE_VALUE 區塊支援選用的參照計數爆炸式 允許有效率的實作方式,如下所示:

Refcount 欄位可能包含參照指定的值的數量 NODE_VALUE做為其家長。刪除時,NODE_VALUE 會成為新的 特殊類型 TOMBSTONE只有當TOMBSTONE的設定檔出現時,系統才會刪除 Refcount 為 0。

這可讓不需要明確地編寫程式碼的編寫工具進行實作 持續追蹤節點的子項,並防止下列情況發生:

// "b" has a parent "a"
Index | Value
0     | HEADER
1     | NODE "a", parent 0
2     | NODE "b", parent 1

/* delete "a", allocate "c" as a child of "b" which reuses index 1 */

// "b"'s parent is now suddenly "c", introducing a cycle!
Index | Value
0     | HEADER
1     | NODE "c", parent 2
2     | NODE "b", parent 1

{INT,UINT,DOUBLE,BOOL}_VALUE

.---------------------------------------------------------------.
|         |1|1|1|1|1|2|2|2|2|2|3|3|3|3|3|4|4|4|4|4|5|5|5|5|5|6|6|
|0|2|4|6|8|0|2|4|6|8|0|2|4|6|8|0|2|4|6|8|0|2|4|6|8|0|2|4|6|8|0|2|
|---+---+-------+-----------------------+-----------------------|
| O | R | Type  | Parent index          | Name index            |
|---------------------------------------------------------------|
| Inlined numeric value                                         |
'---------------------------------------------------------------'

O = Order
R = Reserved, must be 0
Type = {4,5,6,13}

數字 VALUE 區塊全都包含 64 位元數值類型,內嵌在 第二個 8 個位元組的區塊

BUFFER_VALUE

.---------------------------------------------------------------.
|         |1|1|1|1|1|2|2|2|2|2|3|3|3|3|3|4|4|4|4|4|5|5|5|5|5|6|6|
|0|2|4|6|8|0|2|4|6|8|0|2|4|6|8|0|2|4|6|8|0|2|4|6|8|0|2|4|6|8|0|2|
|---+---+-------+-----------------------+-----------------------|
| O | R | Type  | Parent index          | Name index            |
|---------------------------------------------------------------|
| Total length                  | Extent index              | F |
'---------------------------------------------------------------'

O = Order
R = Reserved, must be 0
Type = 7
Total length = size of the buffer
Extent index = index of the first extent containing bytes for the buffer
F = Display format {0,1}

一般 BUFFER_VALUE 區塊會參照 一或多個已連結的 EXTENT 區塊

BUFFER_VALUE 區塊包含第一個保存 EXTENT 區塊的索引 二進位資料,且包含資料總長度 (以位元組為單位) 和規模

格式旗標會指定應如何解譯位元組資料, 如下所示:

Enum 意義
kUtf8 0 位元組資料可能會被解讀為 UTF-8 字串。
kBinary 1 位元組資料是任意二進位資料,可能無法列印。

興奮

.---------------------------------------------------------------.
|         |1|1|1|1|1|2|2|2|2|2|3|3|3|3|3|4|4|4|4|4|5|5|5|5|5|6|6|
|0|2|4|6|8|0|2|4|6|8|0|2|4|6|8|0|2|4|6|8|0|2|4|6|8|0|2|4|6|8|0|2|
|---+---+-------+-----------------------+-----------------------|
| O | R | Type  | Next extent index     | R                     |
|---------------------------------------------------------------|
| Payload                                                       |
'---------------------------------------------------------------'

O = Order
R = Reserved, must be 0
Type = 8
Next extent index = index of next extent in the chain
Extent index = index of the extent containing bytes for the string
Payload = byte data payload up to at most the end of the block. Size
          depends on the order

EXTENT 區塊包含任意位元組資料酬載和 鏈結中的下一個 EXTENT。擷取 buffer_value 的位元組資料 讀取每個 EXTENT,直到讀取「Total Length」位元組為止。

名稱

.---------------------------------------------------------------.
|         |1|1|1|1|1|2|2|2|2|2|3|3|3|3|3|4|4|4|4|4|5|5|5|5|5|6|6|
|0|2|4|6|8|0|2|4|6|8|0|2|4|6|8|0|2|4|6|8|0|2|4|6|8|0|2|4|6|8|0|2|
|---+---+-------+-----------------------------------------------|
| O | R | Type  | Length    | Reserved                          |
|---------------------------------------------------------------|
| Payload                                                       |
'---------------------------------------------------------------'

O = Order
R = Reserved, must be 0
Type = 9
Payload = contents of the name. Size depends on the order

NAME 區塊為物件和值提供使用者可理解的 ID。他們 包含完全符合指定區塊的 UTF-8 酬載。

STRING_REFERENCE

.---------------------------------------------------------------.
|         |1|1|1|1|1|2|2|2|2|2|3|3|3|3|3|4|4|4|4|4|5|5|5|5|5|6|6|
|0|2|4|6|8|0|2|4|6|8|0|2|4|6|8|0|2|4|6|8|0|2|4|6|8|0|2|4|6|8|0|2|
|---+---+-------+-----------------------+-----------------------|
| O | R | Type  | Next Extent Index     | Reference Count       |
|---------------------------------------------------------------|
| Total length                  | Payload                       |
'---------------------------------------------------------------'

O = Order
R = Reserved
Type = 14
Next Extent Index = index of the first overflow EXTENT, or 0 if Payload does not overflow
Reference Count = number of references to this STRING_REFERENCE
Total length = size of the Payload in bytes. Payload overflows into Next Extent if
               Total length > ((16 << Order) - 12)
Payload = the canonical instance of a string. The size of the Payload field depends on the
          Order. If the size of the Payload + 12 is greater than 16 << Order, then the Payload
          is too large to fit in one block and will overflow into Next Extent

STRING_REFERENCE 區塊可用來在 VMO 中實作具有參考語意的字串。 這些是連結 EXTENT 清單的開頭,表示值不受大小限制。 在預期 NAME 的範圍內可使用 STRING_REFERENCE 區塊。

ARRAY_VALUE

.---------------------------------------------------------------.
|         |1|1|1|1|1|2|2|2|2|2|3|3|3|3|3|4|4|4|4|4|5|5|5|5|5|6|6|
|0|2|4|6|8|0|2|4|6|8|0|2|4|6|8|0|2|4|6|8|0|2|4|6|8|0|2|4|6|8|0|2|
|---+---+-------+-----------------------+-----------------------|
| O | R | Type  | Parent index          | Name index            |
|---------------------------------------------------------------|
| T | F | Count | Reserved                                      |
|---------------------------------------------------------------|
| Payload                                                       |
'---------------------------------------------------------------'

O = Order
R = Reserved, must be 0
Type = 11
T = Type of the stored values {4,5,6,14}
F = Display format {0,1,2}
Count = Count of stored values
Payload = array of size |count|

ARRAY_VALUE 區塊 Payload 的格式取決於儲存的值類型 T。 與「Type」欄位完全相同。其中 T ∊ {4, 5, 6}Payload 應為 64 位元 數值。其中 T ∊ {14}Payload 應由以下何者組成: 32 位元值,代表 T 類型區塊的 24 位元索引,與位元組一起封裝 界定範圍在這種情況下,只允許使用扁平陣列的 F = 0

F = 0 時,ARRAY_VALUE 應預設為例項化。如果是數字化,這個值應為 相關的零值。在字串中,這應為空字串,以特殊 值 0

表示指定的儲存值類型 (或這些項目的索引) 的「數量」項目精確出現在 並將偏移量 16 的位元組傳入區塊中

「顯示格式」欄位的用途是影響陣列的編寫方式 ,解讀結果如下:

Enum 說明
kFlat 0 顯示為已排序的無格式陣列。
kLinearHistogram 1 將前兩個項目解讀為線性直方圖的 floorstep_size 參數,如下所示。
kExponentialHistogram 2 將前三個項目解讀為 floorinitial_stepstep_multiplier 的指數直方圖,定義如下。

線性直方圖

陣列是線性直方圖,會以內嵌方式儲存參數 同時包含溢位和反向溢位值區

前兩個元素分別是 floorstep_size 參數 (定義列於下方)。

值區數量 (N) 默示為 Count - 4

其餘元素為值區:

2:     (-inf, floor),
3:     [floor, floor+step_size),
i+3:   [floor + step_size*i, floor + step_size*(i+1)),
...
N+3:   [floor+step_size*N, +inf)

指數直方圖

陣列是指數直方圖,可將參數內嵌於內嵌位置 且包含溢位和反向溢位值區

前三個元素是 floorinitial_stepstep_multiplier (定義如下)。

值區數量 (N) 隱含計數 - 5。

其餘則是值區:

3:     (-inf, floor),
4:     [floor, floor+initial_step),
i+4:   [floor + initial_step * step_multiplier^i, floor + initial_step * step_multiplier^(i+1))
N+4:   [floor + initial_step * step_multiplier^N, +inf)
.---------------------------------------------------------------.
|         |1|1|1|1|1|2|2|2|2|2|3|3|3|3|3|4|4|4|4|4|5|5|5|5|5|6|6|
|0|2|4|6|8|0|2|4|6|8|0|2|4|6|8|0|2|4|6|8|0|2|4|6|8|0|2|4|6|8|0|2|
|---+---+-------+-----------------------------------------------|
| O | R | Type  | Parent index          | Name index            |
|---------------------------------------------------------------|
| Content index     |                                       | F |
'---------------------------------------------------------------'

O = Order
R = Reserved, must be 0
Type = 12
Parent index = Index of the parent block
Name index = Index of the name of this value
Content index = Index of the content of this link (as a NAME node)
F = Disposition flags {0,1}

LINK_VALUE 區塊可讓節點支援目前存在的子項 。

內容索引指定另一個含有內容的 NAME 區塊 是參照另一個檢查檔案的專屬 ID。讀者 預期會取得 (Identifier, File) 組合組合 (透過 讀取目錄或其他介面),並且可能會嘗試追蹤 使用儲存的 ID 將樹狀圖拼湊起來,進而產生連結。

Disposition Flags 會指示讀者如何拼接樹木,如下所示:

Enum 說明
kChildDisposition 0 儲存在連結檔案中的階層應為 LINK_VALUE 父項的子項。
kInlineDisposition 1 儲存在連結檔案中的根層級子項和屬性應新增至 LINK_VALUE 的父項。

例如:

// root.inspect
root:
  int_value = 10
  child = LINK("other.inspect")

// other.inspect
root:
  test = "Hello World"
  next:
    value = 0


// kChildDisposition produces:
root:
  int_value = 10
  child:
    test = "Hello World"
    next:
      value = 0

// kInlineDisposition produces:
root:
  int_value = 10
  test = "Hello World"
  next:
    value = 0

如果節點和值之間發生子項名稱衝突 所新增的內嵌子系連結,而優先順序是由讀取器定義。大多數 但會將連結的值 因此可能會覆寫原始值。

並行控制

寫入者必須使用全域版本計數器,讓讀者能夠 不使用讀取或檢查之間的傳輸修改和修改作業 與作者溝通這項功能支援單一寫入者多重讀取器 並行。

本策略適用於編寫者增加全域產生計數器 讀取及結束寫入作業

這是一項簡單的策略,有很大的好處 寫入寫入者起始和結束的版本號碼 緩衝區上任意數量的作業,皆不考慮 資料更新。

主要缺點是讀取作業可能會因為 讀者經常更新作者,但讀者可以因應緩解措施 實際運作情形

讀者演算法

讀取器會使用以下演算法來取得符合自身情況的 檢查 VMO:

  1. 持續鎖定直到版本號碼為偶數 (無並行寫入) 為止。
  2. 複製整個 VMO 緩衝區,並且
  3. 檢查緩衝區的版本號碼等於版本 步驟 1 中的數字。

只要版本號碼相符,用戶端就能讀取 以建構共用狀態 如果版本號碼不相符,用戶端可能會重試整個 上傳資料集之後,您可以運用 AutoML 自動完成部分資料準備工作

寫入器鎖定演算法

寫入者會按照下列步驟鎖定檢查 VMO 以進行修改:

atomically_increment(generation_counter, acquire_ordering);

這項作業會將產生值設為 奇數。取得順序可確保載入結果不會在之前重新排序 這項變更。

寫入者解鎖演算法

在修改後,寫入器可解鎖檢查 VMO 包括:

atomically_increment(generation_counter, release_ordering);

將產生版本設為 新的偶數。發布順序可確保檔案寫入作業處於 在產生計數更新之前,就能看到這個狀態。