osdev.labedz.org

Wielozadaniowość?

Procesor Intel386 posiada wbudowany, sprzętowy mechanizm wielozadaniowości (ang. Multitasking). Umożliwia to (pseudo) równoległe uruchamianie wielu procesów naraz. W procesorze nie zostały zaimplementowane oddzielne instrukcje wspomagające przełączanie procesów, ale odbywa sie to z wykorzystaniem standardowych instrukcji zmiany kontroli wraz z użyciem specjalnych struktur danych i rejestrów, takich jak:

  • segment stanu zadania TSS
  • deskryptor segmentu stanu zadania
  • rejestr zadania TR
  • deskryptor bramki zadania

Dzięki tym strukturom danych procesor może przełączać wykonywane zadania zachowując oryginalny kontekst procesów, co umożliwia bezproblemowy powrót do przerwanych zadań. Dodatkowo do standardowego mechanizmu obsługi wielozadaniowości, w procesorze Intel386 dodano dwie właściwości, które znacznie zwiększają jego możliwości:

  • przełączanie zadań pod wpływem przerwań i wyjątków
  • przełączanie zadania może zmienić aktualną LDT lub aktualny katalog stron. Dzięki temu każdy proces może posiadać odrębną logiczną przestrzeń adresową i odrębne odwzorowanie adresów liniowych w fizyczne co umożliwia skuteczne odizolowanie od siebie procesów(często wykorzystywane jako dodatek do systemu ochrony zasobów).

TSS

Wszystkie informacje niezbędne do zarządzania zadaniem są przechowywane w specjalnej strukturze zwanej segmentem stanu zadania (TSS). Pola TSS dzielą się na dwie klasy:

  • pola dynamicznie ustawiane przez procesor w trakcie przełączania zadania takie jak:
    • rejestry ogólnego przeznaczenia
    • rejestry segmentowe
    • rejestry segmentowe
    • wskaźnik instrukcji
    • selektor segmentu TSS poprzednio wykonywanego zadania (aktualizowany tylko jeśli jest potrzeba powrotu do tego zadania)
  • pola statyczne ustawiane przez system operacyjny:
    • selektor LDT zadania
    • rejestr PDBR (CR3) zawierający adres bazowy katalogu stron zadania (tylko jeśli włączony mechanizm stronicowania)
    • wskaźniki do programowego stosu każdego poziomu uprzywilejowania
    • bit pracy krokowej powodujący wywołania wyjątku pracy krokowej kiedy nastąpi przełączenie na to zadanie
    • bitmapa dostępności portów wejścia/wyjścia

Segment stanu zadania może się znajdować w dowolnym miejscu liniowej przestrzeni adresowej, jednak należy zwrócić szczególną uwagę, gdy TSS leży na granicy strony i strona z wyższym adresem jest niedostępna. W tym przypadku procesor wywołuje wyjątek 'strona nie obecna' w czasie próby odczytu takiego TSS. Aby uniknąć takiej sytuacji należy umieszczać segmenty stanu zadania tak, aby nie przekraczały granicy strony, lub upewnienie się, że mechanizm wymiany strony wczyta do pamięci nie obecne strony przed ponownym wykonaniem instrukcji przełączenia zadania.

Segment stanu zadania jest przedstawiony na rysunku. Miejsca zaznaczone jako '0' są to miejsca niezdefiniowane, zarezerwowane przez producenta układu.

Segment stanu zadania (32-bitowy, dla procesora i386)
Rys. Segment stanu zadania (32-bitowy, dla procesora i386)

Segment stanu zadania, podobnie jak wszystkie segmenty, jest zdefiniowany przez deskryptor. Pole typu deskryptora TSS:

Pole typu dla deskryptora TSS
Rys. Pole typu dla deskryptora TSS

Bit B w polu typu deskryptora TSS określa czy zadanie określane przez dany TSS jest właśnie wykonywane. Umożliwia to zapobieganie przełączeniu zadania na to samo zadanie.

Większość pól deskryptora ma podobne przeznaczenie jak pola deskryptorów innych typów. Warunkiem jest jednak to, aby wartość limitu była równa lub większa od 103 (najmniejsza możliwa wielkość TSS). Próba przełączenia na zadanie z TSS opisanym deskryptorem z mniejszą wielkością limitu spowoduje wygenerowanie wyjątku. Wartość limitu większa jest dopuszczalna i często stosowana, gdy wraz z TSS występuje pole bitowe dostępności portów wejścia/wyjścia, lub gdy istnieje potrzeba przechowywania przez system większej ilości danych.

Procedura posiadająca dostęp do deskryptora TSS może wywołać przełączenie procesu. W większości systemów wartość pola DPL deskryptora powinna jednak być ustawiona na zero, co spowoduje, że jedynie procedury o odpowiednim poziomie uprzywilejowania będą mogły wywołać przełączenie zadania.

Nie zawsze procedura posiadająca dostęp do deskryptora TSS ma prawo do odczytu czy zapisu jego zawartości. Taka możliwość istnieje - należy ustawić ten sam obszar pamięci jako segment danych z odpowiednimi uprawnieniami. Próba załadowania deskryptora TSS do rejestru segmentowego spowoduje wywołanie wyjątku.

Deskryptor TSS powinien znajdować się jedynie w globalnej tablicy deskryptorów. Próba dostępu do deskryptora segmentu zadania poprzez selektor z ustawionym bitem TI na wartość jeden (wskazującym na użycie LDT) spowoduje wywołanie wyjątku.

Rejestr zadania

Rejestr zadania TR jest podobny do pozostałych rejestrów segmentowych - posiada zarówno część widoczną jak i niewidoczną dla programisty. Przechowywany jest w nim deskryptor segmentu stanu aktualnie wykonywanego zadania.

Format deskryptora bramki zadania
Rys. Format deskryptora bramki zadania

Deskryptor bramki zadania umożliwia pośrednie, chronione odwołanie do TSS.

  • Pole selektor odnosi się do deskryptora TSS umieszczonego w GDT. Wartość RPL tego selektora nie jest używana przez procesor.
  • Pole DPL zawiera wartość poziomu uprawnienia jaki musi posiadać wywołująca żądane zadanie procedura. Procedura wywołująca musi posiadać wartość CPL i RPL numerycznie mniejszą bądź równa wartości DPL wybranego deskryptora TSS. Zapobiega to przed próbą przełączenia zadania przez nieuprawnione procedury.

Procedura która posiada dostęp do bramki zadania ma możliwość przełączenia zadania, tak samo jakby posiadała dostęp do TSS. Właściwie bramki zadania są dodatkiem do deskryptora TSS i zostały wprowadzone z powodu następujących potrzeb:

  • dostęp do zadania przez kilka deskryptorów; Każde zadanie posiada jeden bit zajętości. Ponieważ jest on przechowywany w deskryptorze TSS powoduje to, że zadanie może posiadać też tylko jeden taki deskryptor. Istnieje jednak możliwość wskazywania jednego deskryptora TSS poprzez kilka bramek zadania
  • potrzeba wybiórczego dostępu do zadania; Bramki zadania mogą być przechowywane w LDT i mieć wartość DPL inną niż wartość DPL wskazywanego deskryptora TSS. Procedura, która nie ma uprawnień dostępu do deskryptora TSS (zazwyczaj mają je procedury na poziomie uprzywilejowania zero), może mieć dostęp do bramki zadania i poprzez nią wywołać inne zadanie. Dzięki temu system operacyjny może określić, które zadania mogą wykonać procedury z dowolnego poziomu uprzywilejowania.
  • potrzeba przełączenia zadania przez podprocedure obsługi przerwania lub wyjątku. Bramki zadania mogą być przechowywane w IDT co umożliwia przełączenie zadania pod wpływem wygenerowanego zadania lub wyjątku.

Przełączanie zadań

Przełączenie zadania w procesorze Intel386 następuje w przypadku wystąpienia jednego ze zdarzeń:

  • zadanie wykona instrukcje skoku JMP lub wywołania procedury CALL, które odnoszą się do deskryptora TSS
  • zadanie wykona instrukcje skoku lub wywołania procedury, które odnoszą się do bramki zadania
  • wystąpi przerwanie, albo wyjątek odnoszący się do bramki zadania w IDT
  • zostanie wykonana instrukcja powrotu z przerwania IRET kiedy ustawiona jest flaga NT procesora

i powoduje przeprowadzenie następujących operacji:

  • sprawdzenie czy bieżące zadanie ma wystarczające uprawnienia do przełączenia zadania. Dotyczy to przełączenia zadania wywołanego przez instrukcje JMP i CALL. Wartość pola DPL deskryptora TSS lub bramki zadania musi być numerycznie większa od wartości CPL i RPL procedury przełączającej
  • sprawdzenie czy deskryptor TSS nowego zadania jest zaznaczony jako 'obecny' i ma ustawioną odpowiednią wartość limitu. Jakiekolwiek błędy w tym i poprzednim kroku procedury przełączającej występują w kontekście starego zadania i mogą być obsłużone przez system w sposób przezroczysty dla aplikacji.
  • zachowanie kontekstu przerywanego zadania. Procesor znajduje adres bieżącego segmentu TSS w rejestrze zadania TR, następnie kopiuje wartość rejestrów w odpowiednie pola. Wartość pola w TSS wskazuje na następną instrukcje jaka ma być wykonana w przerywanym programie.
  • załadowanie do TR selektora nowego deskryptora TSS, ustawienie bitu zajętości w TSS nowego zadania, ustawienie flagi TS procesora
  • ładowanie kontekstu nowego zadania z TSS i uruchomienie procesu od wskazanego miejsca. Błędy które wystąpiły w tym kroku występują już w kontekście nowego zadania. W takim przypadku, dla mechanizmu wyjątków, jako instrukcja która wywołała wyjątek jest przedstawiana pierwsza instrukcja nowego zadania.

Podczas przełączania zadania kontekst przerywanego procesu jest zawsze zapamiętywany. W przypadku ponownego jego uruchamiania, wykonywania programu przebiega od instrukcji następnej po komendzie przełączania zadania, a rejestry mają tą samą wartość co przed przełączeniem. Każde przełączenie zadania powoduje też ustawienie flagi TS w słowie stanu procesora. W systemach z koprocesorem pozwala to na powiadomienie systemu, że kontekst koprocesora może nie być zgodny z bieżącym zadaniem procesora głównego. Poziom uprzywilejowania nowego zadania jest taki sam, jaki był przed jego przerwaniem (nie zmienia się).