名稱
clock - 用於追蹤時間進度的核心物件。
摘要
時鐘是時鐘單調性參考時間軸的一維仿射變換,可由時鐘維護者以原子方式調整,並由用戶端觀察。
說明
屬性
時鐘的屬性會在時鐘建立時建立,之後便無法變更。目前已定義三個時鐘屬性。
ZX_CLOCK_OPT_MONOTONIC
設定後,系統會保證時鐘會呈現單調性行為。也就是說,任何時鐘的觀察序列都保證會產生一連串時間,這些時間一律大於或等於先前的觀察結果。單調時鐘永遠不會倒轉,但可以跳轉。正式:
假設有時鐘 C,讓 C(x) 為從參考時間軸 C's 時間軸對應的函式。C(x) 是一個區塊線性函式,由 C 維護者決定的所有時間內的所有仿射轉換區塊組成。C 是單調的,只有在下列情況下才會發生:
針對所有 R1、R2:R2 >= R1
C(R2) >= C(R1)
ZX_CLOCK_OPT_CONTINUOUS
設定後,時鐘就會保證持續運作。也就是說,時鐘轉換的任何更新都保證與先前的轉換區段保持第一順序連續。正式:
讓 Ci(x) 為 C(x) 的 i 同構轉換片段。假設 Ri 是參考時間軸上的第一個時間點,其中 Ci(x) 已定義。時鐘 C 為連續的條件為:對於所有 i
Ci(Ri + 1) = Ci + 1(Ri + 1)
備用時間
時鐘的截止時間代表時鐘可設定的最小值。由於時鐘只能向前走,而不能倒轉,因此時鐘觀察器不可能收到小於時鐘建立者設定的截止時間的值。
在建立時,您可以透過 zx_create_args_v1_t
結構提供備用時間。否則,預設值為 0。
在時鐘更新作業期間,如果嘗試將時鐘的值設為低於備用時間的值,系統會失敗並顯示 ZX_ERR_INVALID_ARGS。未經初始設定的時鐘一律會回報為時鐘設定的備用時間。備援時間不得低於預設值零。
隱含屬性
- 系統中所有時鐘物件的參考時鐘為單調時鐘。
- 所有時鐘物件的標稱單位都會指定為奈秒。這個屬性無法設定。
- 所有時鐘物件的頻率調整單位皆指定為百萬分之一 (PPM)。
- 時鐘物件的頻率調整最大允許範圍已指定為 [-1000, +1000] PPM。這項屬性無法設定。
其他建立選項
ZX_CLOCK_OPT_AUTO_START
在建立時鐘時使用這個選項,時鐘就會以已啟動狀態開始,而非預設的未啟動狀態。詳情請參閱「啟動時鐘」。
讀取時鐘
在提供時鐘句柄的情況下,使用者可以使用 zx_clock_read()
系統呼叫查詢該時鐘提供的目前時間。時鐘讀取 ZX_RIGHT_READ 權限。時鐘讀取作業保證對所有觀察者來說是一致的。也就是說,如果兩位觀察者在完全相同的參考時間 R 查詢時鐘,他們一律會看到相同的值 C(R)。
參考時間軸、zx_ticks_get()
和 zx_clock_get_monotonic()
如先前所述,zx_clock_get_monotonic() 是所有使用者建立的 zircon 時鐘的參考時間軸。也就是說,如果使用者知道時鐘例項的目前轉換,並指定時鐘例項時間軸上的值,則可計算時鐘單調時間軸上的對應點 (反之亦然)。這也表示,如果未對核心時鐘進行速率調整,時鐘單調性和核心時鐘的滴答速率會完全相同。
除了時鐘單調時程外,Zircon 核心也會透過 zx_ticks_get()
和 zx_ticks_per_second()
公開「時標」時程。在內部,滴答實際上是時鐘單調性參考時間線,並直接從可供核心存取的架構適當計時器單位讀取。時鐘單調性其實是將以納秒為單位的標準化時間刻度線性轉換。兩個時間軸都會在核心啟動時從零開始計時。
由於時鐘單調性是根據刻度轉換的靜態轉換,而所有核心時鐘都是根據時鐘單調性轉換,因此除了時鐘單調性之外,刻度也可做為核心時鐘的參考時鐘。
擷取時鐘的詳細資料
除了讀取時鐘的目前值之外,具備 ZX_RIGHT_READ 權限的進階使用者也可以讀取時鐘,並在過程中使用 zx_clock_get_details()
取得詳細資料。呼叫成功後,傳回給呼叫端的詳細資料結構會包含:
- 目前的時鐘單調到時鐘轉換。
- 目前的時間間隔到時鐘轉換。
- 時鐘目前的對稱誤差範圍預估值 (如有)。
- 根據時鐘單調參考時間軸定義的,上次更新時鐘的時間。
- 在觀察時鐘時,系統記時器的觀察結果。
- 在建立時定義的錶面所有靜態屬性。
- 每次更新時鐘的基礎轉換時,產生值都會變更的產生值。
進階使用者可以利用這些詳細資料,不僅計算時鐘的最近 now
值 (透過使用 Ticks-to-Clock 轉換來轉換已回報的 Ticks-now 觀察值,兩者皆由取得詳細資料作業回報),還可以:
- 瞭解自上次
zx_clock_get_details()
作業 (使用產生隨機值) 以來,時鐘轉換是否已變更。請注意,時鐘產生的 Nonce 不保證會從任何指定值開始,也不保證會在每次更新時以任何特定方式 (例如以固定值遞增) 變更。相反地,每次更新時,產生值會變更為一個值,且該值保證會與更新發生前立即的值不同。 - 將時鐘轉換作業與其他時鐘的轉換作業組合,以便推斷兩個時鐘之間的關係。
- 瞭解時鐘維護者對誤差範圍的最佳估計值。
- 根據上次校正時間、目前轉換作業和時鐘的最大許可校正因數,推論時鐘相對於參考時鐘的可能未來值範圍 (請參閱上方「|隱含屬性」一節所述的頻率調整作業最大許可範圍)。
啟動時鐘和時鐘信號
建立後,時鐘並未立即開始運作。所有嘗試讀取時鐘的動作都會傳回時鐘設定的截止時間,如果在建立時未指定,則預設為 0。
時鐘開始運作後,時鐘維護者會執行第一個更新作業,該作業必須包含設定值作業。時鐘會在該時間點開始運作,其速率等於參考時鐘加上維護者指定的偏差。
時鐘也有 ZX_CLOCK_STARTED 信號,可讓使用者瞭解時鐘實際啟動時間。這個信號一開始不會設為已設定,但在第一次成功更新作業後就會設為已設定。一旦啟動,時鐘就會持續運作,並且會一律提出 ZX_CLOCK_STARTED 信號。
一開始,時鐘是時鐘單調性的複本,因此時鐘單調性時間軸與合成時間軸之間的轉換函式為恆等函式。建立後,這部時鐘仍可能維持,但須遵守權利、ZX_CLOCK_OPT_MONOTONIC 和 ZX_CLOCK_OPT_CONTINUOUS 屬性,以及設定的備用時間。
如果您使用 ZX_CLOCK_OPT_AUTO_START 選項建立時鐘,則無法設定大於目前時鐘單調時間的回溯時間。如果允許這項操作,則會導致時鐘的目前時間設為其截止時間之前的時間。
維護時鐘
擁有時鐘物件 ZX_RIGHT_WRITE 權限的使用者,可以使用 zx_clock_update()
系統呼叫,擔任時鐘的維護者。每次呼叫 zx_clock_update()
時,時鐘的三個參數都可能會調整,但並非每次都需要調整所有參數。這些值如下:
- 時鐘的絕對值。
- 時脈的頻率調整值 (與額定值的偏差,以 ppm 表示)
- 時鐘的絕對誤差範圍估計值 (以奈秒為單位)
在系統呼叫本身期間,時鐘轉換會發生變更。使用者可能無法指定調整的具體參考時間。
對設有 ZX_CLOCK_OPT_MONOTONIC 屬性的時鐘進行任何絕對值變更,如果會導致非單調性行為,就會失敗,並傳回 ZX_ERR_INVALID_ARGS 的回傳碼。
第一個更新作業會啟動計時器,且必須包含設定值作業。
除了最開始的設定值作業之外,所有嘗試以 ZX_CLOCK_OPT_CONTINUOUS 屬性設定的計時器絕對值都會失敗,並傳回 ZX_ERR_INVALID_ARGS 的錯誤碼
時鐘誤差範圍估計值的注意事項
zx_clock_get_details()
系統呼叫可為使用者提供時鐘的多項精細詳細資料,包括「誤差範圍估計值」。這個以奈秒為單位的值,代表時鐘維護人員目前對時鐘與參考資料之間差距的最佳估計值。舉例來說,如果使用者擷取的時間 X
誤差範圍估計值為 E
,則時鐘維護者會嘗試表示,他們認為時鐘的實際值位於 [ X-E, X+E ]
範圍內。
此預估值的信賴水準「不會」由核心 API 指定。有些時鐘維護者可能會使用嚴格的上限,其他人則會使用無法證明但提供「高信心」的上限,而其他人可能對自己的預估值沒有信心,甚至完全沒有信心。
如果使用者需要瞭解所存取錯誤預估值的客觀品質 (例如,要強制執行憑證有效日期或 DRM 授權到期日),則應瞭解系統中哪個元件會維護時鐘,以及維護者在提供錯誤邊界預估值的信心程度時,會提供哪些保證。
可對應的時鐘
除了使用標準的系統呼叫 (zx_clock_read()
和 zx_clock_get_details()
) 讀取時鐘或取得其詳細資料之外,Zircon 核心時鐘還提供另一種方法,可減少觀察時鐘的額外負擔,因為您 (幾乎) 不必進入 Zircon 核心即可觀察時鐘。這裡的主要概念是,時鐘的核心只是將仿射變換套用至所選參考時鐘的目前值。如果轉換狀態可透過共用記憶體由使用者模式存取,且基礎參考時鐘不需要進入 Zircon 核心即可讀取,則合成時鐘也可以觀察,而無須進入核心。輸入「mappable」時鐘。
創作
您可以呼叫 zx_clock_create()
,並在建立時傳遞 ZX_CLOCK_OPT_MAPPABLE
,藉此建立可對應的核心時鐘。新建立的時鐘句柄將包含 ZX_RIGHT_MAP
權限,以及所有其他預設權限。此外,透過 get-details 作業回報的選項欄位會包含 ZX_CLOCK_OPT_MAPPABLE
標記,反映出這是可對應的錶面。
對應
如要使用共用記憶體觀察時鐘的狀態,必須先執行兩項操作。
1) 必須決定時鐘狀態的對應大小。2) 時鐘的共用狀態必須對應至使用者的位址空間。
如要擷取時鐘對應的大小,使用者必須使用 ZX_INFO_CLOCK_MAPPED_SIZE
主題呼叫 zx_object_get_info()
,並傳遞 uint64_t
大小的緩衝區,以便接收時鐘的對應大小。
一旦知道大小,使用者就可以呼叫 zx_vmar_map_clock()
,實際將時鐘的狀態對應至其位址空間。這個呼叫的運作方式與 zx_vmap_map_vmo()
非常相似,只是內建了一些額外的限制。特別注意:
1) 僅允許 ZX_VM_PERM_READ
權限,所有其他 ZX_VM_PERM
標記都明確禁止,無論使用者擁有的句柄權限為何。對應時鐘資料一律必須為唯讀。2) 傳遞至 syscall 的 len
參數「必須」與從 get-info 呼叫擷取的值相符。3) 對應時鐘狀態時,無法指定 vmo_offset
。必須一律對應整個時鐘狀態區域。
對時鐘進行對應時,必須同時具備時鐘句柄的 ZX_RIGHT_READ
和 ZX_RIGHT_MAP
權限。
成功的對應作業會傳回時鐘物件狀態在指定 VMAR 中對應的虛擬位址。
取消對應
取消對時脈的對應程序,與取消對 VMO 或 I/O 緩衝區的對應程序相同。使用者只要在用來對應時鐘的 VMAR 上呼叫 zx_vmar_unmap()
,即可傳遞從原始 get-info 呼叫收到的長度,以及從 zx_clock_map()
呼叫收到的虛擬位址。
使用者可能會在對時鐘建立對應後關閉時鐘的句柄,但仍可讀取時鐘的狀態。就像關閉 VMO 句柄不會破壞該 VMO 的任何對應項目一樣,關閉時鐘句柄也不會破壞任何對應項目。請參閱 zx_vmar_unmap()
和 zx_vmar_destroy()
。
觀察已對應的時鐘
如要觀察已對應的時鐘,使用者可以呼叫 zx_clock_read_mapped()
或 zx_clock_get_details_mapped()
,並將已對應時鐘狀態的虛擬位址做為第一個參數傳遞。其他所有設定都維持不變,詳情請參閱 zx_clock_read()
和 zx_clock_get_details()
的說明文件。
下列任何動作都會導致未定義的行為:
- 嘗試使用任何虛擬位址 (除了原始對
zx_clock_map()
的呼叫傳回的位址) 呼叫對應的系統呼叫觀察時鐘。 - 嘗試使用部分或完全未對應的時鐘狀態,呼叫已對應的時鐘觀察系統呼叫。
此外,使用者不得嘗試使用對應的時鐘狀態,推斷時鐘本身的詳細資料。時鐘狀態的格式未指定,且可能隨時變更。唯一合法的時鐘對應狀態使用方式,是搭配呼叫 zx_clock_read_mapped()
or
zx_clock_get_details_mapped()`。
使用提供的系統呼叫進行適當存取時,時鐘觀察值會保持「多觀察者」一致性,也就是說,如果這些觀察值有固定順序,則多個觀察者所做的一組觀察值會保持一致性,例如這些觀察值有固定順序。詳情請參閱這裡。
zx_info_maps_t
和 zx_info_vmos_t
結構體中的時鐘對應表示法
Zircon 目前提供兩個診斷 zx_object_get_info()
主題,用於擷取目前有效對應項目的資訊,分別是 ZX_INFO_VMAR_MAPS
和 ZX_INFO_PROCESS_MAPS
,兩者都會傳回 zx_info_maps_t
結構體陣列,描述指定 VMAR 或程序中目前有效的對應項目。
時鐘對應項目的列舉方式與 VMO 對應項目相同,但有以下差異。
1) 由於時鐘目前不是可命名物件,因此系統會將字串「kernel-clock」傳回做為對應的名稱,而非通常會傳回的 VMO 目前名稱。2) 對應項目的 KOID 欄位會使用時鐘物件的 KOID 填入。
此外,程式可以使用 ZX_INFO_PROCESS_VMOS
主題,擷取 zx_info_vmos_t
結構體陣列,取得與程序相關聯的目前 VMOs 相關資訊。如果程序將時鐘對應至其位址空間,時鐘也會顯示在這項列舉中。就像 zx_info_maps_t
案例一樣,回報的 KOID 會是時鐘物件的 KOID,而回報的名稱會是「kernel-clock」。此外,項目的 flags
欄位會設定 ZX_INFO_VMO_VIA_MAPPING
位元,指出物件因有效對應而列於清單中。
範例
以下是簡易範例,說明如何對應及讀取時鐘。
class MappedClockReader {
public:
MappedClockReader(const zx::clock& clock) {
zx_status_t status = clock.get_info(ZX_INFO_CLOCK_MAPPED_SIZE, &mapped_clock_size_,
sizeof(mapped_clock_size_), nullptr, nullptr));
ZX_ASSERT(status == ZX_OK);
status = zx::vmar::root_self()->map_clock(ZX_VM_PERM_READ | ZX_VM_MAP_RANGE, 0, clock,
mapped_clock_size_, &clock_addr_);
ZX_ASSERT(status == ZX_OK);
}
~MappedClockReader() {
if (clock_addr_ != 0) {
zx::vmar::root_self()->unmap(clock_addr_, mapped_clock_size_);
}
}
zx_time_t Read() {
zx_time_t result{0};
const zx_status_t status = zx::clock::read_mapped(clock_addr_, &result);
ZX_ASSERT(status == ZX_OK);
return result;
}
private:
zx_vaddr_t clock_addr_{0};
uint64_t mapped_clock_size_{0};
}
參考資料
- 時鐘轉換
zx_clock_create()
- 建立時鐘zx_clock_read()
- 讀取時鐘的時間zx_clock_get_details()
- 擷取時鐘與參照資料之間的關係詳細資料zx_clock_update()
- 調整時鐘與參照時鐘之間的目前關係- mappable clocks RFC
zx_clock_read_mapped()
- 讀取已對應時鐘的時間zx_clock_get_details_mapped()
- 擷取已對應時鐘與參照時鐘之間的關係詳細資料zx_clock_map()
- 將時鐘的狀態對應至指定的 VMARzx_vmar_destroy()
- 摧毀 VMAR 及其內的所有對應項目。zx_vmar_map()
:將 VMO 的全部或部分對應至指定的 VMARzx_vmar_unmap()
- 在 VMAR 中取消對應目前有效的對應- [
ZX_INFO_CLOCK_MAPPED_SIZE
]: (/reference/syscalls/object_get_info#zx_info_clock_mapped_size) ZX_INFO_VMAR_MAPS
:擷取 VMAR 中目前有效的 mappin 相關資訊。ZX_INFO_PROCESS_MAPS
- 擷取程序中目前有效的 mappins 相關資訊。