gulczas 2001 opracowanie, Politechnika Wrocławska - Materiały, architektura komputerow 2, egzamin, opracowania


Motto:

X=[xk-1-β ϕ (xk-1)](β k-1- β -m)+Σxiβ+[xk-1-(β - 1) ϕ(xk-1)] β -m.

prof. Janusz Biernat „Arytmetyka komputerów” 1996

/* w założeniu miało to być tłumaczenie książki prof. Janusz Biernata „Architektura komputerów” na język przyjazny studentowi. Niestety, okazało się to niemal niemożliwe. Tłumaczenie następowało z prędkością ok. 25 stron/dzień, a egzamin był coraz bliżej. Wiele fragmentów jest więc żywcem przepisanych z książki - jest to po prostu streszczenie książki, z nielicznymi komentarzami. Mam nadzieję, że na coś się przyda. Od razu przepraszam za literówki itp. - pisałem szybko i na sprawdzenie tekstu, jak na razie, nie miałem czasu. Niech, w poniedziałek Bóg ma nas w swojej opiece i niech moc będzie z nami. */

1. KOMPUTER I PROGRAM.

1.1. Komputer z programem zintegrowanym. --> [Author:DD]

[ ogólnie o modelu von Neumanna]

Główne części komputera z programem zintegrowanym to: pamięć (memory), centralna jednostka przetwarzająca (central processing unit CPU) i linia je łącząca (connecting tube), zwana współcześnie magistralą.

W modelu von Neumanna zadaniem procesora jest przetwarzanie danych, których jedynym źródłem jest pamięć. Wykonanie rozkazu rozpoczyna się pobraniem z pamięci słowa interpretowanego jako kod instrukcji i obejmuje: pobranie danych z pamięci, przetworzenie i zapis wyniku do pamięci. Chwilowe umieszczenie danych w rejestrach procesora służy zmniejszeniu obciążenia magistrali i nie jest sprzeczne ze schematem przetwarzania. Wynik rozkazu zależy od skutku przetwarzania rozkazów poprzednich.

Zasadniczą rolę pełni pamięć, zorganizowana zgodnie z następującymi zasadami:

Procesor może czytać tylko z pamięci głównej (operacyjne), bo tylko ją adresuje. Aby coś odczytać z innych pamięci (np. wtórnej) należy odpowiednie dane przesłać do pamięci operacyjnej i dopiero z niej odczytać. Pamięć główna charakteryzuje się swobodnym dostępem - znaczy to, że czas przesłania słowa między procesorem a pamięcią nie zależy od lokacji (adresu) tej komórki.

Program jest sekwencją rozkazów, co oznacza, że w chwili wykonywania rozkazu wiemy gdzie znajduje się następny rozkaz (lub możemy to określić). Układ wytwarzający adres następnego rozkazu to licznik rozkazów (program counter). Na zmianę domyślnej kolejności rozkazów pozwalają rozkazy rozgałęzień (branch) i skoków (jump).

Klasyczny model von Neumanna z czasem został nieco zmieniony. Najważniejsze innowacje to:

1.1.1. Komputer jako automat.

[Rozdział mocno zjechany]

Działanie komputera z programem zintegrowanym można porównać do działania automatu skończonego. Stan komputera określa stan pamięci i stan procesora ( a dokładniej jego rejestrów) - ogólnie przyjmuje się, że stan komputera jest konfiguracją pamięci. Zmiana stanu komputera jest skutkiem wykonania rozkazu przez CPU. Rozkaz jest funkcją, która przeprowadza stan wejściowy w stan wyjściowy. Pamięć jest zatem zarówno dziedziną, jak i zbiorem wartości tej funkcji. Należy jednak pamiętać, że komputer w odróżnieniu od innych automatów może zmieniać swoją sekwencję sterowania (chodzi o zmianę kolejności wykonywanych programów - czyli skoki). Automat, który jest w stanie interpretować kody rozkazów, nazywa się maszyną właściwą (mikromaszyna) (chodzi po prosu o procesor, który potrafi jedynie zinterpretować rozkaz, ale nic z nim nie zrobi bez oprogramowania), a ten który przetwarza sekwencje instrukcje na bezpośrednio wykonywane rozkazy zakodowane w pamięci to maszyna wirtualna (komputer z oprogramowaniem).

Cykl procesora - czas potrzebny na zmianę jego stanu.

Cykl pamięci (rozkazowy) - czas potrzebny na zmianę stanu pamięci - może obejmować kilka cykli procesora..

Etapy wykonywania rozkazu są wykonywane w kolejnych cyklach procesora:

1.1.2. Poziomy opisu programu.

Poziomy maszynowe (chodzi o abstrakcyjne poziomy umożliwiające wykonanie programu):

Konwersje między poszczególnymi poziomami:

kompilacja - tłumaczenie całego algorytmu na sekwencje rozkazów procesora. Kod wynikowy musi być załadowany do pamięci. Otrzymujemy optymalny kod. Wymaga dużej ilości pamięci. [.txt(kompilator)->.obj(konsolidator)->.exe(do pamięci i procesora).

interpretacja - osobne tłumaczenie danego polecenia dopiero w momencie jego użycia. Wymaga niewielkiej ilości pamięci, ale jest czasochłonne. Niektóre interpretery generują najpierw kod pośredni (p-kod), który jest następnie natychmiast wykonywany (są to pseudokompilatory)

Z procesami tymi łączy się problem przenośności programów, czyli możliwości wykonania danego kodu na każdej maszynie - rozwiązaniem jest stosowanie na poziomie systemu operacyjnego maszyny wirtualnej - przetwarza ona kod źródłowy na kod wymyślonego procesora, tak zdefiniowanego, aby łatwo można było przejść na kod maszyny docelowej (tak właśnie jest w JAVIE).

1.2. Architektura komputera.

Architektura - sztuka projektowania i konstruowania obiektu.

Kompozycja - architektura, budowa, z czego zbudowane zegarek - tarcza, wskazówki).

Organizacja/konstrukcja - moduł wykonawczy, to co nadaje obiektowi kształt operacyjny (rodzaj napędu i układ przekładni).

Realizacja - wyraz technologiczny organizacji, jakimi metodami wykonane (technologia wykonania balansu i przekładni).

Architektura komputera - zbiór rozkazów i pamięć.

Architektura procesora - opis cech funkcjonalnych komputera na poziomie przepływu danych w centralnej jednostce przetwarzającej CPU.

Cechy przejrzystej architektury:

1.3. Szybkość przetwarzania.

Czas wykonania programu jest wprost proporcjonalny do czasu cyklu procesora Tc (potrzebnego na wykonanie zmiany stanu procesora), zależy od liczby wykonań (cykli) danej instrukcji oraz średniego czasu potrzebnego na wykonanie jednej takiej instrukcji.

Liczba wykonań instrukcji i średni czas jej wykonania są ze sobą ściśle powiązane. W maszynach o bogatym repertuarze instrukcji liczba instrukcji potrzebnych do wykonania danego programu jest stosunkowo niewielka, za to średni czas ich wykonania jest dłuższy, bo są to operacje bardziej skomplikowane.

Średnia liczba cykli potrzebnych na wykonanie instrukcji zależy od złożoności listy rozkazów i organizacji procesora. Im więcej rozkazów mamy do dyspozycji, tym dłużej jest wykonywany proces dekodowania rozkazów i generacji sygnałów sterujących procesorem.

Jednym z głównych powodów zwolnienia całego procesu wykonania rozkazu jest ciągłe odwoływanie się do pamięci (przy pobraniu rozkazu, a także zapisania jego wyniku), gdyż czas dostępu do pamięci jest znacznie dłuższy od cyklu procesora. Działania na pamięci możemy przyspieszyć na kilka sposobów:

Drugim czynnikiem znacząco wpływającą na czas wykonania programu jest organizacja przetwarzania. W maszynach ściśle sekwencyjnych każdy etap wykonania programu wykonywany jest jeden po drugim FDEW (fetch,decode...).

Jednym ze sposób poprawienia wydajności jest oddzielenie od siebie jednostki wykonującej rozkazy od jednostki je pobierającej (jednostka magistrali) i zastosowanie bufora kolejki rozkazów.

Odseparowanie układów wykonujących różne etapy kolejnych rozkazów umożliwia ich współbieżne wykonanie w trybie potokowym. Np. w tym samym momencie można pobierać następny kod rozkazu, dekodować aktualny, wykonywać poprzedni..

Niestety, wydajność potoku spada skutkiem występowania nieuniknionych konfliktów przetwarzania:

Przetwarzanie superpotokowe - dodatkowe podzielenie etapów wykonywania rozkazu na podetapy (np. etap wykonania na 2 części), umożliwia skrócenie cyklu, a to z kolei pozwala na wzrost szybkości przetwarzania. Głębokość potoku nie może być jednak zbyt duża, bo podział etapu wymaga zwiększenia liczby buforów separujących i spowalnia cały proces.

Przetwarzanie superskalarne (skalowane) - wzrost wydajności uzyskany jest poprzez użycie większej ilości jednostek wykonawczych (tzn. równocześnie można wykonywać np. dwie fazy execution), pod warunkiem, że między wykonywanymi rozkazami nie występuje konflikt danych. Innym sposobem jest równoczesne użycie specjalizowanych jednostek np. jednostki stało- i zmiennoprzecinkowej). [rys.2.10]

1.4 Ewolucja architektury komputerów.

CISC (complex instruction set computer) - duża liczba rozkazów, w tym te bardzo skomplikowane, złożone tryby adresowania, mały plik rejestrowy (mało rejestrów). Zauważono, że najbardziej złożone rozkazy (i tryby adresowania) używane są najrzadziej. Poza tym duża ilość rozkazów zwalnia proces ich dekodowania i wykonywania.

RISC (reduced instruction set computer) - mała liczba rozkazów, krótszy czas dekodowania, więcej rejestrów (rzadsza komunikacja za pamięcią). Obecnie istotą architektury RISC jest rozsądne projektowania listy rozkazów (jednolita struktura kodów rozkazów, łatwość dekodowania itd.).

2. ARCHITEKTURA LISTY ROZKAZÓW.

Architektura listy rozkazów jest najniższą warstwą abstrakcyjnego opisu komputera.

2.1 Architektura procesora.

Zasadniczymi elementami funkcjonalnymi architektury komputera są [rys.3.1]:

Jednostka sterująca - zawiera:

W układzie dekodera, stosownie do treść pobranego kodu i bieżącego stanu procesora wytwarzane są sygnały sterujące działanie procesora.

Jednostka wykonawcza - tworzy ją jednostka arytmetyczno logiczna ALU, bufor pamięci MBR i plik rejestrów roboczych R. Jednostka wykonawcza musi posiadać co najmniej jeden rejestr roboczy (do tymczasowego przechowywania argumentów). Informacja o poprawności wyniku umieszczana jest w rejestrze warunków CR. ALU jest zwykle rozdzielona na jednostkę stałoprzecinkową (operacje logiczne i na liczbach całkowitych) i jednostkę zmiennoprzecinkową . Korzystają one zwykle z osobnych plików rejestrowych.

Jednostka adresowa - jej zasadniczą częścią jest układ wytwarzania adresu MAG (memory addres generator). Zawiera on plik rejestrów adresowych MAR , a wśród nich SP - wskaźnik stosu.

Architektury ALU i procesora [rys.3.2]:

  1. akumulatorowa AC - argument i wynik operacji jest umieszczany w tym samym rejestrze (czyli normalnie w AX - akumulatorze). Drugi argument jest odczytywany z pamięci i umieszczany jest w rejestrze buforowym MBR.

Szczególny rodzaj to architektura stosowa - argumenty czytane są ze stosu (ściągane), a wynik wpisywany jest w miejsce wskazywane przez SP (po ściągnięciu argumentów).Taką architekturę mają koprocesory numeryczne INTEL 80x87 i FPU INTEL 80486+.

  1. Rejestr - pamięć R/M. - przynajmniej jeden argument podawany jest z rejestru. Drugi argument podawany jest również z rejestru lub pamięci, tam też umieszczony zostaje wynik działania (miejsce drugiego argumentu pełni rolę akumulatora).

  2. Uniwersalna R+M - argumenty i wynik może znajdować się zarówno w rejestrze jak i w pamięci. Konieczne jest tu użycie kilku rejestrów MBR buforujących pamięć.

  3. Rejestrowa L/S - wszystkie argumenty umieszczone w rejestrach ogólnego przeznaczenia. Wyjątkiem jest operacja kopiowania zawartości rejestru do pamięci, lub pamięci do rejestru (wtedy jednym z argumentów musi być pamięć).

Inaczej wygląda schemat przetwarzania w procesorze potokowym [rys.3.4]

Mamy tutaj z separowanymi stanowiskami przetwarzania: stanowisko pobierania kodu, dekodowania i dostępu do rejestrów, wykonania, przekazywania wyniku. Poza tym mamy już rozdzielenie pamięci podręcznej na pamięć kodu i danych.

W pierwszym etapie, na podstawie obliczonej wartości licznika rozkazów, z bufora kodu IM pobierany jest rozkaz. Następnie bieżąca wartość PC oraz kod rozkazu zostają zatrzaśnięte w buforze separującym. W drugim etapie, kod wpisany do rejestru rozkazów IR zostaje zdekodowany, zostaje odtworzony pełny kod i wpisany do kolejnego bufora separującego. W etapie wykonania zostaje uruchomiona jednostka arytmetyczno-logiczna lub sumator adresów- wynik działania zostaje zapisany w kolejnym buforze separującym. W ostatnim etapie wynik zostaje zapisany do pamięci danych DM lub do odpowiednich rejestrów. W dowolnej chwili w każdym stanowisku może być przetwarzany inny rozkaz, każdy w innym etapie zaawansowania.

2.2 Lista rozkazów.

Specyfikacja każdego rozkazu zawiera:

wynik argument1 [OPERACJA argument2] np. al. (al.)+(dx)

MNEMONIK[.rozmiar] [źródło] [cel] np. inc.b (a6,1) - zwiększa liczbę w bajcie pamięci o adresie (a6+1). Opis funkcji to [(a6+1)][(a6+1)]+1. Instrukcje procesora klasyfikuje się wg liczby argumentów:

- opis funkcjonalny - słowny opis instrukcji np. dodanie do półsłowa o adresie (bx+si) w segmencie es liczby z rejestru dx. (add es:[bx+si],dx).)

W architekturze rejestrowej L/S (RISC) pełny kodu rozkazu ma rozmiar słowa maszynowego (32-bity). Przez to operandy mają bardzo ograniczony zakres. W znaczący sposób ogranicza to rozmiar listy rozkazów i tryby adresowania. Zaletą jest wspólny rozmiar kodu dla wszystkich rozkazów co znacznie przyspiesza proces dekodowania rozkazu. [rys. 3.5]

W architekturze akumulatorowej, rejestr-pamięć i uniwersalnej mamy już różne rozmiary kodów (bo argumenty z pamięci i rejestrów, skomplikowane tryby adresowania) (CISC). Różnorodność rozkazów procesorów CISC wpłynęła na niejednolitość w strukturze kodów rozkazów (chodzi nie tylko o różną długość rozkazów, ale także strukturę części służącej do identyfikacji rodzaju operacji).

W architekturze Intel 80x86 jednostką informacji jest bajt (pozostałość po 8085). Pełny kod instrukcjo realizowanej przez jednostkę stałoprzecinkową 80x86 może zawierać od 1 do 15 bajtów (* dopiero od 80386):

W architekturze Motorola 680x0 pełny kod rozkazu zawiera od 1 do 8 półsłów 16 bitowych w tym:

2.3 Adresowanie danych.

2.3.1 Indeksowania danych.

W architekturze klasycznej pamięć jest uporządkowanym zbiorem komórek, każda komórka zawiera jednostkę informacji zwaną słowem. Informacje, których rozmiar przekracza pojemność słowa, musza być umieszczone w kilku komórkach. Występuje wtedy problem, którą część zapisać jako pierwszą (czy bardziej czy mniej znaczącą). Stosowane są dwie konwencje.

- Big Endian - (BE) - ważniejszy niższy, końcowy wyższy - słowo (bit) zawierające bardziej znaczącą część informacji ma niższy adres. Czyli po prostu zapisujemy do pamięci po kolei idąc od lewej strony. Np. gdy mamy liczbę 4562 to najpierw zapiszemy 45 (niższy adres), a dopiero potem 62 (wyższy adres).(POWERPC)

- Little Endian - (LE) - ważniejszy wyższy, końcowy wyższy - słowo (bit) zawierające mniej znaczącą cześć informacji ma niższy adres. Czyli tak jak normalnie w procesorach Intela - zaczynamy zapisywać do pamięci od części najmniej znaczącej (od prawej) i to ona ma najniższy adres, a część najbardziej znacząca ma adres najwyższy. Np. 4562, 62 -zapisana jako pierwsza, niższy adres.

W architekturze Motorola 680x0 numerowanie bajtów jest zgodne z BE, a numerowanie bitów w konwencji LE.

2.3.2 Rozdzielczość adresowania.

Rozdzielczość adresowania wpływa na konstrukcję kompilatora. Jest to po prostu rozmiar jednostki informacji (słowa). Możliwe jest również (poprzez specjalne rozkazy) działanie na pojedynczych bitach - uzyskujemy wówczas rozdzielczość absolutną.

Ponieważ zasadniczą jednostką jest jednak słowo muszą mu „podlegać” wszystkie mechanizmy wewnętrzne - np. dane systemowe są tworzone w formacie zgodnym z rozmiarem słowa maszynowego, szerokość magistrali danych procesora powinna umożliwiać przesłanie co najmniej jednego słowa maszynowego w jednym cyklu pamięci. W momencie gdy rozmiar magistrali danych umożliwia jednoczesne przesłanie kilku jednostek danych (mniejszych od rozmiaru słowa - np. możemy adresować po bajcie, a słowo jest 2-bajtowe ), pojawia się problem wpasowania. Aby go uniknąć należy przestrzegać prostej zasady - jeśli słowo obejmuje dwie komórki, to adres słowa powinien być parzysty, jeśli cztery to powinien być podzielny przez 4. Tylko takie wpasowanie umożliwia dostęp do danych w jednym cyklu pamięci i nie spowalnia wykonywanego programu.

2.3.3 Przestrzenie adresowe.

Ze względu na sposób adresowania wyróżnia się:

Szczególnym rodzajem danej nie pochodzącej od powyższych źródeł jest identyfikator źródła przerwania.

Aby zapewnić jednoznaczność adresowania przestrzenie adresowe powinny być separowane (fizycznie i logicznie na poziomie architektury rozkazów). Separacja ta powinna wykluczyć możliwość jednoczesnego wskazania różnych danych lub dostępu do danej w różnych obszarach adresowych (po prostu, jeden adres ma wskazywać tylko jedną daną w obszarze).

2.3.4 Odwzorowanie przestrzeni adresowych.

[temat ciężki do opisania - trzeba popatrzeć na rysunki w książce]

Atrybutami separowanej przestrzeni adresowej są: dokładność (rozmiar najmniejszej adresowalnej jednostki danych) oraz zakres adresowania (liczba potencjalnie adresowalnych jednostek). W przestrzeni adresowej niektóre obszary są zastrzeżone przez producenta sprzętu lub przez system operacyjny. Blokada dostępu może być tymczasowa i unieważniana przez przełączenie bloków pamięci dostępnych alternatywnie w tym samym obszarze adresowym . Bloki takie tworzą pamięć przysłoniętą (shadow memory).

W procesorach Intela 80x86 przestrzeń wejścia - wyjścia i przestrzeń pamięci są rozdzielone logicznie i możliwe są różne ich odwzorowania (mapping):

W 486 i Pentium wydzielono dodatkowo przestrzeń stosu zmiennoprzecinkowego (rejestry)( 8 lokacji 80-bitowych). W Pentium MMX i II rejestry te wykorzystywane są również do zastosowań multimedialnych.

[najlepiej sytuacje prezentuje rys.3.10]

W Motorolach 680x0 wydzielono z całej pamięci przestrzeń we-wy i przestrzeń rejestrów koprocesorów.

Podobne rozwiązania są stosowane w architekturze RISC, gdzie oprócz przestrzeni pamięci i przestrzeni sterowania, zdefiniowano osobne przestrzenie rejestrów stało-i zmiennoprzecinkowych.

2.3.5 Tryby adresowania.

Użycie danej wymaga wytworzenia jej adresu rzeczywistego w pamięci głównej, zwanego też adresem fizycznym. Na poziomie programu każdej danej jest przypisany adres logiczny. Współbieżne wykonanie programów (tzn. jednoczesna praca na dwóch aplikacjach korzystających z tych samych zasobów) wymaga nadania każdej danej na poziomie procesu unikatowego adresu wirtualnego zawierającego oprócz adresu logicznego jeszcze identyfikator procesu. Trybem adresowania nazywamy sposób przetworzenia adresu wirtualnego lub logicznego w celu wyznaczenia adresu liniowego. Jeśli dostępna pamięć jest mniejsza od tej, którą jesteśmy w stanie zaadresować, to adres liniowy podlega dodatkowej translacji.

Tryby adresowania ze względu na liczbę składowych wskaźnika adresu:

W adresowaniu jednopoziomowym suma składowych adresu wyznacza lokację danej w pamięci. W adresowaniu dwupoziomowym niektóre składowe adresu są użyte do wskazania lokacji w pamięci zawierającej adres odniesienia (bazę pośrednią), który jest dodawany do pozostałych składowych. Po prostu niektóre składowe nie są dodawane, ale jedynie wskazują inną daną i to ona właśnie ma być użyta w sumowaniu.

Szczególnym typem adresowania wieloargumentowego jest adresowanie względne, w którym adresem bazowym jest zawartość licznika rozkazów.

Najprostszym rodzajem adresowania wieloargumentowego jest adresowanie dwuelementowe, które można podzielić na:

W architekturze Intel 80x86 zaadresowanie każdej danej w pamięci wymaga dwóch wskaźników: segmentu i adresu względnego wewnątrz segmentu o wartości nie przekraczającej rozmiaru segmentu. Wskaźnik segmentu, umieszczony w rejestrze, wyznacza adres liniowy początku segmentu (bazę odniesienia). Najbardziej złożonym trybem adresowania w Intel 80x86 jest dwuwskaźnikowe skalowane adresowanie bazowo-indeksowe z przemieszczeniem:

Adres liniowy = ([wskaźnik segmentu])+(baza)+(indeks)*skala+przemieszczenie

W Motorolach używany jest m.in. mechanizm adresowania dwupoziomowego, używający bazy pośredniej jako odniesienia dla innych składowych. Stosuje się również:

LA = [(baza)+przemieszczenie+(indeks)*skala]+relokacja

[na podstawie bazy, przemieszczenia, indeksu i skali wyznaczany jest drugi adres bazy (baza pośrednia) i potem jest ona traktowana jak zwyczajna baza - dodajemy do niej relokacje (odpowiednik przeniesienia).

LA = [(baza)+przemieszczenie]+(indeks)*skala+relokacja

[baza pośrednia jest wyliczana na podstawie bazy i przemieszczenia - i ta suma jest traktowana jako nowa baza do której później dodawana jest relokacja(przesunięcie) oraz skalowany indeks.

Adresowanie opisowe (deskryptorowe) - jest to metoda adresowania wieloelementowego (adresowanie pośrednie). Rolę bazy pośredniej pełni tutaj deskryptor (realizowany przez wskaźnik) i to do niego dodawana jest baza, przemieszczenie i skalowany indeks. Oprócz adresu bazowego (pośrednia baza) deskryptor zawiera również informacje umożliwiające realizowanie selektywnego dostępu do bloku.. Adresowanie opisowe ułatwia elastyczne adresowanie zmiennych strukturalnych przy jednoczesnym zachowaniu prywatności (np. w pamięci wirtualnej. Podstawowo różnica między adresowaniem postindeksowym, a deskryptorowym jest to, że w dwupoziomowym adresowaniu pośrednim (post...) adres bazowy zawarty jest rejestrze, a w deskryptorowym jest to pełny adres wirtualny danej.

Adresowanie wieloelementowe jest charakterystyczne dla architektury CISC. W maszynach RISC-owych najbardziej złożonym trybem jest adresowania skalowane bazowo-indeksowe.

Adresowanie danych strukturalnych - w praktyce sprowadza się do adresowanie stosu i kolejki. W przypadku stosu do adresowania wykorzystywany jest wskaźnik wierzchołka stosu. Takie adresowanie wymaga modyfikacji wskaźnika po działaniach na stosie. Mamy tu dwie sytuacje - jeśli stos jest rozbudowywany w kierunku adresów malejących to mamy tryb predekrementacji wskaźnika (dla przesłań na stos) i postinkrementacji (dla odczytu ze stosu) [ogólnie rzecz biorą stos rośnie w dół]. Mechanizm odwroty jest używany w obrębie stosu tworzonego w kierunku adresów rosnących.[stos rośnie w górę]

Drugim typem strukturalnym jest kolejka (bufor LIFO). Jej adresowanie wymaga dwóch wskaźników - początku i końca kolejki (lub rozmiaru kolejki). Mechanizm ten jest stosowany jedynie przy tworzeniu tzw. bufora rozkazów. Inne mechanizmy wspomagające adresowanie danych strukturalnych: sprawdzanie uprawnień dostępu do danych, testowanie zakresu (gdy mieści się w nim adres), adresowanie pól w rekordach.

2.4. Projektowanie listy rozkazów.

Projektowanie listy rozkazów rozpoczyna się zwykle od określenia repertuaru rozkazów i trybów adresowania, których użycie wydaje się intuicyjnie konieczne. Czujemy również, że sens ma tworzenie rozkazów bardziej skomplikowanych od samego warunkowego kopiowania (które notabene wystarczy) oraz tworzenia nieco bardziej skomplikowanych trybów adresowania, chociażby po to aby uzyskać dostęp do danych strukturalnych.

Analiza częstości używania danych rozkazów pokazuje, że istnieje tylko pewien repertuar rozkazów intensywnie używanych. Na poziomie języków wyższego rzędu są instrukcje warunkowe (if..then..else), wywołania funkcji, pętle i przypisania. Na poziomie architektury rzeczywistej najczęściej używa się instrukcji rozgałęzień i pętli, instrukcji arytmetycznych i logicznych oraz porównań. Rzadziej używamy rotacji, przesunięć i operacji na polach bitowych. Instrukcje kopiowania typu move są szczególnie często używane w architekturze CISC, ze względu na mała ilość rejestrów. Nie możemy jednak określać listy rozkazów jedynie po częstości ich używania - niektóre rozkazy, pomimo tego, że są rzadko używane, są całkowicie niezbędne (chociażby rozkazy ochrony danych czy trybu wirtualnego). Podobnie sprawa wygląda z trybami adresowania - praktyka pokazuje, że najczęściej używamy adresowania pośredniego bazowo-indeksowego oraz rejestrowego z modyfikacją (w tym adresowanie stosu). Na potrzeby ochrony danych niezbędna jest również implementacja adresowania deskryptorowego.

Koncepcja architektury list rozkazów.

Tworzenie architektury listy rozkazów wymaga rozstrzygnięcia kilku zasadniczych problemów:

Ponadto rozkazy powinny mieć jednolitą strukturę kodu (spójność), używać jednakowych typów i formatów danych (ortogonalność) i obsługiwać jednakowe tryby adresowania. Należy dokonać wnikliwej analizy spójności (z innymi założeniami projektowymi, takimi jak sposób obsługi wyjątków i przerwań), ortogonalności i przejrzystości.

Struktura kodu rozkazu

Przejrzystość wstępnie zaprojektowanej architektury rozkazów ułatwia projekt struktury kodowania. W architekturze ortogonalnej można bowiem zdefiniować strukturę kodu rozkazu jako złożenie pól bitowych określających:

Niestety nie jest możliwe ustalenie jednego formatu dla wszystkich instrukcji - nie pozwalają na to chociażby rozkazy rozgałęzień, wymagające innych argumentów niż np. rozkazy arytmetyczne. Prowadzi to do ustalenia kilku standardowych formatów rozkazów (np. dla arytmetycznych), jednak z zachowaniem maksymalnej spójności ich struktury. Wspólne dla wszystkich rozkazów powinny być przynajmniej pola określające rodzaj działania, a dla rozkazów używających podobnych argumentów także pola wskazujące argument docelowy i jeden argument źródłowy.

3. DANE I DZIAŁANIA.

[rozdział nie będzie opisany w całości, większość została już przerobiona na „Arytmetyce komputerów” i miejmy nadzieję, że rozdziała ten nie będzie obowiązywał na egzaminie.]

W architekturze CISC standardowe słowo ma 16 bitów, zatem złożenie dwóch bajtów nazywamy słowem. W maszynach klasy RISC, gdzie typowe słowo maszynowe ma 32 bity, termin słowo oznacza złożenie 4 uporządkowanych bajtów.

Wszystkie dane przetwarzane przez komputer można zaklasyfikować do jednej z trzech grup:

3.1. Kody rozkazów.

Kody rozkazów zawierają informację określającą rodzaj operacji i identyfikatory argumentów tych operacji. Dla danej architektury jest ustalona struktura i rozmiar kodu, będący wielokrotnością elementarnej jednostki informacji. Zwykle na kilku bardziej znaczących bitach słowa jest kodowany rodzaj lub klasa operacji (np. arytmetyczne ), kolejne pola bitowe identyfikują argumenty, pozostała część słowa zawiera informacje dodatkowe. Np. w Intel 8080 grupę operacji wskazywały 2 najbardziej znaczące bity jednobajtowego słowa maszynowego, znaczenie kolejnych bitów było różne zależnie od rodzaju operacji. W współczesnych procesorach, których słowo maszynowe jest zwykle 32-bitowe, pole rodzaju działań, identyfikujące rodzaj informacji, jest zwykle 6-bitowe, kolejne pola 5- lub 6-bitowe są używane do wskazania operandów w pliku rejestrowym , znaczenie pozostałych bitów słowa zależy od rodzaju rozkazu wskazanego w polu identyfikującym typ operacji.[rys.4.1]

W praktyce cecha ortogonalności rozkazów może być zachowana tylko w grupach rozkazów (bo przecież operacje arytmetyczne i np. skoków korzystają z innych argumentów), czego skutkiem są różne formaty kodów. W momencie gdy długość słowa była niewystarczająca do zakodowania wszystkich informacji (w starych procesorach) wprowadzano kody złożone, w których pierwsze słowo udostępniało dodatkową przestrzeń kodową, a właściwy kod udostępniało dopiero drugie słowo. Niestety takie działanie zwiększa złożoność dekoderów i wydłuża etap dekodowania.

3.2. Dane systemowe.

Podczas wykonywania programu niezbędne jest pamiętanie informacji kontekstowej, umożliwiającej zapewnienie spójności programu, szczególnie podczas współbieżnej realizacji programów. Dane te są wytwarzane przez procesor i mają ustaloną strukturę. Typowe to oczywiście stos i kolejka (opisane już wcześniej). W przypadku stosu może wystąpić jego przepełnienie (stack overflow) (ponieważ rozmiar stosu jest ograniczony), lub wyczerpanie (stack underflow), podczas próby pobrania z pustego stosu. Jeśli chodzi o kolejkę to po dodaniu do niej nowego elementu zwiększany jest jej wskaźnik końca, przy pobraniu zwiększany jest wskaźnik początku. Przy pustej kolejce wskaźniki początku i końca pokazują na to same miejsce w pamięci. Jeśli rozmiar kolejki jest ograniczony, to, niezależnie od wartości wskaźnika końca, może nastąpić jej wyczerpanie, gdy wskaźnik czoła przekroczy wartość maksymalną. Zapobiega temu zapętlenie kolejki, któremu odpowiada obliczanie bieżących wskaźników czoła i końca modulo rozmiar kolejki. Może wówczas nastąpić przepełnienie kolejki, jeśli wskaźnik czoła po kolejnym zapełnieniu osiągnie wartość równą wartości wskaźnika końca.

[??????????????cos się chyba komuś w tej książce pomyliło????????????????]

Dane systemowe mogą być też zorganizowane w formie bloków o strukturze narzuconej rodzajem informacji, zwanych segmentami lub tablicami systemowymi (np. kontekst procesora obejmujący zapis „zamrożonego” stanu procesora.

3.3. Dane użytkowe.

Najbardziej różnorodny jest repertuar typów danych użytkowych definiowanych przez projektanta algorytmu. Można wyróżnić trzy zasadnicze typy takich danych:

  1. skalarne - używane do ilościowego opisu wielkości jednowymiarowych

- logiczne

- znakowe

- opisowe

- porządkowe, często utożsamiane z naturalnymi

- całkowite

- wymierne - stałoprzecinkowe lub ułamkowe

- zmiennoprzecinkowe

- logarytmiczne

  1. strukturalne - uporządkowane zestawy danych skalarnych

  1. wskaźnikowe - definiują dane, występujące w operandach w trybach adresowania, lokalizujące obiekt. Dzielimy je na:

Należy pamiętać przy tym, że w rzeczywistej maszynie o architekturze klasycznej, żadnej jednostce informacji nie jest przypisany typ, etykietowanie danych jest bowiem sprzeczne z koncepcja komputera z programem zintegrowanym. Typ danej zawartej w słowie maszynowym jest zawsze odczytywany na podstawie przypisania rozkazu i jego argumentów. Przypisanie takie nie musi być jednoznaczne - należy więc sprawdzić poprawność wyniku na poziomie generowania kodu. Standardowe typy argumentów implikowanych kontekstowo (których typ możemy ustalić analizując powiązanie rozkazu z argumentami) to: typ całkowity, porządkowy (naturalny), logiczny i zmiennoprzecinkowy ( w niektórych architekturach również znakowy, i typ pixel ).

4. PRZEPŁYW STEROWANIA.

W projektowaniu architektury listy rozkazów zakłada się zwykle, że każdy rozkaz jest niezależną jednostką. Czasami jednak przydaje się rozpatrzyć zależności między poszczególnymi rozkazami, gdyż może okazać się to przydatne w niektórych konstrukcjach programowych. Typowymi przykładami instrukcji funkcjonalnie zależnych są instrukcje repeat i execute. Instrukcja repeat zawiera specyfikacje poleceń, które należy powtórzyć oraz liczbę powtórzeń lub warunek zakończenia, execute natomiast, wskazuje polecenie które jest faktycznie wykonywane.

4.1. Rozgałęzienia.

W komputerze z programem zintegrowanym domyślnym porządkiem wykonania instrukcji jest kolejność ustalona w programie. Uporządkowana struktura programu implikuje zależność lokacyjną rozkazów, może być ona : łańcuchowa (każda instrukcja dodatkowo zawiera adres następnej instrukcji - dodatkowe miejsce), lub sekwencyjna (wykonywane po kolei, jak są w pamięci - niestety, nie umożliwia sterowania przebiegiem programu). Nietrudno zgadnąć, że kompromisem jest przyjęcie jako zasady zależności sekwencyjnej oraz dopuszczenie jako wyjątku zależności łańcuchowej, która umożliwi sterowanie. Naruszenie liniowego porządku instrukcji przez realizację zależności łańcuchowej jest skutkiem decyzji. Decyzje zależą od warunków, a ich podjęcie przebiega w trzech etapach: wytworzenie warunku, wybór warunku i użycie warunku. Wytworzenie warunku jest skutkiem wykonania działania, zwykle arytmetycznego lub logicznego. Najczęściej podstawą podjęcia decyzji (źródłem warunku) jest porównanie. Porównanie polega zwykle na sprawdzeniu przynależności do zakresu, poprawności wyniku lub zgodności ze wzorcem. Kod warunku zazwyczaj jest umieszczany w przestrzeni warunków: w rejestrze warunków (flagowym) lub wyjątków. Dzieje się tak przeważnie po wykonaniu operacji arytmetycznych lub logicznych. Zazwyczaj decyzja jest podejmowana na podstawie ustawień rejestru flag dokonanych przez operację poprzedzającą warunek. W PowerPC stan rejestru warunków może być ustalony wcześniej.

Użycie warunku może nastąpić na trzy sposoby:

Wykonanie decyzji przez użycie wybranego warunku realizują rozkazy warunkowe, implementowane na poziomie architektury maszyny rzeczywistej jako rozgałęzienia, zwane też skokami warunkowymi. Rozgałęzienie może być wykonane jako skok ze śladem (na stosie odkładany jest adres instrukcji następnej po rozgałęzieniu). Pułapka (trap) jest rozgałęzieniem realizowany w trybie obsługi wyjątku. Wytworzenie : cmp AX,BX

Rozgałęzienie: jng adres [loop adres]

Zapamiętanie : setng zmienna

Kopiowanie : cmovcc AX,BX (warunkowe kopiowanie, gdy nie spełnione omija tylko jedną

instrukcję)

Pułapka: into

„Budowa” rozgałęzienia wymaga wskazania adresu docelowego. Użycie adresów względnych ogranicza zakres skoku, lecz ułatwia relokację kodu. Dla rozgałęzień typu pułapka i przerwanie lepsze jest użycie adresów bezwzględnych, bo lokalizacja procedury obsługi wyjątku może być znana przed rozgałęzieniem i niezależna od miejsca i czasu wystąpienia wyjątku.

W architekturach RISC poszczególne elementy instrukcji warunkowej są często zintegrowane jako pojedyncza instrukcja „porównaj i skocz” ( w MIPS R2000, bcc arg1,arg2,adres).

W procesorach klasy CISC warunki są wyrażane jako funkcje stanu rejestru kodów warunkowych (chodzi o FLAGS), modyfikowanego zwykle przez instrukcję wykonaną bezpośrednio przed rozkazem rozgałęzienia. [ tutaj należy obejrzeć Tablicę 5.3. Warunki w procesorach klasy CISC).

Szczególnym rodzajem warunku jest wynik testowania licznika powtórzeń, umieszczonego w jednym z rejestrów procesora (CX) - następuje zliczanie w kierunku zera, zatrzymanie gdy 0 ( w Motorolach gdy -1). W procesorach Intela wykorzystuje to np. instrukcja LOOP i REP.

Rozgałęzienia w językach programowania.

[ bardzo prostu temat więc omówię w skrócie, w większości na przykładach programów]

if A>B then polecenie

mov eax,A

cmp eax,B

jle koniec ; alternatywą zapisującą wynik operacji logicznej jest setle B

call polecenie

koniec:

case i,n ((i<n)=>polecenie[i],(i>n)=>poleceni[0])

;(nie implementowana na poziomie listy rozkazów) operacja przebiega dwuetapowo - najpierw w rejestrze procesora zostaje umieszczony indeks ;wariantu , który jest następnie użyty jako argument rozkazu skoku pośredniego, wskazujący adres ;docelowy skoku w tablicy adresów

cmp bx,N

jb mniejszy

xor bx,bx

mniejszy:

shl bx,2

call ds:[bx]

;w architekturze RISC nie mamy niestety rozkazów skoku pośredniego - koniecznej jest wówczas przekazywanie adresu pośredniego przez stos lub rejestr połączenia.

for licznik:=wartość_pocz step przyrost(-1) until limit do polecenie

mov cx, limit

sub cx,wartość_pocz-1

powt:

polecenie()

loop powt

;w architekturze Motoroli licznik rozkazów może być umieszczony w rejestrze danych D#, ;kontynuowanie wykonania pętli może być uzależnione od spełnienia dodatkowego warunku, tak ;jak zwykłych rozkazach rozgałęzień. Podobnie jest w PowerPC, natomiast w MIPS nie ma ;w ogóle pętli.

while warunek(A>B) do polecenie

start:

mov eax,b

cmp eax,a

jle skip

polecenie()

jmp start

skip:

repeat funkcja until warunek(A>B)

start:

polecenie()

mov eax,A

cmp eax,B

jg start

4.2. Funkcje i procedury.

[niestety, tutaj zaczynają się schody]

W kategoriach abstrakcyjnych poziomów architektury można wyróżnić cztery poziomy hierarchii w programie: moduły pełniące rolę funkcji, funkcje złożone z instrukcji wykonujących elementarne działania i mikrooperacje tworzące instrukcje, wykonywane w cyklach maszynowych.

Podział programu na moduły, wykonujące odrębne funkcje, ułatwia implementację algorytmu, bo moduły mogą być traktowane jak rozbudowane pojedyncze instrukcje. Moduły, łatwiejsze do testowania i tłumaczenia na kod maszynowy, mogą mieć również strukturę modularną. W efekcie tego cały program może być zorganizowany jako hierarchiczna struktura funkcji realizowanych przez moduły. Stosowanie funkcji daje wiele korzyści:

Strukturalizacja kodu może jednak utrudniać optymalizację kodu, ponieważ musimy się zajmować zmiennymi lokalnymi, wywoływaniem i powrotem z funkcji, jak również sama budowa hierarchiczna zwiększa czas wykonania programu. Dodatkowo wywołanie funkcji wywołuje duże zaburzenia w przetwarzaniu potokowym.

Wynikiem działania funkcji jest wartość (zwykle liczba) albo wskaźnik obiektu, przekazywany przez rejestr lub stos. Procedury to funkcje, które nie zwracają wartości. W językach strukturalnych wyższego rzędu opis funkcji (procedury) ma postać bloku, który zaczyna deklaracja stałych i zmiennych lokalnych. Następnie zapisywany jest adres powrotu do funkcji wywołującej oraz stan procesora.[rys.5.1] Zgodnie z koncepcja hierarchicznej struktury modułów, wywołanie funkcji może również nastąpić z poziomu funkcji wywołanej (zagnieżdżenie) - chodzi po prostu o to, że wywołana funkcją wywołuje następną funkcję lub samą siebie (rekurencja). Funkcje mogą być całkowicie lub częściowo odizolowane od reszty programu, bo mogą operować jedynie na swoich zmiennych lokalnych. Dane te nie są dostępne dla funkcji wywołującej, mogą być udostępniane funkcjom wywoływanym z poziomu, na którym je zadeklarowano (tzn. ze zmiennych lokalnych mogą korzystać jedynie funkcje wewnętrzne). Ten mechanizm umożliwia przesłonięcie (shadowing) i nakładanie (overlapping) zmiennych, czyli użycie jednakowych nazw zmiennych lokalnych i globalnych. Część programu, w której nazwa jest widoczna, nazywa się zasięgiem.

Przekazywanie parametrów do funkcji odbywa się zazwyczaj przez referencję (gdy przekazujemy wskaźnik obiektu) lub wartość (przekazujemy konkretny obiekt np. liczbę).Identyfikatory użyte w definicji funkcji do wskazania obiektów, przekazywanych przez funkcję wywołująca nazywa się parametrami formalnymi (np. funkcja( int c)), a obiekty przekazywane to parametry bieżące (aktualne) (funkcja (3)). Wewnątrz programu jest również konieczne dokonanie powiązań ,czyli ustalenie jednoznacznej relacji między nazwami symbolicznymi (nazwami zmiennych), a wartościami (stałe z wartościami, zmienne z adresami, parametry formalne z aktualnymi ( pierwszemu na liście parametrów formalnych jest przypisywany pierwszy z listy parametrów aktualnych - funkcja(int a, int c), funkcja (2,3) a=2,c-=3 itd.). Powiązania statyczne są dokonywane podczas kompilacji (na podstawie analizy tekstu programu) lub ładowania programu. Podczas kompilacji dokonywane są wczesne powiązania statyczne - dotyczy to wszystkich stałych.

Powiązania dynamiczne, realizowane podczas wykonywania programu, dotyczą tylko zmiennych. Są one stosowane w technikach programowania obiektowego, gdzie wskaźnik obiektu może być określony dopiero podczas wykonania programu (dlatego łatwo może je wykonywać interpreter, ale nie kompilator).

W dowolnym etapie wykonania programu można określić listę nazw zmiennych i stałych, będących w zasięgu danej funkcji. Lista tych nazw nazywa się środowiskiem wykonania lub kontekstem funkcji. Kontekst funkcji może się zmieniać podczas jej różnych wywoływań.

Wywołanie każdej funkcji wymaga najpierw odwzorowania używanych zmiennych w pamięci. Jest dosyć trudne na poziomie języka maszynowego. Do przechowywania zmiennych stosuje się więc stos (stosowany jest też mechanizm okien rejestrowych). Ponieważ zasięg zmiennych i stałych deklarowanych w obrębie jednej funkcji jest jednakowy, więc mogą być one odwzorowane w spójnym bloku pamięci. Zwykle jest on tworzony w chwili uaktywnienia funkcji i jest nazywany blokiem aktywacji. Każdy blok musi być umieszczony w jakimś miejscu pamięci. Alokacja statyczna bloku aktywacji, wykonana podczas kompilacji, pozwala na użycie adresów bezwzględnych, jednakże wyklucza rekurencję oraz jednoczesne udostępnienie funkcji różnym procesom, każdej funkcji możemy bowiem przydzielić tylko jeden blok aktywacji. Alokacja dynamiczna oznacza przydział pamięci na czas wykonania funkcji i jego unieważnienie po jej zakończeniu. Może być dokonana albo od razu dla całego bloku (w momencie wywołania funkcji), albo indywidualnie dla poszczególnych zmienny (w chwili użycia jej identyfikatora - jest to jednak czasochłonne i rzadko stosowane - używana jest w językach strukturalnych i realizowana w postaci drugiego stosu zwanego stertą programową ). Przydzielony blok musi być zwolniony przed zwolnieniem bloków przydzielonych wcześniej (czyli tak jak stos). W obszarze stosu, w którym umieszczono blok aktywacji, można wyróżnić w kolejności odpowiadającej rozbudowie stosu, obszary:

Wskaźniki DL i SL są różne tylko przy rekurencyjnym wykonywaniu programu - SL wskazuje blok funkcji nadrzędnej, DL lokalizuje blok aktywacji poprzedniego wywołania funkcji.

Blok aktywacji utworzony ostatnio w obszarze stosu nazywa się ramką stosu, a jego położenie określa wskaźnik ramki FP. Podczas tworzenia nowego bloku aktywacji wskaźnik ramki FP jest pamiętany jako wskaźnik powiązania dynamicznego DL. Wskaźnik ramki jest również używany jako adres bazowy w adresowaniu zmiennych lokalnych.

W procesorach RISC jest stosowany także inny mechanizm powiązania funkcji zwany oknami rejestrowymi. Na każdym spośród P poziomów zagnieżdżenia dostępnych jest n rejestrów, w tym s rejestrów globalnych, dostępnych na każdym poziomie zagnieżdżenia, 2 lokalne zestawy po k rejestrów transferowych dostępnych na sąsiednich poziomach oraz jeden zestaw rejestrów dostępnych lokalnie. Indeksy rejestrów globalnych i lokalnych są na każdym poziomie jednakowe, a rejestry transferowe są przeindeksowywane po każdym wykonaniu funkcji, lub po jej zakończeniu. [trochę tego nie rozumiem, więc nie będę się starał tłumaczyć]. Zaletą stosowania okien rejestrowych jest szybkość przekazywania parametrów, wadą jest ograniczona pojemność pliku rejestrowego.

Zasadą powinno być umożliwienie wielokrotnego użycia funkcji bez potrzeby odnawiania przy każdym wykonaniu. W najprostszym przypadku będzie to możliwe, jeśli funkcja nie modyfikuje własnego kodu poza obszarem parametrów przekazywanych. Warunek ten nie jest jednak wystarczający, jeśli funkcja ma być ponawialna, czyli, gdy wymaga się, aby możliwe było wywołanie funkcji niezależne od innych jej wywołań. Szczególnym rodzajem takiej funkcji jest funkcja rekurencyjna. Szczególnie ważny w przypadku funkcji ponawialnych jest przydział pamięci - zadanie to musi realizować funkcja wywołująca (!) lub system operacyjny. Na poziomie architektury procesora są stosowane dwa mechanizmy wspomagania wywołania funkcji i procedury:

Mechanizm wywołania funkcji możemy symulować poprzez wywołanie podprogramu połączone z czynnościami niezbędnymi do utworzenia bloku aktywacji.

Przekazywanie parametrów funkcji jest realizowane za pośrednictwem stosu, przez przekazywanie wskaźnika listy parametrów, lub też przez rejestry.

Ważne rozkazy to : push, pop, call, ret, reti, pusha, popa (do zapamiętania kontekstu), enter (utworzenie ramki stosu), leave (usunięcie ramki stosu).

5. SYSTEM OPERACYJNY.

[welcome in hell]

System operacyjny możemy uznać, za maszynę wirtualną umożliwiającą łatwiejszą implementację algorytmów. Zapewnia ponadto kontrolę przydziału oraz ochrony zasobów systemu, takich jak czas procesora, przestrzeń pamięci itd. Ponadto system operacyjny zajmuje się synchronizacją procesów, obsługiwaniem wyjątków, zarządzaniem pamięcią oraz obsługą urządzeń we/wy. Wdrożenie systemu operacyjnego wprowadza niewielki (chyba nie Winxxx) narzut czasowy (wydłużenie czasu wykonywania programów) i przestrzenny (wykorzystywanie niewielkiego obszaru pamięci) na wykonanie programu.

5.1. Klasyfikacja systemów operacyjnych i ich funkcje.

Systemu operacyjne ogólnego przeznaczenia, zależnie od sposobu wykonania ich głównych funkcji, można podzielić na cztery zasadnicze typy:

Funkcje systemu operacyjnego.

Funkcje użytkowe:

Funkcje systemowe (zadanie niezbędne do poprawnego działania):

5.2. Model procesowy systemu operacyjnego.

W momencie, gdy mamy do dyspozycji tylko jeden procesor (lub liczba wykonywanych procesów jest większa niż liczba procesorów) konieczne jest okresowe przełączanie wykonywanych programów. Przy dużej szybkości przełączania uzyskuje się złudzenie, że programy wykonywane są równolegle. Do opisu mechanizmów związanych z wykonywaniem funkcji zarządzania równoległością i innych usług służy model procesowy systemu operacyjnego. W modelu tym przyjmuje się, że wszystkie funkcje systemu i wszystkie programy użytkowników mogą być wykonywane równolegle. Program w chwili uruchomienia staje się procesem. Każdy proces może być uruchomiony na osobnym procesorze, lub dzielić zasoby jednego, jeśli pozwala na to system operacyjny. Bardzo ważne jest przyjęcie reguł harmonogramowania (szeregowania) procesów, które określają, w jakiej kolejności należy je uruchamiać, zamykać, wstrzymywać. W systemie operacyjny procesowym muszą być też określone reguły tworzenia nowych procesów i usuwania zakończonych. Działanie systemu rozpoczyna się od uruchomienia procesu inicjującego (macierzystego), który z kolei może utworzyć nowy, niezależny proces poprzez tzw. wywołanie systemowe (system call). Wygląda to mniej więcej tak [rys.6.2]:

    1. Utworzenie nowego procesu potomnego (syscall)

    2. Umieszczenie procesu na liście harmonogramowania (start)

    3. Uruchomienie przez układ harmonogramujący