WindowsCE: wirklich Realtime?
-
Es wird immer mal behauptet, WinCE wäre ein Echtzeitbetriebssystem. Leider kann ich davon aber nichts entdecken. Selbst wenn sich die elementaren Echtzeitfunktionen tief unten verstecken (in speziellen ISR oder ähnlichem), so müsste es doch zumindest irgend wo ein paar simple Delay-Funktionen geben, die sich mit Auflösungen und garantierten Reaktionszeiten im Mikrosekundenbereich bewegen. Leider kann ich auch das nicht finden.
Deswegen mal hier die Frage: was versteht MS bei CE jetzt tatsächlich unter Echtzeit?
TomoT
-
Was verstehst du unter Echtzeit. Die haben sich an irgend einen Standard gehalten udn garantieren unter diesen Standard die einhaltung von Echtzeitbedingungen. Wirklich Harte echtzeitbedingungen einhalten ist ja eh Programmierersache. Im endeffekt reicht es erstmal aus wenn ich bei jeder Sache die ich im BS mache einen Timer angeben kann, wann dieser Garantiert zurückkehrt. Ob die Aufgabe erfolgreich gelöst wurde oder nicht. Zusätzlich werden echte Prioritäten vergeben. Das klingt schon verdächtig nach Echtzeitfähig. Der Rest ist am Programmierer.
Kurzum, der Task mit der Höchsten Priorität kann eine Aufgabe garantiert Deterministisch in einer bestimmten Zeit erfolgreich abschließen oder kehrt nach dieser Zeit mit eine "Erfolglos" Meldung zurück. Und ich denke schon das Windows CE das einhält.
-
Also ich sage es mal so als Grundsatz zum Thema "Echtzeit":
Echtzeit heißt hier nur, daß Funktionen (und Events) definierte Reaktionszeiten haben.
Somit sind die benötigten Zeiten dieser Funktionen klar vorhersagbar. Das kennzeichnet die Echtzeit-Eigenschaft.Hier kann ich mal den weitläufigen Irrtum auch gleich ausmisten:
Echtzeit heißt NICHT, daß das Betriebssystem "sauschnell" reagiert, wie von der Mehrheit der Leser (und Schreiber) hier im Forum und auch anderswo üblich angenommen wird.Martin
-
Die Zeiten sind auch nur dann klar Vorhersagbar wenn man davon ausgehen kann das diese Funktion die höchste Priorität hat. Andernfalls ist ja eben gerade was wichtigeres dran. Im schlimmsten fall muss halt ein Echtzeitsystem mit einer Ausnahme abbrechen die besagt: ICh kann keine Echtzeit für diesen Task Garantieren. Daher haben nur absolut Kritische Tasks die höchste Prio. (Airbag öffnen oder sowas). Denn wenn dort der Gau eintritt und dort viele Task mit hoher Priorität und Geringen "Zeitfenster" starten kann die Echtzeitbedingung auch nicht eingehalten werden.
Daher ist die Programmierung von Echtzeitsystem nicht ganz einfach. Nicht wie in der Standard Multiprozessing welt wo jeder denkt sein prozess ist der wichtigste. Wenn man in einen Echtzeitsystem seinen Prozess zu ernst nimmt, kann das Leben kosten da gerade eine Notfallroutine nicht dran kommt weil der eigene Prozess mit höherer Priorität läuft.
-
Was ich unter Echtzeit verstehe habe ich ja eigentlich geschrieben: Wenn ich eine Pause von x einlege, dann erwarte ich, dass die Pause exakt x dauert. Beim normalen Windows ist es ja so, dass Pausen kleiner 10 msec gar nicht erst möglich sind und Pausen größer 10 msec und Threadswitches auch einen Jitter von min. 10 msec haben.
Deswegen etwas detaillierter: Was ist hier für CE spezifiziert? Welche möglichkeit einer Pause definierter Länge habe ich?
-
Die einzigste möglichkeit sowas unter Windows zu machen ist meines wissens nach RTX (http://www.intervalzero.com/rtx.htm).
Wenn du einen Takt erzeugen willst ohne so eine Echtzeit Erweiterung musst du die Uhrzeit überwachen und in manchen Takten mehrere Takte ausführen um verlorene Zeit wieder aufzuholen. (dies ist natuerlich ungenau für messwerterfassung, füer simulation ist das jedoch in ordnung)
Hier ein paar Hilfsfunktionen die ich mir mal geschrieben hatte.
void sleep(double usleept) { if(usleept >= 0) { // Nur wenn zu schlafen ist struct timeval tv; tv.tv_sec = floor(usleept); tv.tv_usec = fround((usleept - floor(usleept)) * 1000000); // s -> us // 500 ms = 500 000 us; fd_set dummy; SOCKET s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); FD_ZERO(&dummy); FD_SET(s, &dummy); select(0, NULL, NULL, &dummy, &tv); closesocket(s); } } bool setcyclefrequency( double frequency, bool noadjustment /*= false*/ ) { // Es muss ein Core ausgesucht werden als Zeitgeber HANDLE hProcess = GetCurrentProcess(); m_dwAffinityMask = 0; DWORD dwSystemAffinityMask = 0; GetProcessAffinityMask(hProcess, &m_dwAffinityMask, &dwSystemAffinityMask); unsigned int iProzessor = 0; for(iProzessor = 0; iProzessor < sizeof(m_dwAffinityMask)*8; iProzessor) { DWORD dwAffinityMask = m_dwAffinityMask & (((DWORD)1) << iProzessor); if(dwAffinityMask) { m_dwAffinityMask = dwAffinityMask; break; // Ersten Prozessor wählen } } if(!m_dwAffinityMask) return false; // Immer auf gleichem Prozessor ausführen m_hThread = GetCurrentThread(); DWORD oldAffinityMask = SetThreadAffinityMask(m_hThread, m_dwAffinityMask); if(!oldAffinityMask) return false; if(!QueryPerformanceFrequency((LARGE_INTEGER*)&m_clockFrequency)) // zeit in clocks pro sekunde return false; if(frequency > 1000000) // Max 1 MHz / 1 us (theoretisch) // real max 10 KHz frequency = 1000000; m_cycleTime = (1.0/frequency); // Zeit in Clocks die ein Zyklus dauert m_bAdjustment = !noadjustment; SetThreadAffinityMask(m_hThread, oldAffinityMask); return resetcycle(); } bool waitfornextcycle() { m_cycles++; // Immer auf gleichem Prozessor ausführen DWORD oldAffinityMask = SetThreadAffinityMask(m_hThread, m_dwAffinityMask); if(!oldAffinityMask) return false; if(!m_bInit) { m_dwstartTime = m_dwcurrentTime = GetTickCount(); m_bInit = true; } else m_dwcurrentTime = GetTickCount(); if(m_dwcurrentTime < m_dwlastTime) // Overflow in Timer ~50 Tage { m_dwstartTime = m_dwcurrentTime; m_dwlastTime = ((~(DWORD)0) - m_dwlastTime) + m_dwcurrentTime; m_cycles = 1; } if(0 == m_cycles) // Overflow Zykluszähler (Taktabhängig) { m_dwstartTime = m_dwcurrentTime; m_cycles = 1; } // Overflow des PerfomanceCounters sind bei 3 Ghz circa 180 Jahre also nicht relevant -> Rückfall auf 10 ms Auflösung in diesem Fall da hohe differenz if(!QueryPerformanceCounter((LARGE_INTEGER*)&m_currentTime)) return false; double QPCdiff = m_currentTime - m_lastTime; double GTCdiff = m_dwcurrentTime - m_dwlastTime; double GTCdiff_total = m_dwcurrentTime - m_dwstartTime; QPCdiff /= m_clockFrequency; // clocks -> s GTCdiff /= 1000; // ms -> s GTCdiff_total /= 1000; // ms -> s if(fabs(QPCdiff - GTCdiff) > 0.1) // mehr als 100ms Differenz -> QPC springt oder overflowt, GTC timer Korrektur (bei langen Zyklusdauern) // Oder Overflow im Performance Counter QPCdiff = GTCdiff; // Fallback zu ms Auflösung in diesem Takt m_nLostCycles = fround((GTCdiff_total / m_cycleTime) - m_cycles); // Genauerer Sleeps über BSD Socket select Funktion if(m_nLostCycles <= 0 || !m_bAdjustment) { // Nur wenn momentan keine Takte verlorengegangen sind oder Takt Korrektur abgeschaltet ist double usleept = m_cycleTime - QPCdiff; // Berechne wie lange wir schlafen duerfen anhand der vergangen Zeit leit letzem Aufruf sleep(usleept); } // Frequenz hier nochmal aktualisieren angeblich ändert sie sich trotz MSDN trotzdem manchmal if(!QueryPerformanceFrequency((LARGE_INTEGER*)&m_clockFrequency)) // zeit in clocks pro sekunde return false; m_dwlastTime = GetTickCount(); if(!QueryPerformanceCounter((LARGE_INTEGER*)&m_lastTime)) return false; SetThreadAffinityMask(m_hThread, oldAffinityMask); return true; } bool resetcycle() { m_lastTime = 0; m_currentTime = 0; m_dwstartTime = 0; m_dwlastTime = 0; m_dwcurrentTime = 0; m_bInit = false; m_cycles = 0; m_nLostCycles = 0; // Immer auf gleichem Prozessor ausführen DWORD oldAffinityMask = SetThreadAffinityMask(m_hThread, m_dwAffinityMask); if(!oldAffinityMask) return false; m_dwlastTime = GetTickCount(); if(!QueryPerformanceCounter((LARGE_INTEGER*)&m_lastTime)) return false; SetThreadAffinityMask(m_hThread, oldAffinityMask); return true; } bool isfrequencyok() { // Wenn wir über 10 Sekunden nachhängen ist der Takt nicht realisierbar oder es wird debuggt etc. bool bRet = ((double)m_nLostCycles * m_cycleTime) <= 10.0; if(!bRet) return false; return bRet; }Frequenz einmal initialisieren und dann einfach immer waitfornextcycle aufrufen sorgt dafür das die Frequenz eingehalten wird. (bei mir bis zu 10 khz)
-
TomoT schrieb:
Was ich unter Echtzeit verstehe habe ich ja eigentlich geschrieben: Wenn ich eine Pause von x einlege, dann erwarte ich, dass die Pause exakt x dauert.
Das ist aber nicht unbedingt eine allgemeingültige Definition von Echtzeit. Auf einem C64 konnte man sowas machen wie Du sagst, war deswegen das C64-OS ein Echtzeitsystem?
Siehe hierzu
http://msdn.microsoft.com/library/ms834456#_rtc_real_time_properties_of_windows_ce_30
Der Artikel verweist z.B. schon mal darauf, daß man keine Nicht-Realtime-API-Aufrufe innerhalb von Realtime-Slots verwenden soll. Bei so einem komplexen OS fängt das doch schon an, man kann mit CE Echtzeitanwendungen erstellen, aber aus dem Einsatz von CE folgt noch keine Echtzeitfähigkeit.
-
TomoT schrieb:
Was ich unter Echtzeit verstehe habe ich ja eigentlich geschrieben: Wenn ich eine Pause von x einlege, dann erwarte ich, dass die Pause exakt x dauert.
Das ist aber keine übliche Definition.
Viele RTOS bieten als einzige "realtime" Option einen (!) Interrupt der mit garantiert weniger als X Latenzzeit seinen Handler startet, dessen Ausführung dann durch nichts unterbrochen wird.
-
Ja, und sowas bietet CE.