2017年2月21日 星期二

Bug Check 0xE4: WORKER_INVALID

前言:
在驅動開發中常會使用到系統執行緒(System thread)來處理一些異步工作,例如一個虛擬串列阜驅動,對上接收應用程式(SCADA)的讀寫控制請求來讀取PLC下的感測器的數據。驅動可能會在短時間內收到大量的命令(non blocking write),然後將命令依序傳給底下的裝置。這時候驅動通常會把這些命令依序放入佇列中,然後交給另一個執行緒來處理這些動作。這個執行緒在核心驅動中有兩種寫法,一種是透過調用PsCreateSystemThread(用於長週期)來建立自己的工作執行緒,另一種則是透過建立WorkItem(用於短周期)來讓系統執行緒調用。

問題:
我在設計驅動架構中有一層就是實作一個TDI client來負責發送網路封包給遠端裝置,這會透過插入WorkItem的方式來處理這些命令。後面試跑了一下,常常會隨機發生BSOD。

分析:

透過下圖可以知道問題出在哪裡,一個配置的workitem已經在佇列中。表示說同一個workitem我插入了兩次。檢查程式碼,發現我配置的Routine,因為處理網路封包的時候與上層的命令是非同步的,所以在共用workitem的情況下,IoQueueWorkItem會被呼叫了兩次。藍屏之所以會隨機發生的原因就是因為有時候Routine處理的速度比插入workitem的時間還要慢。後來我在接收命令的dispatch中透過IoAllocateWorkItem來配置新的workitem,然後在Routine結束前透過IoFreeWorkItem來釋放,問題就可以解決。


補充:
1. 驅動會讓一個WorkItem關聯一個WorkItem回呼例程(routine) ,當系統工作線程處理一個work item時,他會調用關聯的WorkItem例程。

2. 系統工作線程會先移除在佇列中的workitem,然後在調用workitem關聯的回呼例程,所以在回呼例程中釋放workitem是安全的。

3.workitem關聯的routine運行在系統執行緒的上下文(context)。因為系統執行緒的pool是有限資源(推測是紀錄有關插入佇列中的workitem資源),所以執行rountine的時間要短,不然會造成系統死鎖。

參考:
System worker thread

沒有留言:

張貼留言