41. System operacyjny. Postrzeganie systemu operacyjnego
przez warstwę oprogramowania użytkowego
Pierwsze komputery, budowane według zasad określonych przez Johna von
Neumanna, nie były kompatybilne dla tworzonych programów. Każdy program
był tworzony dla konkretnej maszyny, mógł współpracować ze ściśle określoną
elektroniką komputera i urządzeń peryferyjnych. Nie można było przenieść
programu do komputera innego producenta. Zaistniała konieczność
odseparowania programów użytkowych (aplikacji) od sprzętu komputerowego.
W latach sześćdziesiątych pojawił się pomysł, aby pracą komputera zarządzało
specjalne oprogramowanie, zwane do dziś systemem operacyjnym
System operacyjny (ang. skrót OS Operating System) jest to oprogramowanie
które działa jako pośrednik pomiędzy użytkownikiem komputera a sprzętem
komputerowym. Zadaniem systemu operacyjnego jest stworzenie środowiska
do uruchamiania i kontroli zadań użytkownika oraz zarządzanie sprzętem
komputerowym.
System operacyjny jest ważną częścią systemu komputerowego.
System komputerowy
można podzielić na:
sprzęt
system operacyjny
aplikacje użytkowe
użytkownik
Sprzęt
– podstawowe zasoby systemu komputerowego dające możliwości
obliczeniowe (procesor, pamięć, urządzenia wejścia/wyjścia)
Aplikacje użytkowe
– określają sposoby, w jakie zostają użyte zasoby
systemowe do rozwiązywania problemów obliczeniowych zadanych przez
użytkownika (kompilatory, systemy baz danych, gry, oprogramowanie
biurowe). Zazwyczaj istnieje wielu różnych użytkowników (ludzie, maszyny,
inne komputery) zmagających się z rozwiązywaniem różnych zadań.
Odpowiednio do rozmaitych potrzeb może istnieć wiele różnych programów
użytkowych. System operacyjny nadzoruje i koordynuje posługiwanie się
sprzętem przez różne programy użytkowe, które pracują na zlecenie różnych
użytkowników.
Użytkownik
– ludzie, maszyny, inne komputery, mają bezpośredni kontakt z
oprogramowaniem użytkowym.
System operacyjny
Przyjęto podział na trzy główne elementy budowy systemu operacyjnego:
jądro
powłoka
programy użytkowe
Jądro
(ang. kernel) jest to część systemu operacyjnego, która przyjmuje
kierowane do niego zlecenia od programów użytkowych oraz użytkownika
komputera i wykonuje je przydzielając im zasoby komputera i urządzenia
zewnętrzne. Jądro jest najważniejszą częścią systemu operacyjnego, która
działa zawsze. Jest to pierwszy program, który startuje po uruchomieniu
komputera, i ostatni, jaki jeszcze działa przy zamykaniu systemu. Jego zadanie
to przydzielanie czasu procesora poszczególnym programom, przydziałem
pamięci i obsługą pamięci masowych. Komunikacja programów użytkowych i
użytkownika odbywa się za pośrednictwem powłoki systemu.
Powłoka
(ang. shell) jest programem, który interpretuje (stąd nazwa
interpreter) polecenia przesyłane do jądra systemu. Powłoki mogą być
tekstowe lub graficzne. Powłoką tekstową w systemie DOS jest command.com,
a graficzną w systemie Windows Eksplorer.
Programy użytkowe
są to programy wbudowane w system lub do niego
dołączone, które mają na celu pomagać użytkownikowi korzystanie z systemu.
Większość systemów operacyjnych ma budowę warstwową :
Funkcje poszczególnych części składowych systemu:
System operacyjny dostarcza narzędzi do właściwego użycia zasobów systemu
komputerowego. System operacyjny nie wykonuje sam żadnej użytecznej
funkcji a jedynie tworzy środowisko (ang. environment), w którym inne
programy mogą wykonywać pożyteczne prace. Możemy uważać system
operacyjny za dystrybutora zasobów poniższych zasobów:
Procesor
– przydział czasu procesora
Pamięć
o
alokacja przestrzeni adresowej dla procesów
o
transformacja adresów
Urządzenia zewnętrzne
o
udostępnianie i sterowanie urządzeniami pamięci masowej
np. dysk twardy
o
alokacja przestrzeni dyskowej
o
udostępnianie i sterowanie drukarkami, skanerami, aparatami itp
Informacja (system plików)
o
organizacja i udostępnianie informacji
o
ochrona i autoryzacja dostępu do informacji
System operacyjny pełni funkcję zarządcy owych dóbr i przydziela je
poszczególnym programom i użytkownikom wówczas, gdy są one nieodzowne
do wykonywania zadań. Ponieważ często może dochodzić do konfliktów przy
zamawianiu zasobów, system operacyjny musi decydować o przydziale
zasobów poszczególnym zamawiającym, mając na względzie wydajne i
harmonijne działanie całego systemu komputerowego.
Podział systemów operacyjnych
Najszerszym ale najbardziej podstawowym kryterium podziału systemów
operacyjnych jest podział na:
system operacyjny czasu rzeczywistego (RTOS)
systemy operacyjne czasowo niedeterministyczne
Podział ten odnosi się do najbardziej podstawowej funkcjonalności systemu
operacyjnego jakim jest planowanie i przydział czasu procesora
poszczególnym zadaniom.
Ze względu na sposób realizacji przełączania zadań systemy operacyjne można
podzielić na:
systemy z wywłaszczaniem zadań
systemy bez wywłaszczania.
Inny rodzaj podziału to podział na:
otwarte systemy operacyjne
wbudowane systemy operacyjne.
Systemy otwarte można uruchomić na dowolnej maszynie wskazanego
rodzaju np. PC i w określonym stopniu modyfikować. Systemy wbudowane
jak sama nazwa wskazuje są zaszyte
(wbudowane)
wewnątrz urządzeń
użytkowych, maszyn pojazdów itp. Aby uzyskać wysoką niezawodność
pracy minimalizuje się w takich przypadkach możliwość dokonywania zmian
w konfiguracji systemu operacyjnego.
Pod względem środowiska użytego do implementacji systemu można
wprowadzić podział na:
programowe
sprzętowe.
Sprzętowe systemy operacyjne to: sprzętowo programowe rozwiązania
integrowane z wybraną architekturą procesora. W takim przypadku
sprzętowa część systemu przyśpiesza wybrany zakres czynności
wykonywanych przez system (przykładowo przełączania zadań i
zachowywanie ich kontekstu).
Nie ma w pełni adekwatnej definicji systemu operacyjnego. Istnienie systemów
operacyjnych jest uzasadnione tym, że umożliwiają one rozsądne
rozwiązywanie problemu kreowania użytecznego systemu obliczeniowego.
Podstawowym celem systemów komputerowych jest wykonywanie programów
użytkownika i ułatwianie rozwiązywania stawianych przez użytkownika
problemów. Do spełnienia tego celu konstruuje się sprzęt komputerowy.
Ponieważ posługiwanie się samym sprzętem nie jest szczególnie wygodne,
opracowuje się programy użytkowe. Rozmaite programy wymagają pewnych
wspólnych operacji, takich jak sterowanie pracą urządzeń wejścia-wyjścia.
Wspólne funkcje sterowania i przydzielania zasobów gromadzi się zatem
w jednym fragmencie oprogramowania - systemie operacyjnym.
42. Stany procesów i przejścia między nimi w
wielozadaniowym systemie operacyjnym
Proces
- to jedno z najbardziej podstawowych pojęć w informatyce. Z definicji
jest to po prostu egzemplarz wykonywanego programu, jednak każdy nowo
powstały proces otrzymuje unikalny numer, który go jednoznacznie
identyfikuje, tzw. numer
PID
(ang. Process IDentifier).
W celu wykonania programu system operacyjny przydziela procesowi zasoby
(np: pamięć, czas procesora), ale także może być konieczne
współbieżne
wykonywanie pewnych fragmentów programu. Aby to zrealizować program
może zażądać utworzenia określonej liczby
wątków
, wykonujących wskazane
części tego programu - o ich współbieżne wykonanie dba system operacyjny
(albo sam program, wówczas mówi się o
zielonych wątkach
). Wątki współdzielą
prawie wszystkie zasoby zarezerwowane dla danego procesu, wyjątkiem
jest czas procesora, który jest przydzielany indywidualnie każdemu wątkowi.
Za zarządzanie procesami odpowiada
jądro systemu operacyjnego
, sposób ich
obsługi jest różny dla różnych
systemów operacyjnych
. W systemie
operacyjnym każdy proces posiada proces nadrzędny (rodzica), z kolei każdy
proces może, poprzez wywołanie funkcji systemu operacyjnego, utworzyć
swoje procesy potomne. W ten sposób tworzy się swego rodzaju drzewo
procesów.
Każdemu procesowi przydzielone zostają zasoby, takie jak:
procesor
pamięć
dostęp do urządzeń wejścia-wyjścia
pliki
W skład procesu wchodzi:
kod programu
licznik rozkazów
stos
sekcja danych
Stany procesów;
Nowy
(new) - przed uruchomieniem lub po zakończeniu działania
Aktywny
(running, executing)
– zakończono operacje we/wy a jeden z
procesów systemu wykonuje jego kod
Gotowy
(waiting) - jeżeli zakończył operacje we/wy ale nie ma wolnego
procesora
Wstrzymany
(ready) - czekanie na zakończenie operacji we/wy
dozorowanej przez system operacyjny
Zakończony
(terminated) - proces zakończył działanie, lecz wciąż
pozostaje w systemie (np. nie przekazał wyników).
Tworzenie procesów
•
Użytkownik za pomocą powłoki zleca uruchomienie programu, proces
wywołujący wykonuje polecenie fork, lub jego pochodną.
•
System operacyjny tworzy przestrzeń adresową dla procesu oraz
strukturę opisującą nowy proces w następujący sposób:
o
wypełnia strukturę opisującą proces
o
kopiuje do przestrzeni adresowej procesu dane i kod, zawarte w
pliku wykonywalnym
o
ustawia stan procesu na działający
o
dołącza nowy proces do kolejki procesów oczekujących na procesor
(ustala jego priorytet)
o
zwraca sterowanie do powłoki użytkownika
Wykonywanie procesów
Dany proces rozpoczyna wykonywanie w momencie przełączenia przez Jądro
systemu operacyjnego przestrzeni adresowej na przestrzeń adresową danego
procesu oraz takie zaprogramowanie procesora, by wykonywał kod procesu.
Wykonujący się proces może żądać pewnych zasobów, np. większej ilości
pamięci. Zlecenia takie są na bieżąco realizowane przez system operacyjny.
Wykonanie procesu musi przebiegać sekwencyjnie. Proces może przyjmować
tutaj któryś z wyżej wymienionych stanów.
Kończenie procesów
•
Proces wykonuje ostatnią instrukcję - zwraca do systemu operacyjnego
kod zakończenia. Jeśli proces zakończył się poprawnie zwraca wartość 0,
w przeciwnym wypadku zwraca wartość kodu błędu.
•
W momencie zwrotu do systemu operacyjnego kodu zakończenia, system
operacyjny ustawia stan procesu na przeznaczony do zniszczenia i
rozpoczyna zwalnianie wszystkich zasobów, które w czasie działania
procesu zostały temu procesowi przydzielone.
•
System operacyjny po kolei kończy wszystkie procesy potomne w
stosunku do procesu macierzystego.
•
System operacyjny zwalnia przestrzeń adresową procesu. Jest to
dosłowna śmierć procesu.
•
System operacyjny usuwa proces z kolejki procesów gotowych do
uruchomienia i szereguje zadania. Jest to ostatnia czynność wykonywana
na rzecz procesu.
•
Procesor zostaje przydzielony innemu procesowi.
Zombie
Proces zombie to wpis w tablicy procesów opisujący program, którego
wykonanie w systemie operacyjnym zostało zakończone, ale którego
zamknięcie nie zostało jeszcze obsłużone przez proces rodzica. Termin ten
odnosi się zazwyczaj do systemów z rodziny UNIX, gdzie pozostawienie wpisu
zombie tymczasowo zajmującego pozycję w tablicy procesów zapobiega
ponownemu wykorzystaniu danego PIDa i możliwym na skutek tego pomyłkom
programistycznym. Wpisy takie nie dają się wyeliminować poleceniem kill,
czemu prawdopodobnie zawdzięczają swoją złowrogą nazwę.
Wpis zombie znika po odpowiednim wywołaniu funkcji wait(), waitpid() lub
analogicznej przez proces macierzysty. W przypadku zakończenia także
procesu rodzica, w wielu architekturach pozostawione przez niego zombie są
obsługiwane automatycznie przez dziedziczący po nim proces init lub przez
system operacyjny. Chociaż wpisy zombie nie obciążają znacząco komputera,
nieprawidłowo napisany program, który nie obsługuje zakończenia pracy
potomków, może doprowadzić do destabilizacji pracy systemu. Dzieje się tak
gdy cała tablica procesów, zwykle posiadająca sztucznie ograniczony
rozmiar, zostanie zajęta przez wpisy zombie. Hierarchia powiadamiania o
procesach zombie może zostać czasowo zmieniona przez mechanizm ptrace,
czasem prowadząc do utrudnień przy debuggowaniu programów.
Demon
Demonem (ang. daemon czyli duszek) nazywamy proces działający w tle, nie
podlegający sterowaniu z żadnego terminala, uruchamiany zwykle podczas
startu systemu i działający do jego zamknięcia. Demon świadczy zwykle usługi
o charakterze systemowym. Ponieważ demony nie są związane z żadnym
terminalem, wyjściem diagnostycznym dla nich jest zwykle podsystem
dziennika pracy systemu (demon syslogd), zwyczajowo zapisujący komunikaty
w plikach tekstowych. Sterowanie demonami odbywa się poprzez sygnały lub
nazwane środki komunikacji międzyprocesowej. Zakończenie pracy procesu
potomnego powoduje wysłanie do procesu macierzystego sygnału SIGCLD oraz
przejście w stan zwany zombie. Wszystkie zasoby przydzielone procesowi
potomnemu są zwalniane (np. zamykane są pliki), ale pozostaje wpis w liście
procesów, mogący doprowadzi do jej przepełnienia. Wpis ten zostaje usunięty,
o ile proces macierzysty wykona funkcję z rodziny wait. Najprostszą formą jest
funkcja z argumentem typu wskazanie na int, gdzie zapisuje się stan
zakończonego procesu potomnego. Kod powrotu procesu na przykład można
uzyska za pomocą makra WEXITSTATUS (definicja w pliku nagłówkowym
sys/wait.h). Funkcja wait zwraca numer procesu potomnego. Sposób realizacji
wszystkich tych działań jest różny dla różnych systemów operacyjnych.
Definicje niektórych pojęć związanych ze współbieżnością
wątek
W pojedynczym procesie może działać kilka wątków. Każdy wątek wykonuje
kod programu, przy czym różne wątki mogą być w różnych "miejscach" kodu.
Wątki działają w jednej przestrzeni adresowej (należącej do procesu), dlatego
mają wszystkie dostęp do zmiennych globalnych. Każdy wątek ma własny stos,
dlatego każdy wątek ma swoje własne zmienne lokalne.
zasoby krytyczne
Tylko jeden proces może mieć w danej chwili dostęp do danego zasobu
krytycznego. Takim zasobem może być np lista elementów znajdująca się w
pamięci operacyjnej, na której proces chce dokonywać operacji
dodawania/usuwania elementów. Gdyby dwa procesy wykonywały takie
operacje równocześnie to lista prawdopodobnie zostałaby uszkodzona (przy
niekorzystnym zbiegu okoliczności).
sekcja krytyczna
Jest to fragment kodu, w którym dany proces używa zasobu krytycznego. Tak
więc sekcja krytyczna jest zawsze związana z pewnym zasobem krytycznym
(zauważmy że dwa procesy mogą równocześnie przebywać w sekcjach
krytycznych różnych zasobów).
wzajemne wykluczanie
Mechanizm wzajemnego wykluczania musi zapewniać, że tylko jeden proces
może przebywać w sekcji krytycznej związanej z danym zasobem. Zazwyczaj
na początku i na końcu sekcji krytycznej dodaje się pewien protokół wstępny
i protokół końcowy.
zagłodzenie
Grupa procesów oczekuje na zdarzenie. Zdarzenie pojawia się wiele ale "nasz"
proces nigdy nie jest wybierany. Mówimy o tym procesie, że został zagłodzony.
przełączanie procesora, przełączenie kontekstu, wywłaszczenie procesu
Gdy mamy kilka procesów w maszynie z jednym procesorem, to procesor ten
musi obsługiwać "po trochu" wszystkie procesy. Operację w której procesor
przechodzi od jednego procesu do drugiego nazywamy przełączeniem
procesora lub przełączeniem kontekstu. Trzeba wtedy zapamiętać stan (m.in.
wartości rejestrów procesora) jednego procesu i odtworzyć stan drugiego. O
procesie który "stracił" procesor mówimy, że został wywłaszczony.
rodzaje interakcji między procesami
1. Procesy nieświadome swojego istnienia. Pojawia się problem
współzawodnictwa w dostępie do zasobów krytycznych (niezbędna jest
synchronizacja procesów).
2. Procesy współpracujące przez "dzielenie". Może to być dzielenie pliku lub
pamięci operacyjnej.
3. Procesy współpracujące przez wysyłanie komunikatów.
synchronizacja procesów lub wątków
Jest to wstrzymywanie działania procesu lub wątku aby nie dopuścić do "utraty
spójności danych"; np gdy dwa procesy chcą wejść do sekcji krytycznej
związanej z pewnym zasobem to jeden z nich musi być "wstrzymywany" tak
długo aż drugi proces nie opuści sekcji krytycznej.
43. Semafor binarny. Definicja Dijkstry
Współbieżne operacje na pamięci grożą jej desynchronizacją. Zjawisko to
powstaje wtedy, gdy pewna podzielna (wywłaszczalna) operacja modyfikująca
jest wykonywana przez wiele procesów jednocześnie. Po wywłaszczeniu
jednego procesu w jej trakcie, drugi dostaje dane częściowo tylko zmienione -
zwykle nieprawidłowe. Cześć kodu, która musi być wykonywana
niepodzielnie (atomowo) przez co najwyżej jeden z procesów grupy nazywa się
zwykle sekcją krytyczną. Najpopularniejszą metodą zapewniania atomowości
kodu jest zabezpieczenie go semaforem.
Semafor
to jeden ze sposobów komunikacji międzyprocesowej. Semafory
zostały po raz pierwszy opisane przez
Edsgera Dijkstrę
jako istotne rozwinięcie
algorytmu Dekkera. Typowy semafor implementowany jest jako zmienna typu
całkowitego. Semafor binarny może przyjmować tylko jedną z dwu możliwych
wartości: 1 lub 0. Semafory są zwykle implementowane w obszarze jądra
systemu operacyjnego. Pozwala to na zaawansowaną obsługę zadań chcących
uzyskać dostęp do zasobu:
wstrzymywanie ich do czasu zwolnienia semafora powiązanego z danym
zasobem,
wznowienie pracy zadania oczekującego na semaforze,
utrzymywania semafora nawet po zakończeniu zadania, który go
utworzyło.
Na semaforze możemy wykonać dwie operacje:
operacja opuszczenie (P)
operacja podniesienia (V)
Operacja opuszczenia (P),(wait)
polega na zmniejszeniu jego wartości o jeden
jeśli jest to możliwe. Tak więc jeśli semafor ma wartość 1, to wówczas
przyjmuje wartość 0. Jeśli natomiast jest już "wyzerowany", to uważany jest
jako "niedostępny" lub "pusty" a zadanie wykonujące taką operację jest
blokowane.
Operacja podniesienia (V),(signal)
takiego semafora kieruje się zasadą, że jeśli
istnieją procesy wstrzymane w wyniku jego opuszczenia, to jeden z nich jest
wznawiany. W przeciwnym wypadku semafor przyjmuje wartość 1
Definicja klasyczna:
P(S):
zaczekaj a
ż
warto
ść
semafora S > 0; zmniejsz S := S – 1
V(S):
S := S + 1
Definicja "praktyczna":
P(S):
je
ś
li S > 0, to S := S – 1
w przeciwnym razie wstrzymaj proces wykonuj
ą
cy te operacje
V(s):
je
ś
li s
ą
procesy oczekuj
ą
ce na S, to obud
ź
jeden z nich
w przeciwnym razie S := S + 1
Litery P i V zwykle są kojarzone ze słowami holenderskimi:
passeren (przejść), proberen (próbować),
vryjgeven (zwolnić), verhoog (zwiększać).