Linux a czas rzeczywisty
Wprowadzenie do systemów RT:

RTLinux:

KURT Linux:

Slajdy:

Scheduler

Są trzy główne tryby szeregowania dla procesu:

  • KURT_EXPLICIT - procesy te wykonują się podczas okresów działania czasu rzeczywistego (real-time execution intervals), system używa timerów (wakeup i watchdog) do budzenia procesów w odpowiednim czasie. Poza takimi okresami procesy mogą jedynie obsługiwać sygnały. Ponieważ czasy wykonania takich procesów są bardzo konkretnie określone, mają one zasadniczo pierwszeństwo przed innymi procesami.
  • KURT_ANYTIME - procesy te mogą wykonywać się podczas okresów działania czasu rzeczywistego, również w oparciu o timery wakeup i watchdog, wtedy są traktowane na równi z procesami KURT_EXPLICIT. Mogą również działać poza takimi okresami, wtedy są traktowane zależnie od aktualnego trybu szeregowania w jądrze.
  • KURT_PERIODIC - tylko jeden proces czasu rzeczywistego w danym momencie może być w tym trybie. Proces określa okres pomiędzy podejmowanymi przez siebie działaniami. Jest to czas w mikrosekundach, określa go pole period struktury rtparams. Na podstawie tej wartości jądro dynamicznie generuje wakeup timery dla procesu. Taki proces może działać tyko wówczas, gdy trybem szeregowania w jądrze jest SCHED_KURT_MIXED.

Jeśli proces wybrał KURT_EXPLICIT, musi mieć również określony podtryb szeregowania:

  • KURT_EPISODIC - ten tryb jest dla procesów z gwarantowanym pesymistycznym czasem wykonywania się. Takie procesy wykonują te same czynności podczas każdego okresu działania. Scheduler takiego procesu składa się z wakeup timerów, które określają, kiedy proces ma zacząć się wykonywać. Po wykonaniu swojej pracy proces musi wywołać rt_suspend (zwolnić jądro) aby zawiesić swoje działanie. Pole exec_time struktury rtparams oznacza najgorszy możliwy czas działania procesu (w mikrosekundach). Na tej podstawie jądro programuje watchdog timery dla każdego okresu działania. Jeśli proces nie wykona rt_suspend przed upływem zadeklarowanego czasu generowany jest błąd a proces jest zawieszany. Procesy z trybem KURT_PERIODIC zawsze działają jako KURT_EPISODIC.
  • KURT_CONTINUOUS - ten tryb jest dla procesów, które nie są w stanie określić czasu swojego działania i być może podczas kolejnych okresów działania wykonują inne czynności. Dla tych procesów wakeup timery również określają, kiedy proces ma zacząć się wykonywać, natomiast watchdog timery "pilnują" końca okresu działania. Procesy nie muszą wywoływać rt_suspend; zostaną zawieszone przez system po wygaśnięciu watchdog timerów. Opcjonalnie pole exec_time może być określone, wtedy odpowiednie watchdog timery zostaną dynamicznie wygenerowane przez jądro. Jeśli proces zawiesi się przed wygaśnięciem watchdog timera, np. wskutek jakiejś blokującej operacji, jądro usunie timer. Procesy z trybem KURT_ANYTIME zawsze działają jako KURT_CONTINUOUS.

Tryby szeregowania w jądrze:

Standardowo KURT jest w trybie normalnym, zachowując się jak zwykły Linux. Funkcja switch_to_rt przełącza jądro w tryb czasu rzeczywistego. Jako rt_mode można podać:

  • SCHED_KURT_FOCUSSED - w tym trybie tylko procesy czasu rzeczywistego mogą działać. Jeżeli wygasa wakeup timer dla procesu KURT_EXPLICIT lub KURT_ANYTIME, proces zacznie się wykonywać. Jeżeli proces szeregujący został obudzony z jakiegoś innego powodu niż wygaśnięcie wakeup timera, zostanie obudzony gotowy do działania proces KURT_ANYTIME. Jeżeli nie ma gotowych do działania procesów czasu rzeczywistego, zaczyna działać idle. Widać zatem, że procesy inne niż procesu czasu rzeczywistego nie mogą się w tym trybie wykonywać.
  • SCHED_KURT_PREFERRED - podobnie jak SCHED_KURT_FOCUSSED, z tym że jeśli nie ma żadnych procesów czasu rzeczywistego gotowych do działania, budzony jest standardowy proces szeregujący Linuksa, który wybierze albo proces nie będący procesem czasu rzeczywistego, albo idle.
  • SCHED_KURT_MIXED - Jeżeli wygasa wakeup timer dla procesu KURT_EXPLICIT, KURT_ANYTIME lub KURT_PERIODIC, proces zacznie się wykonywać. Jeżeli proces szeregujący został obudzony z jakiegoś innego powodu niż wygaśnięcie wakeup timera, budzony jest standardowy proces szeregujący Linuksa, który może wybrać proces KURT_ANYTIME, proces nie będący procesem czasu rzeczywistego lub idle.

    rtparams

    struct rtparams {
    	int rt_id;
    	unsigned long period;
    	unsigned long exec_time;
    	char rt_name[MAX_RT_NAME_LENGHT];	
    	unsigned int rt_mode;
    }
    

    Obiekt takiej struktury musi być przez proces zadeklarowany i zainicjowany aby proces został zarejestrowany jako proces czasu rzeczywistego. Znaczenie poszczególnych pól:

    • rt_id - unikatowy identyfikator procesu (liczbe z zakresu 0-255). Jest używany przez jądro do indeksowania tablicy procesów czasu rzeczywistego. Jeśli to pole jest ustawione na ASSIGN_RT_ID, KURT automatycznie nada numer.
    • period - czas (w mikrosekundach) pomiędzy kolejnymi budzeniami dla procesu KURT_PERIODIC. Jądro automatycznie wygeneruje wszystkie wakeup timery. Dla procesów innych niż KURT_PERIODIC musi być ustawione na 0.
    • exec_time - dla procesów KURT_EPISODIC i KURT_PERIODIC oznacza pesymistyczny czas działania, na tej podstawie generowane są dynamicznie watchdog timery. Dla procesów KURT_CONTINUOUS pole to jest opcjonalne. Oznacza czas po rozpoczęciu działania, w którym proces powinien się zawiesić. Tu również generowane są watchdog timery, ale nie powodują one komunikatów o błędach. Takie watchdog timery są ignorowane, jeśli w zgłoszonym przez użytkownika schedule file są już watchdog timery.
    • rt_name - unikatowa nazwa przypisana procesowi czasu rzeczywistego, może być ustawiona na NULL
    • rt_mode - tryb szeregowania procesu.

    Po zainicjalizowaniu takiej struktury proces powinien wywołać set_rtparams, co skopiuje strukturę do jądra, sprawdzi poprawność i dołączy do danego procesu. Po takiej rejestracji proces być może (jeśli jest KURT_EXPLICIT lub KURT_PERIODIC) wywołać rt_suspend, aby czekać na wygaśnięcie timera. Procesy KURT_ANYTIME nie muszą tego robić, ponieważ moga działać poza okresami działania czasu rzeczywistego.

    format schedule file

    Procesy KURT_EPISODIC lub KURT_CONTINUOUS muszą zgłaszać odpowiednie wakeup timery, abu system wiedział kiedy powinny być obudzone. Procesy KURT_CONTINUOUS mogą też zgłaszać watchdog timery, aby było wiadomo kiedy powinny być zawieszone. To wszystko jest zapisane w schedule file. Schedule file to plik binarny zawierający zbiór obiektów struktury timer_list. Pola struktury mają następujące znaczenie:

    • list.next i list.prev - używane przez jądro do łączenia w listę (sortuje po czasach). Przy inicjowaniu przez użytkownika powinny buć ustawione na NULL.
    • expires - po ilu "jiffies" powinien zostać wykonany. Dla architektury X86 "jiffie" wynosi 10 milisekund.
    • data - określa do którego modułu timer powinien zostać dostarczony. W większości przypadków będzie to moduł procesów (wtedy data wynosi 0). Pole to może być też ustawione na KURT_REPEAT, wtedy wszystko będzie powtarzane odpowiednią ilość razy.
    • function - identyfikator (rt_id struktury rtparams) procesu.
    • usec - dokładny czas w danym jiffie (w mikrosekundach) wygaśnięcia timera. Jeśli więc expires wynosi 100, a usec 500, timer wygaśnie po 1000 milisekundach i 500 mikrosekundach.
    • flags - musi być ustawione na KURT_WAKEUP lub KURT_WATCHDOG.

    Tak więc proces najpierw tworzy odpowiednie timery, zapisuje je do jakiegoś pliku, teraz plik musi być zgłoszony do schedulera. Aby proces mógł zgłosić schedule file do schedulera, musi być zarejestrowany jako proces czasu rzeczywistego, jako aktualnie szeregujący proces, jądro musi działać w trybie czasu rzeczywistego.

    Co więc należy zrobić aby stworzyć plan szeregowania?

    1. Stworzyć odpowiednią ilość obiektów struktury timer_list i odpowiednio zainicjować ich pola.
    2. Zapisać wszystkie obiekty do pliku.

    Co trzeba zrobić aby plan z danego pliku stał się aktualnym planem szeregowania?

    1. Proces który ma zamiar to zrobić musi być zarejestrowany jako proces czasu rzeczywistego (czy zainicjować strukturę rtparams i zgłosić ją do jądra za pomocą set_rtparams).
    2. Ustawić siebie jako aktualny proces szeregujący (set_scheduling_task).
    3. Przełączyć jądro w tryb czasu rzeczywistego (switch_to_rt).
    4. Zgłosić dany schedule file do jądra (rt_schedule_events).
    5. Wrócić do normalnego trybu (switch_to_normal).

To jest szeregowanie statyczne. KURT pozwala także na szeregowanie dynamiczne. Odbywa się to w podobny sposób, z tym że nie trzeba używać już plików, istnieją funkcje pozwalające na zgłoszenie nowego planu szeregowanie do schedulera w formie pewnych struktur.