2010-11-18 12 views
6

Jestem programistą wbudowanym, który próbuje symulować planowanie zapobiegawcze w czasie rzeczywistym w środowisku Win32 przy użyciu Visual Studio 2010 i MingW (jako dwóch oddzielnych środowisk kompilacji). Jestem bardzo zielony w środowisku planowania Win32 i uderzyłem w mur z tego, co próbuję zrobić. Nie próbuję osiągnąć zachowania w czasie rzeczywistym - wystarczy, aby symulowane zadania były uruchamiane w tej samej kolejności i kolejności, jak w rzeczywistym sprzęcie docelowym.Planowanie wątków systemu Win32 do zdefiniowanej sekwencji na podstawie priorytetu

Symulowany program planujący w czasie rzeczywistym ma prosty cel - zawsze wykonywać zadanie o najwyższym priorytecie (wątek), które można uruchomić. Gdy tylko zadanie stanie się możliwe, musi uprzedzić aktualnie uruchomione zadanie, jeśli ma wyższy priorytet niż aktualnie uruchomione zadanie. Zadanie może zostać uruchomione ze względu na zdarzenie zewnętrzne, na które czekał, lub upłynął limit czasu/czasu blokowania/czasu uśpienia - z przerwą zaznaczającą podstawę czasu.

Oprócz tego poboru zachowania zadanie może dawać albo ochotnika zrezygnować z przedział czasu, ponieważ to realizuje się funkcję uśpienia lub czekać typu.

Symuluję to, tworząc wątek Win32 o niskim priorytecie dla każdego zadania tworzonego przez symulowaną symulację czasu rzeczywistego (wątek skutecznie przełącza konteksty, które planista wykonywałby na rzeczywistym docelowym obiekcie osadzonym), średni priorytet Wątek Win32 jako pseudo przerywnik obsługi (obsługuje symulowane przerwania i żądania yield, które są sygnalizowane do niego przy użyciu obiektu zdarzenia Win32), a wątek Win32 o wyższym priorytecie symuluje urządzenie peryferyjne, które generuje przerwania.

Gdy obsługa pseudo przerwań ustali, że powinien wystąpić przełącznik zadań, zawiesza bieżący wątek za pomocą metody SuspendThread() i wznawia wątek, który wykonuje nowo wybrane zadanie za pomocą funkcji ResumeThread(). Spośród wielu zadań i skojarzonych z nimi wątków Win32, które mogą być tworzone, tylko jeden wątek, który zarządza zadaniem, nigdy nie będzie znajdował się w stanie zawieszenia.

Ważne jest, aby zawieszona nić zawiesiła natychmiast wywołanie metody SuspendThread() i aby wątek obsługi pseudo przerwań został wykonany, gdy tylko zostanie wydane powiadomienie, że przerwanie jest w toku - ale to nie jest zachowanie widzę.

Jako przykładowy problem, nad którym już pracowałem: Gdy zadanie/wątek daje zdarzenie wydajności jest zablokowane w zmiennej i wątek obsługi przerwań jest sygnalizowany, ponieważ istnieje pseudo przerwanie (wydajność), które wymaga przetwarzanie. Teraz w systemie czasu rzeczywistego, jak jestem przyzwyczajony do programowania, oczekiwałbym natychmiastowego uruchomienia wątku obsługi przerwania, który jest sygnalizowany, ponieważ ma wyższy priorytet niż wątek, który go sygnalizuje. To, co widzę w środowisku Win32, to wątek, który sygnalizuje wątek o wyższym priorytecie przez jakiś czas, zanim zostanie zawieszony - ponieważ zajmuje trochę czasu, zanim wątek o wyższym priorytecie zacznie się uruchamiać lub ponieważ trwa to dłużej zadanie, aby faktycznie przestać działać - nie jestem pewien, który. W każdym razie może to być łatwo poprawne poprzez utworzenie blokowania wątku Win32 na semaforze po sygnalizowaniu wątku obsługi przerwań Win32, i użycie wątku Win32 w celu przerywania wątku odblokowuje wątek po zakończeniu jego funkcji (uzgadnianie). Efektywnie za pomocą synchronizacji wątku wymuszam schemat harmonogramu na to, czego potrzebuję. Używam do tego celu SignalObjectAndWait().

Stosując tę ​​technikę symulacji działa perfekcyjnie, gdy harmonogram czasu rzeczywistego są symulowane działa w trybie kooperacji - ale nie (tak jak jest to konieczne) w trybie poboru.

Problem z przełączaniem zadań prewencyjnych jest taki sam, zadanie to jest kontynuowane przez pewien czas po tym, jak kazano mu zawiesić, zanim faktycznie przestanie działać, więc nie można zagwarantować, że system pozostanie w stanie spójnym, gdy wątek, który uruchamia zadanie, zawiesza się. W przypadku prewencyjnym jednak, ponieważ zadanie nie wie, kiedy ma się wydarzyć, nie można użyć tej samej techniki używania semafora, aby zapobiec dalszym błędom Win32, dopóki nie zostanie on ponownie wznowiony.

Ktoś zrobił tak daleko w tym poście - przepraszam za jego długość!

Moje pytania są więc:

  • Jak mogę zmusić Win32 (XP) harmonogram, aby rozpocząć i natychmiast zatrzymać zadania, że ​​zawiesić i wznowić funkcje gwint nazywane są - lub - jak mogę zmusić wyższy priorytet Wątek Win32, aby rozpocząć wykonywanie od razu, że jest to w stanie wykonać (obiekt, na którym jest zablokowany, jest sygnalizowany). Skutecznie zmuszając system Win32 do zmiany harmonogramu uruchomionych procesów.

  • Czy istnieje sposób asynchronicznego zatrzymania zadania, aby czekać na zdarzenie, gdy nie znajduje się w ścieżce wykonywania sekwencji/wątków zadań/wątków.

  • Symulator działa dobrze w środowisku Linux, w którym sygnały POSIX są używane do skutecznego przerywania wątków - czy istnieje odpowiednik w Win32?

Dzięki każdemu, kto podjął czasu na przeczytanie tego długiego posta, a zwłaszcza z góry dzięki każdemu, który może pomieścić rękę „Real mechaników czasu” przez ten labirynt Win32.

Odpowiedz

5

Jeśli chcesz wykonać własne planowanie, możesz rozważyć użycie fibers zamiast wątków. Włókna są jak wątki, ponieważ są oddzielnymi blokami kodu wykonywalnego, jednak włókna można zaplanować w kodzie użytkownika, podczas gdy wątki są planowane tylko przez system operacyjny. Pojedynczy wątek może obsługiwać szeregowanie wielu światłowodów i zarządzać nimi, a włókna mogą nawet nawzajem się planować.

+0

Ale włókna nie będą działać jednocześnie na wielu rdzeniach. –

+0

Dzięki za cynk.Będę jutro sprawdzał materiał referencyjny na temat włókien. – Richard

+0

@Zan: wiele włókien może działać w jednym wątku i można uruchomić wiele wątków, po jednym dla każdego rdzenia. –

1

Po pierwsze, jakie wartości priorytetowe wykorzystujesz do swoich wątków?

Jeśli ustawisz wątek o wysokim priorytecie na THREAD_PRIORITY_TIME_CRITICAL, powinien on działać niemal natychmiast - tylko te wątki związane z procesem czasu rzeczywistego będą miały wyższy priorytet.

Po drugie, skąd wiadomo, że zawieszenie i wznowienie nie następuje natychmiast? Jesteś pewien, że to jest problem?

Nie można zmusić nici do oczekiwania na coś z zewnątrz bez zawieszania wątku w celu wstrzyknięcia kodu oczekiwania; jeśli SuspendThread nie działa dla ciebie, to to nie pomoże.

Najbliższy sygnał to prawdopodobnie QueueUserAPC, który zaplanuje wywołanie zwrotne w celu uruchomienia po następnym przejściu wątku do "stanu wyczekiwanego oczekiwania", np. pod numerem SleepEx lub WaitForSingleObjectEx lub podobnym.

1

@Anthony W - dzięki za poradę. Uruchamiałem wątki Win32 symulujące zadania w czasie rzeczywistym w THREAD_PRIORITY_ABOVE_NORMAL oraz wątki, które uruchamiały obsługę pseudo przerwań i generator przerwań zaznaczenia na THREAD_PRIORITY_HIGHEST. Zawieszone wątki zmieniły się na THREAD_PRIORITY_IDLE w przypadku, które miało jakąkolwiek różnicę. Właśnie wypróbowałem twoją sugestię użycia THREAD_PRIORITY_TIME_CRITICAL, ale niestety to nie robi żadnej różnicy.

Jeśli chodzi o pańskie pytanie, to jestem pewien, że zawieszenie i wznowienie, które nie nastąpiło natychmiast, jest problemem - no nie, nie jestem. To jest moje najlepsze przypuszczenie w środowisku, którego nie znam. Moje myślenie o niepowodzeniu zawieszenia i wznowienia pracy wynika natychmiast z moich obserwacji, gdy zadanie zostanie wykonane. Jeśli wykonam wywołanie, aby wykonać (sygnał [przy użyciu zdarzenia Win32] wątek Win32 o wyższym priorytecie, aby przełączyć się do następnego zadania w czasie rzeczywistym) Mogę umieścić punkt przerwania po wydajności i to zostanie pobite przed punktem przerwania w wyższym priorytecie wątek. Nie jest jasne, czy przyczyną tego było opóźnienie w sygnalizacji zdarzenia i uruchamianie zadania o wyższym priorytecie, czy też opóźnienie w zawieszeniu wątku i wątku faktycznie zatrzymującego się, ale zachowanie to było zdecydowanie przestrzegane. Naprawiono to za pomocą uścisku dłoni semafora, ale nie można tego zrobić dla przejęć spowodowanych przerwaniami.

Wiem, że symulacja nie działa zgodnie z oczekiwaniami, ponieważ zestaw testów sprawdzających kolejność planowania zadań w czasie rzeczywistym nie działa. Zawsze jest możliwe, że program planujący ma problem lub test ma problem, ale test będzie działał przez kilka tygodni bez pomijania rzeczywistego celu w czasie rzeczywistym, więc jestem skłonny pomyśleć, że test i harmonogram są w porządku. Duża różnica polega na docelowym czasie rzeczywistym, częstotliwość tikowania wynosi 1 ms, podczas gdy w symulowanym obiekcie Win32 jest to 15 ms z dość dużą zmiennością nawet wtedy.

@Remy - Dzisiaj sporo czytałem na temat włókien, a moim wnioskiem jest to, że do symulacji harmonogramu w trybie współpracy byłyby idealne. Jednak, o ile widzę, mogą one być zaplanowane tylko przez same włókna wywołujące funkcję SwitchToFiber(). Czy wątek może zostać zablokowany na czasomierzu lub uśpiony, aby działał okresowo, skutecznie zapobiegając działaniu działającego włókna? Z tego, co przeczytałem, odpowiedź brzmi: nie, ponieważ zablokowanie jednego włókna zablokuje wszystkie włókna biegnące w nici. Gdyby mogło to zadziałać, czy okresowo działające włókno może wywołać funkcję SwitchToFiber(), aby wybrać następne włókno do uruchomienia przed ponownym spaniem przez określony czas? Ponownie myślę, że odpowiedź brzmi nie, ponieważ po przełączeniu się na inne włókno nie będzie już wykonywane i tak naprawdę nie wywoła funkcji Sleep(), dopóki następnym razem światłowód wykonawczy nie przełączy się z powrotem do niego. Proszę poprawić moją logikę tutaj, jeśli mam błędne pojęcie jak działają włókna.

Myślę, że to mogłoby działać, gdyby okresowa funkcjonalność mogła pozostać we własnym wątku, niezależnie od wątku, który wykonywał włókna - ale (ponownie z tego, co przeczytałem) Nie sądzę, że jeden wątek może wpływać na wykonanie włókien biegnących w innym wątku. Ponownie byłbym wdzięczny, gdybyś mógł poprawić moje wnioski tutaj, jeśli są one złe.

1

[EDYCJA] - prostsze niż hack poniżej - wydaje się po prostu zapewnienie, że wszystkie wątki uruchomione na tym samym rdzeniu procesora również rozwiązuje problem: o) Po tym wszystkim. Jedynym problemem jest to, że procesor pracuje prawie na 100% i nie jestem pewien, czy ciepło to jest szkodliwe. [/ EDYC]

Ahaa! Wydaje mi się, że mam do tego jakieś zadanie, ale jest to brzydkie. Brzydota jest jednak przechowywana w warstwie portowej.

Co teraz robię, to przechowuję identyfikator wątku za każdym razem, gdy tworzony jest wątek w celu uruchomienia zadania (wątek Win32 jest tworzony dla każdego tworzonego zadania w czasie rzeczywistym). Następnie dodałem funkcję poniżej - która jest wywoływana za pomocą makr śledzenia. Makra śledzenia można zdefiniować tak, aby robić, co się chce, i okazały się bardzo przydatne w tym przypadku. Komentarze w poniższym kodzie wyjaśniają. Symulacja nie jest doskonała, a wszystko to ma rację harmonogramowania wątków, gdy już odchodzi od harmonogramu w czasie rzeczywistym, podczas gdy ja wolałbym, aby nie pomylił się w pierwszej kolejności, ale pozycjonowanie makr śledzenia sprawia, że ​​kod zawierający to rozwiązanie przechodzi wszystkie testy:

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(); 
     } 
    } 
} 
Powiązane problemy