驅動程式調度和執行緒

調度工具可讓驅動程式為驅動程式主機中可用的執行緒安排非同步工作。這些由驅動程式執行階段管理的共用執行緒可改善驅動程式代管程序中驅動程式的整體效能和執行緒安全性。強烈建議驅動程式不要產生自己的執行緒。

Fuchsia 的驅動程式架構提供 fdf::Dispatcher,這是建議與 Fuchsia 驅動程式搭配使用的調度工具。此驅動程式庫調度工具支援下列功能:

調度工具作業

調度工具,指派給驅動程式庫和座標,並為驅動程式庫執行非同步作業。驅動程式代管程序中的所有調度工具 (位於相同程序) 都會由驅動程式庫程式執行階段管理的共用執行緒集區支援。調度工具只能在驅動程式庫執行階段環境中運作,例如驅動程式代管程序或驅動程式庫測試架構。

調度工具主要會處理兩項工作:在驅動程式代管程序的執行緒上安排非同步工作 (回應驅動程式庫或驅動程式代管程序建立的回呼),且會代替驅動程式庫 (或「呼叫驅動程式庫」) 在這些執行緒上執行工作。

調度員可能會在下列常見情況中呼叫驅動程式庫:

  • 系統會執行驅動程式庫掛鉤 (請參閱「預設調度工具」)。
  • 系統會針對先前註冊的非同步作業執行回呼。
    • 已張貼的工作已準備就緒。
    • 已收到「等待中」信號。
    • 收到 FIDL 要求或回應。
  • 調度器正在關閉 (請參閱關閉調度工具)。

執行緒模型

驅動程式代管程序中的調度工具可透過不同選項建立自身的執行緒模型:

已同步且未同步

使用 FDF_DISPATCHER_OPTION_SYNCHRONIZED 選項建立,同步的調度工具一律不會向驅動程式庫同時發出呼叫。這可能會視為單一執行緒調度工具,但請注意,系統無法保證調度員每次都會從相同執行緒呼叫驅動程式庫 (請參閱「執行緒本機儲存空間」)。

使用 FDF_DISPATCHER_OPTION_UNSYNCHRONIZED 選項建立時,未同步處理的調度工具可能會向驅動程式庫同時發出呼叫。但是,如果使用這個選項,就不需要保證排序;驅動程式庫會負責所有狀態同步處理。因此,建議您盡可能使用同步處理的調度工具,避免這類複雜度。

執行緒模型

圖 1. 同步處理與未同步處理的調度工具排定的回呼時間軸。

如圖 1 所示,同步的調度工具不會顯示多個平行回呼,而未同步處理的調度工具會顯示多個平行回呼。

同步作業

一般來說,我們不建議您進行同步呼叫,因為驅動程式可能會阻止其他工作執行。不過,如有必要,驅動程式庫可以使用 FDF_DISPATCHER_OPTION_ALLOW_SYNC_CALLS 選項建立調度工具,該選項僅適用於同步處理的調度工具

這個選項會在建立調度工具時,在執行緒集區中產生額外的執行緒。這有助於驅動程式庫執行階段降低驅動程式庫引入的同步呼叫,而會封鎖相同驅動程式庫主機中的其他驅動程式的可能性。

封鎖工作

圖 2:與使用及不使用 FDF_DISPATCHER_OPTION_ALLOW_SYNC_CALLS 選項同步的調度工具排定的回呼時間軸。

在圖 2 中,左側的方塊 (工作 A1 和工作 A2) 代表非封鎖的工作,而右側的方塊 (工作 B1 和工作 B2) 則代表封鎖工作。驅動程式代管程序中的調度工具會共用執行緒 1 和 2,以執行封鎖和非阻塞工作。工作 B1 正在執行同步呼叫,阻止其他在執行緒 1 排定的工作。不過,使用 Thread 1 時,驅動程式庫執行階段可在 Thread 2 上安排非同步工作。

執行緒本機儲存空間

調度工具可透過驅動程式庫執行階段管理的任何共用執行緒呼叫驅動程式庫。然而,驅動程式庫執行階段無法保證每次呼叫都會使用相同的執行緒。因此,驅動程式不應使用任何執行緒本機儲存空間 (例如,在 C++ 中使用 thread_local 關鍵字),因為這類儲存空間會將變數的生命週期綁定到不同的執行緒。

備援保證

調度工具絕對不會向驅動程式庫發出重複呼叫;如果驅動程式庫先前已在同一個呼叫堆疊中收到調度工具的呼叫,系統會將該呼叫視為向後註冊。如果呼叫會重複,調度器會安排在日後的調度工具迴圈疊代進行。

調度工俱生命週期

驅動程式代管程序會管理驅動程式庫的預設調度工具生命週期。驅動程式代管程序保證在驅動程式庫停止前,這個調度工具不會遭到刪除。

預設調度工具

在 DFv2 中,調度工具會在驅動程式的啟動掛鉤中 (也就是驅動程式程式碼中的 Start() 函式) 提供給驅動程式庫。這會成為驅動程式庫的預設調度工具。如要擷取這個調度工具,驅動程式庫可能會在驅動程式庫掛鉤期間呼叫 fdf::Dispatcher::GetCurrent() 函式,例如 Start()PrepareStop()Stop()

在 DFv1 中,驅動程式代管程序會在繫結時為驅動程式庫建立新的調度工具。如要擷取這個調度工具,驅動程式庫可能會在驅動程式掛鉤裝置掛鉤期間呼叫 fdf::Dispatcher::GetCurrent() 函式,例如 Bind()Unbind()Release()

關閉調度工具

在 DFv2 中,驅動程式代管程序會在呼叫驅動程式的 Stop() 掛鉤之前,自動關閉驅動程式庫的所有調度工具。如果驅動程式庫想在關閉前收到通知,驅動程式庫可以實作 PrepareStop() 掛鉤。

在 DFv1 中,驅動程式的預設調度器會在系統呼叫驅動程式主裝置上的 Unbind() 掛鉤後 (也會導致所有子裝置已解除繫結),但在呼叫 Release() 掛鉤之前,驅動程式庫程式的主要裝置則是由驅動程式庫的 Bind() 掛鉤新增的裝置。駕駛人員必須處理其所建立的任何其他調度工具關閉作業。

當調度工具關閉時,調度器會以 ZX_ERR_CANCELED 狀態分派所有待處理的回呼,並呼叫提供給 fdf::Dispatcher::Create() 的關閉處理常式。

建立其他調度工具

驅動程式只能在分派器回呼期間建立其他調度工具 (當調度工具呼叫驅動程式庫時),請參閱「調度工具作業」一節以瞭解這個狀況的發生範例。與預設調度工具不同,驅動程式庫擁有並管理這些額外調度工具的生命週期。在 DFv2 中,驅動程式代管程序會在呼叫驅動程式的 Stop() 掛鉤之前,自動關閉駕駛人的其他調度工具。