分派器可讓驅動程式在驅動程式主機的可用執行緒上排定非同步工作。這些共用執行緒由驅動程式執行階段管理,可提升驅動程式代管程序中驅動程式的整體效能和執行緒安全性。強烈建議驅動程式不要產生自己的執行緒。
Fuchsia 的驅動程式架構提供 fdf::Dispatcher,建議搭配 Fuchsia 驅動程式使用。這項驅動程式庫調度功能支援下列功能:
- 強制執行指定的執行緒模型。
- 強制執行無重入。
- 啟用驅動程式傳輸 FIDL,以便在程序內進行驅動程式庫對驅動程式庫通訊。
- 提供
async程式庫的實作項目,做為啟動非同步作業的介面。
調度員作業
指派給驅動程式庫的調度員會協調及執行驅動程式庫的非同步作業。驅動程式代管程序中的所有調度器 (因此位於相同程序中) 都由驅動程式庫執行階段管理的共用執行緒集區提供支援。調度器只能在驅動程式庫執行階段環境中運作,例如驅動程式代管程序或驅動程式庫測試架構。
調度器主要負責兩項工作:排定要在驅動程式代管程序的執行緒上執行的非同步工作 (回應驅動程式庫或驅動程式代管程序建立的回呼),以及代表驅動程式庫在這些執行緒上實際執行工作 (或「呼叫驅動程式庫」)。
在下列常見情況中,調度員可能會致電給驅動程式庫:
執行緒模型
驅動程式代管程序中的調度器可能會以不同選項建立,以設定自己的執行緒模型:
已同步和未同步
使用 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 上排程。不過,當執行緒 1 正在使用中時,驅動程式庫執行階段可在執行緒 2 上排定非同步工作,
執行緒本機儲存空間
調度員可以從驅動程式庫管理的所有共用執行緒中呼叫驅動程式庫。不過,驅動程式庫執行階段無法保證每次呼叫都會使用相同執行緒。因此,駕駛人不得使用任何執行緒局部儲存空間 (例如在 C++ 中使用 thread_local 關鍵字),因為這類儲存空間會將變數的生命週期繫結至不同的執行緒。
重入性保證
調度器絕不會對驅動程式庫進行重入呼叫,如果驅動程式庫先前在同一個呼叫堆疊中收到調度器的呼叫,該呼叫就會視為重入呼叫。如果呼叫會重新進入,調度器會排定在調度器迴圈的未來疊代中發生。
調度員的生命週期
驅動程式代管程序管理驅動程式庫的預設調度器生命週期。驅動程式主機會保證在驅動程式庫停止前,不會毀損這個調度器。
預設調度器
在 DFv2 中,調度器會提供給驅動程式庫,做為驅動程式啟動掛鉤 (也就是驅動程式程式碼中的 Start() 函式) 的一部分。這會成為驅動程式庫的預設調度員。如要擷取這個調度器,驅動程式庫可以在驅動程式庫掛鉤期間呼叫 fdf::Dispatcher::GetCurrent() 函式,例如 Start()、PrepareStop() 和 Stop()。
在 DFv1 中,驅動程式代管程序會在驅動程式繫結時,為驅動程式庫建立新的調度器。
如要擷取這個調度器,驅動程式庫可能會在驅動程式掛鉤或裝置掛鉤 (例如 Bind()、Unbind() 和 Release()) 期間呼叫 fdf::Dispatcher::GetCurrent() 函式。
關閉調度器
在 DFv2 中,驅動程式代管程序會在呼叫驅動程式的 Stop() 勾點之前,自動關閉驅動程式庫的所有調度器。如果驅動程式庫希望在關機前收到通知,可以實作 PrepareStop() hook。
在 DFv1 中,驅動程式的預設調度器會在呼叫驅動程式主要裝置中的 Unbind() hook 後自動關閉 (這也會導致所有子項裝置取消繫結),但會在呼叫 Release() hook 前關閉。驅動程式的主要裝置是驅動程式庫 Bind() hook 新增的裝置。驅動程式必須處理所建立任何額外調度器的關閉作業。
調度器關閉時,會以 ZX_ERR_CANCELED 狀態調度所有待處理的回呼,並呼叫提供給 fdf::Dispatcher::Create() 的關閉處理常式。
建立其他調度工具
驅動程式只能在調度器回呼期間建立額外的調度器,也就是調度器呼叫驅動程式庫時 (如需發生這種情況的範例,請參閱「調度器作業」)。與預設的調度器不同,驅動程式庫擁有並管理這些額外調度器的生命週期。
在 DFv2 中,驅動程式代管程序會在驅動程式庫關機期間,自動關閉驅動程式的其他調度器。如果是 C++ 驅動程式,這項作業會在 DriverBase::PrepareStop() 之後,但會在 DriverBase::Stop() 之前發生。如果是 Rust 驅動程式,則會在 Driver::stop() 後發生。