2010-11-18 33 views
6

我是一個嵌入式程序員,嘗試使用Visual Studio 2010和MingW(作爲兩個獨立的構建環境)在Win32環境中模擬實時搶先調度程序。我在Win32調度環境中非常綠色,並且正在嘗試做的事情碰到了一堵磚牆。我不是試圖實現實時行爲 - 只是爲了讓仿真任務按照與實際目標硬件上相同的順序和順序運行。強制Win32線程調度到基於優先級的定義序列

被模擬的實時調度器有一個簡單的目標 - 總是執行能夠運行的最高優先級任務(線程)。只要任務能夠運行,它就必須搶佔當前運行的任務,如果它的優先級高於當前運行的任務。一個任務可以由於它正在等待的外部事件或超時/阻塞時間/休眠時間到期而變得能夠運行 - 具有生成時基的刻度中斷。

除了這種先發制人的行爲,任務可以產生或自願放棄其時間片,因爲正在執行睡眠或等待類型功能。

我通過爲模擬的實時調度程序創建的每個任務創建低優先級的Win32線程來模擬此線程(該線程有效地執行調度程序在真實嵌入式目標上切換的上下文),中等優先級Win32線程作爲僞中斷處理程序(處理模擬滴答中斷和產生請求,通過Win32事件對象向它發送信號),以及更高優先級的Win32線程來模擬產生滴答中斷的外設。

當僞中斷處理程序確定應該發生任務切換時,它使用SuspendThread()掛起當前正在執行的線程,並使用ResumeThread()恢復執行新選擇任務的線程。在可能創建的許多任務及其關聯的Win32線程中,只有一個管理任務的線程在任何時候都不會處於暫停狀態。

暫停的線程立即掛起SuspendThread()被調用並且一旦事件告訴它中斷掛起的時候執行僞中斷處理線程就會被執行,這一點很重要 - 但這不是行爲I我正在看。

作爲一個例子問題,我已經有了一個解決方法:當一個任務/線程產生yield事件鎖存在一個變量中,並且中斷處理線程被標記爲有需要的僞中斷(yield)處理。現在在我用於編程的實時系統中,我希望中斷處理線程立即執行,因爲它具有比發送信號的線程更高的優先級。我在Win32環境中看到的情況是,信號較高優先級線程的線程在掛起之前會持續一段時間 - 或者是因爲在信號較高的優先級線程開始執行之前需要一些時間,或者因爲掛起需要一些時間任務實際上停止運行 - 我不確定哪個。在任何情況下,通過在發信號通知Win32中斷處理線程之後在信號量上創建信號量爲Win32的線程塊,並且在完成其功能(握手)時讓中斷處理Win32線程解除對線程的阻塞時,這可以很容易地正確。有效地使用線程同步來強制調度模式到我需要的。我爲此使用SignalObjectAndWait()。

使用此技術時,模擬的實時調度程序在協作模式下運行時可以完美工作 - 但在搶先模式下不會(需要時)。

搶先式任務切換的問題我猜也是一樣的,任務在它被告知在實際停止運行之前掛起一段時間後繼續執行,因此係統無法保證在執行任務時保持一致狀態運行任務的線程暫停。然而,在搶先的情況下,因爲任務不知道什麼時候會發生,所以不能使用使用信號量來阻止Win32繼續下一次直到下一次恢復的技術。

有沒有人在這篇文章中做了這件事 - 對不起,因爲它的長度!

我的問題則是:

  • 我怎麼能強迫的Win32(XP)調度啓動和立即停止任務的暫停和恢復線程函數的調用 - 或 - 我怎麼能強迫更高的優先級Win32線程立即開始執行,它能夠這樣做(阻塞的對象被髮送)。有效地強制Win32重新安排正在運行的進程。

  • 有沒有某種方式異步停止任務,以等待事件,而不是任務/線程順序執行路徑。

  • 模擬器在Linux環境下運行良好,POSIX信號被用來有效地中斷線程 - 在Win32中是否有相當的功能?

感謝任何人誰已閱讀本長職務,並事先特別感謝任何人可以通過這個迷宮的Win32握住我的'實時工程師的手的時候。

回答

5

如果你需要做自己的調度,那麼你可以考慮使用fibers而不是線程。光纖就像線程,因爲它們是可執行代碼的獨立塊,但是可以在用戶代碼中調度光纖,而線程僅由OS調度。單線程可以託管和管理多條光纖的調度,光纖甚至可以相互調度。

+0

但是光纖不能同時在多個內核上運行。 – 2010-11-18 19:28:32

+0

感謝您的提示。我明天會在纖維上查找一些參考資料。 – Richard 2010-11-18 22:11:43

+0

@Zan:多根光纖可以在單個線程中運行,並且可以運行多個線程,每個線程一個線程。 – 2010-11-19 02:44:30

1

首先,你爲你的線程使用了哪些優先級值?

如果您將高優先級線程設置爲THREAD_PRIORITY_TIME_CRITICAL,它應該立即運行 - 只有那些與實時進程關聯的線程才具有更高的優先級。

其次,您如何知道暫停和恢復不會立即發生?你確定這是問題嗎?

你不能強制一個線程等待來自外部的東西而不掛起線程來注入等待代碼;如果SuspendThread不適合你,那麼這不會有幫助。

最接近信號的可能是QueueUserAPC,它會安排回調以在下一次線程進入「可警告等待狀態」時運行。通過呼叫SleepExWaitForSingleObjectEx或類似。

1

@Anthony W - 感謝您的建議。我運行的是模擬THREAD_PRIORITY_ABOVE_NORMAL上的實時任務的Win32線程,以及在THREAD_PRIORITY_HIGHEST處運行僞中斷處理程序和tick中斷髮生器的線程。如果有任何改變,暫停的線程將更改爲THREAD_PRIORITY_IDLE。我剛剛嘗試了使用THREAD_PRIORITY_TIME_CRITICAL的建議,但不幸的是它沒有任何區別。

關於你的問題,我確定暫停和恢復不會立即發生是問題 - 呃,我不是。在我不熟悉的環境中,這是我最好的猜測。我對於暫停和恢復工作失敗的想法立刻產生於我觀察到任務產生時。如果我調用yield(signal [使用Win32事件]更高優先級的Win32線程切換到下一個實時任務),我可以在yield之後放置一個斷點,並在優先級更高的斷點之前命中線。目前還不清楚延遲發信號通知事件和優先級較高的任務是否正在運行,或者延遲掛起線程和線程實際上是否停止運行導致了這種情況 - 但是這種行爲絕對是可以觀察到的。這是使用信號量握手修復的,但是由於tick中斷造成的搶佔無法完成。

我知道模擬沒有按照我的預期運行,因爲一組檢查實時任務調度順序的測試失敗。調度程序總是有可能出現問題,或者測試有問題,但測試將運行數週而不會失敗,因此我傾向於認爲測試和調度程序是正常的。一個很大的區別在於實時目標的嘀嗒聲頻率是1毫秒,而在Win32模擬目標上它是15ms,即使那樣也有很多變化。

@Remy - 我今天已經完成了很多關於光纖的閱讀,我的結論是,爲了在合作模式下模擬調度器,他們將是完美的。但是,據我所見,它們只能由光纖本身調用SwitchToFiber()函數來調度。一個線程是否可以阻塞定時器或進入睡眠狀態,以便定期運行,從而有效搶佔當時運行的光纖?從我讀過的答案來看,沒有,因爲阻塞一條光纖會阻塞線程中運行的所有光纖。如果可以使其工作,那麼定期執行的光纖是否可以調用SwitchToFiber()函數在再次休眠一段固定時間之前選擇下一個要運行的光纖?我再次認爲答案是否定的,因爲一旦它切換到另一個光纖,它將不再執行,因此直到下一次執行光纖切換回它時才實際調用Sleep()函數。如果我對纖維的工作原理有錯誤的想法,請在此糾正我的邏輯。

我認爲它可以工作,如果週期性功能可以保留在它自己的線程中,與執行光纖的線程分離 - 但是(從我讀過的內容來看)我認爲一個線程不會影響執行的纖維在不同的線程中運行。如果你能在這裏糾正我的結論,如果他們錯了,我將不勝感激。

1

[編輯] - 比下面的黑客簡單 - 它似乎只是確保所有線程運行在相同的CPU核心也解決了這個問題:o)畢竟。唯一的問題是CPU的運行速度接近100%,我不確定散熱是否會對其造成損害。 [/編輯]

阿哈!我想我有一個解決這個問題的方法 - 但它很醜。儘管如此,醜陋仍然留在港口層。

我現在所做的是每次創建一個線程來運行任務時存儲線程ID(爲每個創建的實時任務創建一個Win32線程)。然後我添加了下面的函數 - 這是使用跟蹤宏調用的。跟蹤宏可以被定義爲做你想做的任何事情,並且在這種情況下證明它非常有用。下面的代碼中的評論解釋。仿真並不完美,而且所有這些在線程調度時都是正確的,因爲它已經偏離了實時調度,而我更希望它不會出錯,但跟蹤宏的定位使代碼包含此解決方案通過了所有測試:

void vPortCheckCorrectThreadIsRunning(void) 
{ 
xThreadState *pxThreadState; 

    /* When switching threads, Windows does not always seem to run the selected 
    thread immediately. This function can be called to check if the thread 
    that is currently running is the thread that is responsible for executing 
    the task selected by the real time scheduler. The demo project for the Win32 
    port calls this function from the trace macros which are seeded throughout 
    the real time kernel code at points where something significant occurs. 
    Adding this functionality allows all the standard tests to pass, but users 
    should still be aware that extra calls to this function could be required 
    if their application requires absolute fixes and predictable sequencing (as 
    the port tests do). This is still a simulation - not the real thing! */ 
    if(xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) 
    { 
     /* Obtain the real time task to Win32 mapping state information. */ 
     pxThreadState = (xThreadState *) *((unsigned long *) pxCurrentTCB); 

     if(GetCurrentThreadId() != pxThreadState->ulThreadId) 
     { 
      SwitchToThread(); 
     } 
    } 
}