Mechanizmy stosowane w systemach operacyjnych - system dydaktyczny

Podobne dokumenty
Simulator of Operating System

Prezentacja systemu RTLinux

4. Procesy pojęcia podstawowe

Poniższe funkcje opisane są w 2 i 3 części pomocy systemowej.

IPC: Kolejki komunikatów

Mechanizmy z grupy IPC

4. Procesy pojęcia podstawowe

Działanie systemu operacyjnego

Działanie systemu operacyjnego

SYSTEMY OPERACYJNE: STRUKTURY I FUNKCJE (opracowano na podstawie skryptu PP: Królikowski Z., Sajkowski M. 1992: Użytkowanie systemu operacyjnego UNIX)

Działanie systemu operacyjnego

Projektowanie oprogramowania systemów PROCESY I ZARZĄDZANIE PROCESAMI

Działanie systemu operacyjnego

Jądro systemu operacyjnego

Komunikacja za pomocą potoków. Tomasz Borzyszkowski

SYSTEMY OPERACYJNE I SIECI KOMPUTEROWE

4. Procesy pojęcia podstawowe

Programowanie współbieżne Wykład 2. Iwona Kochańska

Budowa systemów komputerowych

Szeregowanie zadań w Linux Kernel 2.6. Daniel Górski Przemysław Jakubowski

dr inż. Jarosław Forenc

Od uczestników szkolenia wymagana jest umiejętność programowania w języku C oraz podstawowa znajomość obsługi systemu Linux.

5. Model komunikujących się procesów, komunikaty

Systemy operacyjne III

Procesy, wątki i zasoby

znajdowały się różne instrukcje) to tak naprawdę definicja funkcji main.

Przykłady implementacji planowania przydziału procesora

Materiały pomocnicze 1

SYSTEMY OPERACYJNE WYKLAD 6 - wątki

Wprowadzenie do systemów operacyjnych

Wprowadzenie. Dariusz Wawrzyniak. Miejsce, rola i zadania systemu operacyjnego w oprogramowaniu komputera

dr inż. Konrad Sobolewski Politechnika Warszawska Informatyka 1

Wprowadzenie. Dariusz Wawrzyniak. Miejsce, rola i zadania systemu operacyjnego w oprogramowaniu komputera

przydziału procesora Przykłady implementacji planowania przydziału procesora Wykład prowadzą: Jerzy Brzeziński Dariusz Wawrzyniak

Struktura i funkcjonowanie komputera pamięć komputerowa, hierarchia pamięci pamięć podręczna. System operacyjny. Zarządzanie procesami

Systemy operacyjne. Wprowadzenie. Wykład prowadzą: Jerzy Brzeziński Dariusz Wawrzyniak

SYSTEMY CZASU RZECZYWISTEGO - VxWorks

Instrukcja do laboratorium Systemów Operacyjnych. (semestr drugi)

Podstawy programowania. Wykład Funkcje. Krzysztof Banaś Podstawy programowania 1

Programowanie Współbieżne. Komunikacja między procesowa IPC

Klient-Serwer Komunikacja przy pomocy gniazd

Planowanie przydziału procesora

Wykład 3. Procesy i wątki. Wojciech Kwedlo, Wykład z Systemów Operacyjnych -1- Wydział Informatyki PB

SYSTEMY OPERACYJNE I laboratorium 3 (Informatyka stacjonarne 2 rok, semestr zimowy)

Kolejki FIFO (łącza nazwane)

Systemy operacyjne. Paweł Pełczyński

Zarządzanie procesami i wątkami

Planowanie przydziału procesora

Systemy wbudowane. Systemy operacyjne czasu rzeczywistego

SYSTEMY OPERACYJNE WYKLAD 6 - procesy

Systemy operacyjne. Systemy operacyjne. Systemy operacyjne. Program wykładów. Strona WWW przedmiotu: Program ćwiczeń projektowych

Podstawy Informatyki Systemy operacyjne

LEKCJA TEMAT: Zasada działania komputera.

SYSTEMY OPERACYJNE. kik.pcz.czest.pl/so. (C) KIK PCz Materiały pomocnicze 1 PROWADZI: PODSTAWOWA LITERATURA: ZAJĘCIA: STRONA

Temat zajęć: Mechanizmy IPC: kolejki komunikatów.

Struktura systemu operacyjnego. Opracował: mgr Marek Kwiatkowski

Zarządzanie procesorem

Podstawy informatyki. System operacyjny. dr inż. Adam Klimowicz

System operacyjny System operacyjny

System operacyjny MACH

Definicja systemu operacyjnego (1) Definicja systemu operacyjnego (2) Miejsce systemu operacyjnego w architekturze systemu komputerowego

Procesy. Systemy Operacyjne 2 laboratorium. Mateusz Hołenko. 9 października 2011

1.1 Definicja procesu

Zarządzanie w systemach i sieciach komputerowych. Dr inż. Robert Wójcik. Wykład 3. Zarządzanie przydziałami procesora w systemach komputerowych

Metody obsługi zdarzeń

Wielozadaniowość w systemie Microsoft Windows

Instrukcja do laboratorium Systemów Operacyjnych (semestr drugi)

Czujniki obiektowe Sterowniki przemysłowe

Autor: dr inż. Zofia Kruczkiewicz, Programowanie aplikacji internetowych 1

Procesor ma architekturę rejestrową L/S. Wskaż rozkazy spoza listy tego procesora. bgt Rx, Ry, offset nand Rx, Ry, A add Rx, #1, Rz store Rx, [Rz]

Aplikacja Sieciowa wątki po stronie klienta

Program jest więc strukturą statyczną zapisaną na jakimś nośniku. Natomiast proces jest wykonującym się programem.

Jeśli chcesz łatwo i szybko opanować podstawy C++, sięgnij po tę książkę.

Mikroprocesor Operacje wejścia / wyjścia

Laboratorium Systemów Operacyjnych

Mariusz Rudnicki PROGRAMOWANIE SYSTEMÓW CZASU RZECZYWISTEGO CZ.1

Przerwania, polling, timery - wykład 9

Pamięć wirtualna. Przygotował: Ryszard Kijaka. Wykład 4

Mechanizmy pracy równoległej. Jarosław Kuchta

Procesy i wątki. Blok kontrolny procesu. Proces. Proces - elementy. Stan procesu

Podstawy Programowania Obiektowego

System operacyjny komputera Informacje podstawowe

POSIX ang. Portable Operating System Interface for Unix

System plików. Warstwowy model systemu plików

Pamięć współdzielona

Księgarnia PWN: Włodzimierz Stanisławski, Damian Raczyński - Programowanie systemowe mikroprocesorów rodziny x86

Podstawowe zagadnienia

Mechanizmy z grupy IPC

System komputerowy. System komputerowy

J. Ułasiewicz Programowanie aplikacji współbieżnych 1

Wprowadzenie do systemu Minix

Dodatek B. Zasady komunikacji z otoczeniem w typowych systemach komputerowych

Proces y i y w i ąt ą ki

Adresowanie obiektów. Adresowanie bitów. Adresowanie bajtów i słów. Adresowanie bajtów i słów. Adresowanie timerów i liczników. Adresowanie timerów

UNIX: architektura i implementacja mechanizmów bezpieczeństwa. Wojciech A. Koszek dunstan@freebsd.czest.pl Krajowy Fundusz na Rzecz Dzieci

Uniwersytet Zielonogórski Instytut Sterowania i Systemów Informatycznych. Ćwiczenie 3 stos Laboratorium Metod i Języków Programowania

Struktura systemów komputerowych

Systemy operacyjne oparte na mikrojądrze na przykładzie Minix3. Maciej Łaszcz, Wojciech Łowiec, Patryk Spanily 2 XII 2008

Instytut Teleinformatyki

Łącza nienazwane(potoki) Łącza nienazwane mogą być używane tylko pomiędzy procesami ze sobą powiązanymi.

Transkrypt:

POLITECHNIKA CZĘSTOCHOWSKA WYDZIAŁ INŻYNIERII MECHANICZNEJ I INFORMATYKI KATEDRA INŻYNIERII KOMPUTEROWEJ PRACA MAGISTERSKA Mechanizmy stosowane w systemach operacyjnych - system dydaktyczny Sebastian Świerczyna nr albumu: 30041 kierunek: Informatyka specjalność: Inżynieria oprogramowania i Systemy Informatyczne promotor: dr inż. Jarosław Bilski Częstochowa 2004 1

Spis treści WSTĘP...6 1. CEL PRACY...7 2. WPROWADZENIE DO SYSTEMÓW OPERACYJNYCH...9 2.1 STRUKTURY SYSTEMÓW...10 2.1.1 Struktura jednolita...10 2.1.2 Struktura warstwowa...11 2.1.3 Struktura klient serwer...13 2.2 ALGORYTMY SZEREGOWANIA ZADAŃ...14 2.2.1 Linux...17 2.2.2 Windows NT...20 2.3 WYBRANE FUNKCJE SYSTEMOWE...23 2.3.1 Komunikacja międzyprocesowa...23 2.3.2 Obsługa urządzeń...25 2.4 FUNKCJE W SYSTEMACH UNIX......26 2.4.1 Tworzenie procesów...27 2.4.1.1 fork...27 2.4.1.2 execve...28 2.4.2 Komunikaty...30 2.4.2.1 msgget...30 2.4.2.2 msgsnd...31 2.4.2.3 msgrcv...32 2.4.3 Semafory...34 2.4.3.1 semget...34 2.4.3.2 semop...35 2.4.4 Operacje na urządzeniach wejścia wyjścia...38 2.4.4.1 open...38 2.4.4.2 close...39 2.4.4.3 read...40 2.4.4.4 write...41 2.4.4.5 lseek...42 2.5 FUNKCJE W SYSTEMACH WINDOWS NT...43 2.5.1 Tworzenie nowych procesów...44 2.5.1.1 CreateProcess...44 2.5.1.2 CreateProcessAsUser...47 2.5.2 Komunikaty...48 2.5.2.1 SendMessage...48 2.5.2.2 SendNotifyMessage...49 2.5.2.3 GetMessage...49 2.5.2.4 PeekMessage...51 2.5.3 Semafory...52 2

2.5.3.1 CreateSemaphore...52 2.5.3.2 WaitForSingleObject...53 2.5.3.3 WaitForMultipleObject...55 2.5.3.4 RelaseSemaphore...57 2.5.4 Operacje na urządzeniach wejścia wyjścia...58 2.5.4.1 CreateFile...58 2.5.4.2 ReadFile...61 2.5.4.3 WriteFile...62 2.5.4.4 SetFilePointer...63 3. OPIS WEWNĘTRZNEGO JĘZYKA...66 3.1 PODSTAWOWE INFORMACJE NA TEMAT JĘZYKA...66 3.1.1 Bloki instrukcji...66 3.1.2 Komentarze...67 3.2 TYPY DANYCH, DEKLAROWANIE I OPERACJE NA NICH...67 3.2.1 Typy zmiennych...... 68 3.2.2 Deklarowanie zmiennych...68 3.2.3 Operatory...69 3.2.4 Konwersje między typami zmiennych...70 3.3 INSTRUKCJE STERUJĄCE...71 3.3.1 Instrukcja warunkowa - if...71 3.3.2 Instrukcja iteracyjna - for...72 3.4 FUNKCJE BIBLIOTECZNE...73 3.4.1 Funkcje arytmetyczne...74 3.4.2 Obsługa konsoli...74 3.4.2.1 WRITE...74 3.4.2.2 READ...75 3.4.3 Obsługa komunikacji międzyprocesowej...76 3.4.3.1 SEND...76 3.4.3.2 BLOCK_SEND...77 3.4.3.3 RECEIVE...78 3.4.3.4 BLOCK_RECEIVE...79 3.4.3.5 MSG_RESULT...79 3.4.4 Obsługa semaforów...80 3.4.4.1 CREATE_SEMAPHORE...80 3.4.4.2 RELASE_SEMAPHORE...81 3.4.4.3 OPEN_SEMAPHORE...81 3.4.4.4 WAIT...82 3.4.4.5 SIGNALIZE...83 3.4.4.6 WAIT_MULTIPLE...84 3.4.4.7 SEM_RESULT...86 3.4.5 Obsługa urządzeń...88 3.4.5.1 MAKE_DEVICE...89 3.4.5.2 RELASE_DEVICE...90 3.4.5.3 OPEN_DEVICE...91 3

3.4.5.4 CLOSE_DEVICE...92 3.4.5.5 WRITE...93 3.4.5.6 CLEAR_DEVICE...94 3.4.5.7 DEV_CLEAR...94 3.4.5.8 SEEK...95 3.4.5.9 POS...96 3.4.5.10 SIZE...97 3.4.6 Procesy i ich obsługa...97 3.4.6.1 CREATE_PROCESS...97 3.4.6.2 GET_MY_ID...99 4. INSTRUKCJA OBSŁUGI PROGRAMU...100 4.1 INSTALACJA I URUCHOMIENIE...100 4.1.1 Instalacja...100 4.1.2 Uruchomienie...101 4.2 GŁÓWNE OKNO SYSTEMU...102 4.2.1 Panel sterowania...104 4.2.2 Główne menu systemu... 105 4.2.2.1 Menu Procesy...106 4.2.2.2 Menu Semafory...108 4.2.2.3 Menu Urządzenia...108 4.2.2.4 Menu Narzędzia...109 4.3 TWORZENIE, KOMPILACJA I URUCHAMIANIE PROGRAMÓW...110 4.3.1 Tworzenie i kompilacja...110 4.3.2 Uruchamianie...111 4.4 WIZUALIZACJA OBIEKTÓW...113 4.4.1 Wizualizacja procesów...114 4.4.1.1 Okno procesu...114 4.4.1.2 Eksplorer procesów...117 4.4.2 Wizualizacja semaforów...118 4.4.2.1 Okno semafora...118 4.4.2.2 Eksplorer semaforów...119 4.4.3 Wizualizacja urządzeń...119 4.4.3.1 Okno urządzenia...119 4.4.3.2 Eksplorer urządzeń...120 4.4.4 Wykres czasowy...122 4.4.5 Zawartość i przeglądanie katalogu domowego symulatora...123 4.5 ZASTOSOWANE ALGORYTMY SZEREGOWANIA ZADAŃ...125 4.5.1 Algorytm domyślny...125 4.5.2 Algorytm z podwyższaniem priorytetów...126 5. PODSUMOWANIE I WNIOSKI...127 6. LITERATURA...129 4

7. DODATEK. SPIS ZAWARTOŚCI DOŁĄCZONEJ PŁYTY CD...130 8. SPIS ILUSTRACJI...131 9. SPIS TABEL...133 5

Wstęp System operacyjny [8] jest elementem pośredniczącym pomiędzy sprzętem, a aplikacjami uruchamianymi przez użytkownika. Od właściwości systemu operacyjnego zależy w ogromnej mierze wygoda użytkowania komputera Ponieważ dzisiejsze, powszechnie używane systemy operacyjne [13] są tworami niezwykle skomplikowanymi, tak naprawdę niewiele osób zdaje sobie sprawę, co właściwie dzieje się wewnątrz systemu. Większość procesów wewnątrz systemu musi zachodzić bardzo szybko, co jest dodatkowym utrudnieniem w prowadzeniu obserwacji. Dodatkowym problemem podczas prowadzenia badań, jest dążenie twórców systemów operacyjnych do jak największego odizolowania użytkownika od wszystkiego, o czym przeciętna osoba korzystająca z komputera nie musi wiedzieć. Wszystkie te czynniki prowadzą do tego, że bardzo ciężko jest zaobserwować zjawiska, które mają miejsce w codziennym użytkowaniu komputera. Szczególnie założenie mówiące o jak największym odciążeniu użytkownika od niepotrzebnych obowiązków - stoi zazwyczaj w zupełnej sprzeczności łatwością badania wnętrza systemu. Z pomocą dla dociekliwego użytkownika na pewno mogą przyjść różnorakie publikacje, omawiające systemy operacyjne [3,2]. Niemniej jednak tego typu książki i artykuły mają jedną zasadniczą wadę: nie dają możliwości doświadczalnego zbadania tego wszystkiego, co ma miejsce wewnątrz systemu. Zbadanie tych wewnętrznych zjawisk jest bardzo pomocne przede wszystkim dla programistów, którzy będą pisać aplikacje, które muszą w jakiś sposób współdziałać z innymi procesami uruchomionymi w systemie. Przeprowadzenie symulacji pozwala zaobserwować, w jaki sposób procesy mogą komunikować się między sobą, a także jak uzyskują dostęp do urządzeń wejścia/wyjścia. Osoby przeprowadzające tego typu badania mogą dowiedzieć się, w jaki sposób należy synchronizować współdziałające procesy, jak dbać o poprawny przydział zasobów niepodzielnych, aby nie doprowadzić do niepożądanych sytuacji typu zakleszczenie. Przeprowadzenie tego typu symulacji i doświadczeń, będzie z pewnością wartościowe także dla każdego, kto jest zainteresowanym tematyką systemów operacyjnych. 6

1. Cel pracy Celem pracy było stworzenie aplikacji umożliwiającej symulowanie zjawisk zachodzących wewnątrz systemu operacyjnego. Na etapie analizy wymagań stawianej przed projektem przyjęto następujące założenia: 1) Aplikacja ma w przejrzysty sposób obrazować następujące zjawiska: komunikowanie się procesów za pomocą wiadomości, operacje na semaforach oraz urządzeniach np.: tworzenie, usuwanie, przenoszenie procesu pomiędzy kolejkami systemowymi, działanie algorytmów szeregujących zadania. 2) Wyposażenie aplikacji w własny, wewnętrzny pseudo język programowania, który umożliwia wykonywanie powyższych operacji. Składnia języka powinna być wystarczająco prosta, przy czym na tyle elastyczna, by móc budować bardziej rozbudowane programy. Składnia języka powinna być zbliżona do którejś z powszechnie stosowanych w językach programowania. 3) Dostarczenie odpowiedniego narzędzia umożliwiającego tworzenie, kompilowanie oraz składowanie własnych programów. 4) Umożliwienie przeprowadzania symulacji w dwóch trybach: Trybie krokowym, pozwalającym obserwować krok za krokiem wykonywanie kolejnych instrukcji. W tym przypadku użytkownik wyzwala kolejne takty, Trybie ciągłym, dającym możliwość przeprowadzenia symulacji, w której badany jest stan, jaki osiągnie system po wykonaniu określonego zbioru rozkazów. Wyzwalaniem taktów zajmuje się wbudowany moduł aplikacji. Takty generowane są w ustalonych przez użytkownika odstępach czasu, bądź bez jakichkolwiek opóźnień. 5) Stworzenie intuicyjnego, graficznego interfejsu użytkownika, z uwzględnieniem internacjonalizacji. 7

6) Maksymalne uproszczenie procedury instalacji i uruchomienia aplikacji na komputerze użytkownika. 7) Umożliwienie pracy aplikacji na różnych platformach sprzętowych i programowych. 8) Dostarczenie użytkownikowi dokumentacji opisującej obsługę aplikacji, oraz dokładnego opisu składni wewnętrznego języka programowania. 9) Dostarczenie przykładów napisanych w wewnętrznym języku, obrazujących charakterystyczne problemy z dziedziny systemów operacyjnych typu: pięciu filozofów, pisarze i czytelnicy [8]. 8

2. Wprowadzenie do systemów operacyjnych System operacyjny (ang. operating system) - jest zbiorem programów pośredniczących pomiędzy użytkownikiem a sprzętem. Ładowany jest do pamięci na początku pracy komputera. System operacyjny ma bardzo specyficzny charakter - w przeciwieństwie do programów użytkowych aplikacji, nie korzysta z żadnych gotowych funkcji, wręcz przeciwnie, jest programem, który takie funkcje udostępnia. Więc od własności systemu zależą rzeczywiste możliwości wykorzystania komputera. Zadaniem systemu operacyjnego jest: zarządzanie zasobami komputera, m.in.: procesorem (czasem procesora), pamięcią, urządzeniami peryferyjnymi oraz przydzielanie zasobów procesom, koordynacja pracy w/w urządzeń poprzez obsługę przerwań oraz odpowiednie na nie reagowanie, umożliwienie uruchamiania i zakańczania zadań umieszczenie ich w pamięci przed uruchomieniem i usunięcie ich po zakończeniu, ochrona danych i pamięci tak, aby jeden proces, w wyniku błędu lub zamierzonego działania nie mógł zniszczyć lub pozyskać danych innego procesu, zapewnienie mechanizmów do komunikacji międzyprocesowej, zapewnienie mechanizmów do synchronizacji procesów. Dodatkowe funkcje systemu: zarządzanie zasobami w taki sposób, by z systemu mogło korzystać wielu użytkowników oraz by każdy użytkownik mógł wykonywać w tym samym czasie wiele zadań, udostępnienie systemu plików i zarządzanie nim, system powinien sygnalizować użytkownikowi wszelakie błędy sprzętowe, błędy w samym systemie oraz w programach użytkownika, W dalszej części rozdziału zostaną omówione struktury systemów operacyjnych (rozdział 2.1). W dalszej części poruszane są zagadnienia związane z algorytmami szeregowania zadań (rozdział 2.2). Rozdział zakończony jest podrozdziałem omawiającym 9

zagadnienia: tworzenia nowych procesów, komunikacji międzyprocesowej oraz operacji na urządzeniach wejścia/wyjścia. Każdy z tych aspektów jest przedstawiony w ujęciu dwóch najpopularniejszych platform systemowych: systemów wywodzących się z rodziny UNIX(rozdział 2.4), system Windows NT firmy Microsoft(rozdział 2.5) Rozważania na temat tych zagadnień są poprzedzone wstępem teoretycznym, zawartym w rozdziale omawiającym teorie dotyczącą komunikacji międzyprocesowej oraz obsługi urządzeń wejścia/wyjścia (rozdział 2.3) 2.1 Struktury systemów Współcześnie spotykamy wiele odmian systemów operacyjnych. Istnieje zatem wiele kryteriów, według których można je sklasyfikować. Można je podzielić dla przykładu ze względu na to gdzie są instalowane - systemy przeznaczone na komputery typu mainframe; stosowane w komputerach osobistych, czy takie używane w urządzeniach typu PDA, a nawet w telefonach komórkowych. Można je również podzielić na systemy wielozadaniowe lub jednozadaniowe, a także ze względu na ilość użytkowników, którzy mogą równocześnie korzystać z systemu (systemy wielodostępne / jednoużytkownikowe). Jednak wśród tych wszystkich kryteriów istnieje jedno, którego omówieniu warto poświęcić więcej czasu mianowicie podział systemów ze względu na wewnętrzną strukturę. W dalszej części rozdziału omówione są trzy podstawowe struktury stosowane w systemach operacyjnych [2][8]. 2.1.1 Struktura jednolita Systemy tego typu należą do najstarszych i obecnie nie są już w praktyce stosowane (poza systemami czasu rzeczywistego). System posiadający taką strukturę jest właściwie jednym, wielkim programem. Sprawia to, że bardzo ciężko jest budować bardziej złożone systemy tego typu. Powodowane to jest tym, że wprowadzenie zmian w jakiejś funkcji może powodować problemy w zupełnie innych częściach systemu, które pozornie wydają się niezależne od tej funkcji. Jak już wspomniano, tego typu strukturę stosuje się jedynie w systemach czasu rzeczywistego. Systemy czasu rzeczywistego mają zastosowanie tam, gdzie istnieją surowe wymagania na czas wykonania operacji lub przepływu danych, dlatego używa się ich często jako sterowników w urządzeniu o ściśle określonym celu. 10

Przykładowe systemy czasu rzeczywistego: ecos, MicroC/OS-II, OS-9, OSE, OSEK/VDX, psos, QNX, RSX-11, RT-11, RTOS-UH, VRTX, VxWorks, Windows CE. 2.1.2 Struktura warstwowa Innym sposobem tworzenia systemu jest podzielenie go na moduły, które połączone są w warstwy. Każda z warstw ma inne zadanie i jest uzależniona od warstwy znajdującej się poniżej. Struktura taka wpływa korzystnie na zmniejszenie zależności pomiędzy modułami, co z kolei powoduje na łatwość utrzymania systemu i bezproblemowość wprowadzania zmian oraz rozszerzeń. Warstwa M Nowe funkcje Warstwa M-1 Ukryte funkcje Istniejące funkcje Rys. 1. Warstwowa struktura systemu Przedstawicielem systemów tego typu jest system T.H.E [10]. Stworzony został do celów naukowych właśnie po to, by przedstawić modelowy system operacyjny, bazujący na strukturze warstwowej. Warto dodać, że właśnie w tym systemie po raz pierwszy zaimplementowano semafory. 11

Tabela 1. Warstwy systemu operacyjnego T.H.E. Warstwa Zadania Warstwa 4 procesy użytkownika W tej warstwie działają procesy uruchamiane przez użytkowników Tworzenie wirtualnych urządzeń Warstwa 3 po jednym procesie na każde fizyczne urządzenie Obsługa przerwań pochodzących z programów obsługi urządzeń Obsługa przerwań pochodzących z menadżera pamięci Obsługa przerwań pochodzących od procesu obsługującego konsole Synchronizacja żądań pochodzących od wyższych warstw Warstwa 2 Proces obsługujący konsole jeden proces Synchronizacja żądań pochodzących od wyższych warstw Obsługa przerwań pochodzących od konsoli Warstwa 1 Utworzenie wirtualnej przestrzeni adresowej jeden proces Synchronizacja żądań pochodzących od wyższych warstw Tworzy wirtualny procesor Warstwa 0 Dostarcza usług IPC Obsługuje przerwania zegarowe Innym przykładem systemu, który zbudowany jest w architekturze warstwowej może być któryś z rodziny UNIX [6]. System taki składa się z następujących warstw: jądro systemu dostarczające mechanizmy bezpieczeństwa, zarządzania zasobami współdzielonymi takimi jak czas procesora, czy też pamięć, warstwa fizyczna oferująca dostęp do fizycznych urządzeń. Przykładowo chcąc uzyskać dostęp do przestrzeni dyskowej, należy użyć funkcji, w której należy podać parametry typu: napęd/głowica/cylinder/sektor, warstwa logiczna oferuje dostęp na wyższym poziomie abstrakcji. Za pośrednictwem tej warstwy mamy dostęp do urządzeń logicznych. Przykładowo funkcja zwracająca kod wciśniętego klawisza, zwraca nam informacje w postaci kodu ASCII, procesor komend dostarcza metod dla użytkowników, za pomocą których mogą uruchamiać własne programy, 12

programy, aplikacje programy zapewniające użytkownikowi wygodną obsługę systemu typu menadżer okien, narzędzia dyskowe itp. Programy takie są (a przynajmniej powinny być) niezależne od fizycznych zasobów maszyny. Aplikacje Jądro Narzędzia Warstwa Warstwafizyczna fizczna Demony (usługi) Warstwa logiczna Procesor komend Powłoka Rys. 2. Struktura systemu UNIX Innymi systemami posiadającymi tę strukturę są: RC-4000, VME, VMS. 2.1.3 Struktura klient serwer Inne podejście do tworzenia struktury sytemu polega na podziale sytemu na moduły, które spełniają pewne wyodrębnione zadania [3]. Jednakże moduły nie są rozmieszczone w warstwach, ale traktowane są bardziej równorzędnie. Komunikacja między modułami zachodzi zawsze za pośrednictwem centralnego programu obsługi komunikatów (mikrojądra). Komunikacja oczywiście może zachodzić w obie strony. Moduł wysyłający początkowy komunikat nazywany jest klientem (ang. client), moduł odbierający ten komunikat serwerem (ang. server). Rozwiązanie takie ma zalety podobne do zalet struktury warstwowej, ale zwiększa izolację, zwiększając zarazem niezależność między modułami. Przykładem systemu o takiej strukturze jest system Windows NT. 13

Aplikacja POSIX Aplikacja Win32 Serwer POSIX Serwer Win32 Komunikaty Komunikaty JĄDRO Rys. 3. Struktura systemu Windows NT Innym systemami posiadającymi architekturę mikrojądra są: AIX, GNU Hurd, Minix, MorphOS, QNX, RadiOS, BeOS. 2.2 Algorytmy szeregowania zadań We współczesnych systemach możemy mieć równocześnie uruchomionych kilka, a nawet kilkaset procesów. Każdy z tych procesów korzysta z jakichś zasobów systemowych. Prawie zawsze mamy do czynienia z sytuacją, w której uruchomionych procesów jest, aniżeli dostępnych w systemie zasobów. Nie możemy więc pozwolić sobie na działanie typu bierz, kiedy chcesz. W systemie muszą więc istnieć pewne mechanizmy, dzięki którym będzie możliwe współdzielenie tychże zasobów. Należy tutaj zaznaczyć, że pod pojęciem zasobu należy rozumieć nie tylko urządzenia wejścia/wyjścia, ale także czas procesora. Dzięki umiejętności dzielenia czasu procesora pomiędzy procesy, system stwarza wrażenie, że równocześnie wykonuje wiele operacji. Od algorytmu określającego w jaki sposób dzielony jest procesor, w znacznej mierze zależy ogólna wydajność systemu. Przed przystąpieniem do omawiania algorytmów szeregowania zadań, nie sposób nie wspomnieć o podstawowych pojęciach [8] związanych z tym tematem. Podstawowym pojęciem, które wiąże się tym zagadnieniem jest dyspozytor (ang. dispatcher). Dyspozytor nazywany jest czasem planistą niskiego poziomu (ang. low - level scheduler), a jego zadaniem jest przydzielanie procesora centralnego do różnych procesów w systemie. Do dyspozytora przechodzi się wówczas, gdy bieżącego procesu nie można (lub nie powinno się wykonywać, bo np. pojawił się ważniejszy proces) nadal wykonywać. 14

Działanie dyspozytora jest proste i przebiega w następujący sposób: 1) zapamiętaj środowisko ulotne bieżącego procesu w jego deskryptorze, 2) znajdź najodpowiedniejszy proces i odtwórz jego środowisko ulotne na podstawie deskryptora. Aby wybrać odpowiedni proces należy zebrać wszystkie procesy i uporządkować według jakiegoś priorytetu. Przypisywanie priorytetów nie jest zadaniem dyspozytora, ale planisty (planista zostanie omówiony poniżej), 3) przekaż sterowanie do nowo wybranego procesu, do miejsca wskazywanego przez jego odtworzony licznik rozkazów. Drugim istotnym pojęciem, o którym przed momentem wspomniano jest planista (ang. Scheduler). Jego zadaniem jest ogólne ustalenie, kiedy można wprowadzać nowe procesy do systemu i w jakiej kolejności powinny one działać. Właśnie to wybieranie najodpowiedniejszego procesu do wykonywania jest algorytmem szeregowania procesów. Jak już wspomniano, głównym celem algorytmu planowania jest takie zorganizowanie pracy wykonywanej przez system operacyjny, aby można było zwiększyć wartość pewnej miary zadowolenia użytkownika. W zależności od rodzaju sytemu miara ta może być rozmaicie określana, np. w systemach interakcyjnych ważnym czynnikiem jest czas reakcji systemu na działania użytkownika. Natomiast w systemach służących do wykonywania rozbudowanych obliczeń, istotnym jest maksymalne skrócenie czasu ich wykonywania. Oczywiście cel, który ma zostać zrealizowany, będzie mieć wpływ na wybór zastosowanego algorytmu szeregowania zadań. 15

Nowe procesy Procesy częściowo wykonane KOLEJKA PROCESORA Procesy wykonywalne Procesy wykonywane Procesy wybrane przez dyspozytora Procesy zablokowane Zamówienie może być zrealizowane KOLEJKI POD SEMAFORAMI PROCESORY CENTRALNE Procesy wykonane Proces zgłosił zamówienie, które nie może zostać zrealizowane Rys. 4. Ogólny model planowania zadań Poniżej przedstawiono kilka najpopularniejszych algorytmów: Najpierw najkrótsza praca jak sugeruje nazwa algorytmu, kolejka uporządkowana jest według czasu potrzebnego na wykonanie zadania. Ten algorytm jest odpowiedni tylko dla systemów wsadowych, w których czas wykonywania poszczególnych zadań można oszacować na podstawie opisu zadania. W podstawowej wersji proces, który rozpocznie działanie musi się zakończyć nim zacznie być przetwarzany kolejny. Spotykamy również modyfikacje tego algorytmu, która umożliwia wywłaszczenie procesu, jeśli czas jego wykonania jest dłuższy niż maksymalny, jednorazowy czas przeznaczony pojedynczego procesu. Algorytm rotacyjny przeznaczony do systemów, gdzie trzeba szybko reagować na zamówienia. Każdemu procesowi w systemie przydziela się ustalony, jednakowy czas na wykonywanie zamawianej przez niego usługi. Po tym proces trafia na koniec kolejki. Wadą tego algorytmu jest to, że w przypadku bardzo dużej ilości procesów wydajność systemu drastycznie maleje, gdyż koszt przełączania procesów jest zbyt 16

duży. Metodą zapobiegania tego typu sytuacjom, jest stopniowe zwiększanie czasu przydzielanego procesom, wraz ze wzrostem ich liczby. Kolejka dwupoziomowa algorytm ten jest modyfikacją algorytmu omówionego powyżej. Procesy, które nie ukończyły swojego działania w kilku kolejnych kwantach czasu przenoszone są do drugiej kolejki. Ta druga kolejka może być obsługiwana zarówno za pomocą algorytmu rotacyjnego, jak również metodą FIFO, jednak podstawową zasadą jest obsługa drugiej kolejki w momencie, gdy zabraknie zadań do wykonania w pierwszej. Opisane algorytmy stanowią podstawę dla najbardziej popularnych strategii planowania. Takie rzeczywiste algorytmy szeregowania zadań zostały omówione w dwóch następnych podrozdziałach, na podstawie dwóch systemów: Linux i Windows NT. 2.2.1 Linux Algorytm szeregowania procesów, wraz z pakietem funkcji wspierających operacje wyboru i przełączania zadań, stanowi krytyczną część podsystemu zarządzania procesami w systemie Linux [6]. Od jego jakości bezpośrednio zależą: szybkość, wydajność i współbieżność wszelkich czynności wykonywanych przez system operacyjny, a tym samym przez użytkownika. Zbiór reguł pozwalających ustalić jak, kiedy i komu przydzielać czas procesora, nazywany jest polityką szeregowania. Linux działa generalnie zgodnie z zasadą podziału czasu (ang. time sharing), co oznacza, iż każdy proces otrzymuje procesor jedynie na pewien okres, określany mianem kwantu czasu. Wspomnianej regule nie podlegają jedynie procesy szeregowane w trybie SCHED_FIFO, co zostanie omówione nieco dalej. W szczególności podział czasu sprawia, iż proces, w większości przypadków nie wykonuje całego zadania w jednym, spójnym przedziale czasu. Jego działanie może zostać przerwane i wznowione po chwili w niezmienionym stanie - nie wymaga to aplikowania żadnego dodatkowego kodu dla wykonującego się zadania. Niewidoczność pracy szedulera jest dla procesów korzyścią, uzyskaną dzięki obsłudze przerwań i operacjom zmiany kontekstu. Polityka szeregowania odnosi się także do pojęcia priorytetu procesu oraz klasyfikuje procesy posiadające wspólne, wyróżniające cechy. Priorytet, a także dynamika jego zmian, jest kluczowym czynnikiem, zapewniającym współbieżne i sprawiedliwe działanie procesów. 17

W systemie Linux są zaimplementowane następujące klasy procesów: idle - w ten sposób nazywa się proces określany inaczej jako proces jałowy. Ma on najniższy priorytet w systemie i otrzymuje procesor tylko wtedy, gdy żaden inny proces go nie potrzebuje. Z chwilą nadejścia dowolnego procesu, traci natychmiast procesor. Czas procesora poświęcony na wykonywanie tego procesu pozwala ocenić natężenie jego użycia. Im jest on dłuższy, tym obciążenie procesora mniejsze, procesy zwykłe - do tej klasy zalicza się znakomita większość procesów. System Linux pozwala na szeregowanie tych procesów jedynie według jednej polityki szeregowania, ale jest ona na tyle elastyczna, że umożliwia znaczne zróżnicowanie poziomów uprzywilejowania to: SHED_OTHER zostanie procesów omówiona tej poniżej). klasy Co ważne, (polityka ich ta priorytety są dynamiczne, tzn. zmieniają się wraz z długością używania procesora przez proces. Dzięki temu system zwiększa sprawiedliwość i zapobiega głodzeniu procesów procesy czasu rzeczywistego - są to procesy, które wymagają bardzo szybkiej, natychmiastowej reakcji systemu. Jeżeli ona nie nastąpi może dojść do, w najlepszym przypadku, niepożądanego zwolnienia ich pracy (np. w programach odtwarzających obraz wideo). W najgorszym razie może to spowodować zagrożenie życia ludzkiego (np. w systemach chłodzenia elektrowni atomowych). Choć Linux nie jest systemem dedykowanym do tego typu zastosowań, zawiera implementację procesów czasu rzeczywistego. Procesy te mają zawsze pierwszeństwo przed procesami zwykłymi, tzn. w chwili, gdy proces czasu rzeczywistego będzie potrzebował procesora, a będzie on w posiadaniu procesu zwykłego, ten ostatni zostanie natychmiast wywłaszczony. Oczywiście, tak duże możliwości procesów czasu rzeczywistego mogą w łatwy sposób doprowadzić do destabilizacji systemu (np. poprzez stworzenie takiego procesu wykonującego nieskończoną pętlę). Dlatego procesy czasu rzeczywistego może tworzyć jedynie administrator systemu. Podobnie jak w przypadku procesów zwykłych, także w obrębie tej klasy występuje podział na procesy bardziej lub mniej uprzywilejowane. Jednak w odróżnieniu od tamtej klasy mają priorytet statyczny, tzn. niezmieniający się z powodu używania procesora przez proces. Priorytety te mają wartości od 1 do 99, przy czym wyższy numer oznacza wyższy priorytet. Procesy takie, szeregowane są według polityk SCHED_FIFO i SCHED_RR, które zostaną omówione poniżej. 18

Algorytm szeregujący dzieli czas procesora na epoki (ang.. epoch). Na początku epoki dla każdego procesu obliczany jest kwant czasu. Oznacza on maksymalny czas procesora, jaki w danej epoce może wykorzystać proces. Kiedy proces wykorzysta cały kwant, zostaje wywłaszczony. Każdy proces może być przydzielany do procesora wielokrotnie podczas jednej epoki, do czasu, gdy nie skończy się jego kwant. Na przykład, jeżeli proces zostanie zawieszony w oczekiwaniu na jakiś zasób, zachowuje pewną część swojego kwantu i może zostać wybrany do wykonywania jeszcze w tej samej epoce. Epoka kończy się, gdy wszystkie procesy gotowe do wykonania wykorzystały swój kwant czasu. Wówczas algorytm szeregujący oblicza ponownie kwant dla każdego procesu - rozpoczyna się nowa epoka. Wartość kwantu wyliczana jest na podstawie następującego wzoru: kwant=priorytet statyczny niewykożystane jednostki czasu 2 Polityki szeregowania procesów w linuksie: SCHED_FIFO - w tej polityce proces, który otrzyma procesor może utracić go tylko w wypadku, gdy: zgłosi żądanie wejścia-wyjścia, które nie może zostać natychmiast zrealizowane (jest wtedy usuwany z kolejki procesów gotowych), proces z kolejki koncepcyjnej o wyższym numerze przejdzie do stanu TASK_RUNNING - oznaczającego, że proces może być wykonywany, dobrowolnie odda procesor używając funkcji sched_yield(). W wyniku wywołania proces zostaje wywłaszczony i przeniesiony na koniec kolejki procesów gotowych do wykonywania, zakończy działanie. Jak łatwo zauważyć polityka ta jest niesprawiedliwa dla procesów z tej samej kolejki koncepcyjnej - teoretycznie proces, który zdobył procesor może trzymać go dowolnie długo. Oczywiście w praktyce procesy dosyć często zgłaszają żądania wejścia - wyjścia, a także dobrowolnie rezygnują z procesora. SCHED_RR - Po przyznaniu procesora procesowi, odmierzany jest kwant czasu. Jego długość określa pole priority struktury opisującej proces. Proces może utracić procesor w takich samych przypadkach, jak przy polityce SCHED_FIFO, ale dodatkowo także kiedy skończy się jego kwant czasu. Po jego upływie proces 19

jest wyrzucany na koniec kolejki procesów gotowych i przyznaje mu się nowy kwant. Polityka jest sprawiedliwa dla procesów z tej samej kolejki koncepcyjnej, gdyż żaden proces nie może nieskończenie długo trzymać procesora. Skrót SCHED_RR pochodzi od angielskiego zwrotu Round-Robin oznacza szeregowanie cykliczne, SCHED_OTHER - polityka ta jest podobna do przedstawionej powyżej polityki SCHED_RR.. Istotna różnica polega na tym, że priorytet procesu jest dynamiczny, tzn. zmienia się w czasie posiadania procesora przez proces. Na początku epoki procesowi przyznawany jest kwant czasu, równy wartości pola priority struktury opisującej proces. Następnie wyliczany jest priorytet dynamiczny, który jest sumą pola priority i czasu (mierzonego w tyknięciach zegara), jaki pozostał procesowi z jego kwantu. Zatem priorytet dynamiczny procesu posiadającego procesor, zmniejsza się z każdym tyknięciem zegara. Podsumowując, zgodnie z polityką SCHED_OTHER proces może utracić procesor, gdy: zgłosi żądanie wejścia-wyjścia, dobrowolnie odda procesor używając funkcji sched_yield(), zgłosi się proces czasu rzeczywistego, zgłosi się proces zwykły o wyższym priorytecie dynamicznym, zakończy działanie. 2.2.2 Windows NT W rozdziale tym zostały przedstawione pewne cechy działania szedulera w systemie Microsoft Windows NT (wersja Workstation 4.0) [4]. W systemie tym podstawową jednostką szeregowania są wątki, jednak dla celów przedstawienia ogólnej zasady można myśleć o nich tak jak o procesach. Windows NT przydziela każdemu wątkowi wielkość priorytetu od 1 do 31, gdzie wyższa liczba oznacza wyższy priorytet. Priorytety od 16 do 31, zwane priorytetami czasu rzeczywistego, zarezerwowane są dla wątków specjalnych, które są wykonywane w pierwszej kolejności. Tylko nadzorca systemu może zlecić wykonanie takiego wątku. Priorytety od 1 do 15, zwane priorytetami dynamicznymi, przeznaczone są dla typowych programów użytkowych. 20

Wątki z priorytetami dynamicznymi podzielone są na trzy klasy, o różnych zakresach priorytetów: wysoki - priorytety z zakresu 11 15, priorytet startowy : 13 (ang. high), normalny - priorytety z zakresu 6 10, priorytet startowy : 8 (ang. normal), niski - priorytety z zakresu 2 6, priorytet startowy : 4 (ang. idle). Każdy nowo tworzony wątek dziedziczy klasę priorytetu po procesie bazowym i uruchamiany jest z priorytetem startowym danej klasy ±2 priorytety. Czasu rzeczywistego 31 procesy czasu rzeczywistego Procesy 24 czasu rzeczywistego priorytety 16-13 priorytet wysoki 16 15 13 Dynamiczne priorytet normalny priorytet niski 8 priorytety 1-15 4 1 0 Proces jałowy Rys. 5. Priorytety w Windows NT Program szeregujący podejmuje decyzję o wybraniu wątku do wykonania w każdym z następujących przypadków: Gdy wątek obecnie wykonywany wykorzysta swój kwant czasu. Szeduler wykonuje wówczas algorytm FindReadyThread (znajdź gotowy wątek), 21

aby sprawdzić, czy inny wątek nie powinien przejąć procesora. Jeśli wątek o wyższym priorytecie czeka na wykonanie, to wywłaszcza on obecny wątek z procesora. Jeśli nie, to obecny wątek jest nadal wykonywany. Gdy wątek obecnie wykonywany zawiesza działanie w oczekiwaniu na jakieś zdarzenie (np. na komunikat). Obecny wątek jest wywłaszczany z procesora, a jego miejsce zajmuje wątek wyznaczony spośród wątków gotowych do wykonania za pomocą algorytmu FindReadyThread. Gdy inny wątek przejdzie w stan gotowości do wykonania. Kiedy nowy wątek, lub wątek dotychczas blokowany przejdzie w stan "ready", szeduler wykonuje algorytm ReadyThread. Algorytm ten decyduje, czy wątek przejmie procesor od razu, czy też umieszczony zostanie na liście wątków gotowych do wykonania. Poniżej opisany został sposób działania algorytmów FindReadyThread i ReadyThread. FindReadyThread - algorytm ten znajduje wątek o najwyższym priorytecie wśród wątków gotowych do wykonania. Wątki gotowe do wykonania znajdują się na liście DispatcherReady. Lista ta zawiera 31 pozycji, z których każda odpowiada jednemu poziomowi priorytetu i posiada wskaźnik do kolejki wątków przydzielonych do tego priorytetu. Algorytm FindReadyThread przegląda listę DispatcherReady i wybiera z niej wątek z początku niepustej kolejki o najwyższym priorytecie, ReadyThread - algorytm ten umieszcza wątek na liście DispatcherReady. Gdy pojawia się wątek gotowy do wykonania, algorytm sprawdza, czy wątek ten ma wyższy priorytet od wątku obecnie wykonującego się na procesorze. Jeśli tak, to nowy wątek wywłaszcza obecny wątek i zajmuje procesor, a obecny wątek umieszczany jest na liście DispatcherReady. W przeciwnym przypadku, nowy wątek umieszczany jest na liście DispatcherReady. Wątki, które zostały wywłaszczone z procesora przed zakończeniem swojego pierwszego kwantu czasu, umieszczane są na początku swojej kolejki na liście DispatcherReady. Wszystkie pozostałe wątki umieszczane są na końcu swojej kolejki. 22

Szczególną własnością szedulera stosowanego w Windows NT są mechanizmy: doładowania (ang. boosting) i zaniku (ang. decay) wątków. Mechanizmy te dotyczą tylko priorytetów dynamicznych. Mechanizm doładowania i zaniku polega na zwiększeniu priorytetu wątku, który obudził się z oczekiwania na jakieś zdarzenie. Na przykład wątek oczekujący na zdarzenie od klawiatury lub myszy otrzymuje doładowanie zwiększające jego priorytet o 6, gdy nadejdzie oczekiwane zdarzenie. Takie doładowanie będzie stopniowo zanikać. Za każdym razem, gdy wątek w pełni wykorzysta swój kwant czasu, jego priorytet zmniejszy się o 1, aż z powrotem osiągnie początkową wartość, tzn. wartość, jaką miał przed pierwszym doładowaniem. Górną granicą doładowania jest 15, więc wątek dynamiczny nigdy nie dostanie priorytetu dla wątków czasu rzeczywistego. Oczywiście, wątek może być doładowywany wielokrotnie, gdy budzi się z oczekiwania na kolejne zdarzenia. 2.3 Wybrane funkcje systemowe Przed przystąpieniem do rozważań na temat implementacji poszczególnych funkcji systemowych w konkretnych systemach operacyjnych, należy zaznajomić się z materiałem teoretycznym opisującym zagadnienia poruszane przy okazji omawiania tychże funkcji. 2.3.1 Komunikacja międzyprocesowa Komunikacja międzyprocesowa ma za zadanie umożliwienie wymiany informacji między procesami, a także dostarczenie mechanizmów synchronizacji procesów. Implementacja funkcji umożliwiających takie czynności jest więc koniecznym elementem każdego systemu operacyjnego. Wyjątkiem od tej reguły jest prymitywna komunikacja za pomocą plików blokujących, która nie musi być w jakiś szczególny sposób zaimplementowana w systemie. Wszystkie techniki służące do komunikacji i synchronizacji procesów oraz wątków zostały pod wspólną nazwą: IPC (ang. interprocess communications) [5]. Na mechanizmy IPC składają się: Pliki blokujące jeden z najprostszych sposób komunikacji. Obiektami zaangażowanymi w komunikację mogą być odrębne procesy, lub tylko różne instancje jednego programu. Ten sposób komunikacji polega na utworzeniu na dysku pliku o określonej nazwie. Jeśli stosujemy tę technikę, np. do synchronizacji dostęp 23

do niepodzielnego zasobu, korzystamy z następującego schematu: Proces chcąc uzyskać dostęp sprawdza obecność pliku. Jeśli pliku nie ma, tworzy go stosując wcześniej ustalone konwencje. Zajmuje zasób, a zwalniając go usuwa także plik blokujący. Jeśli jednak okaże się, że przed zajęciem zasobu plik istnieje (oznacza to, że inny proces korzysta właśnie z zasobu) proces powinien czekać i w pętli sprawdzać obecność pliku, Sygnały komunikacja kryjąca się pod tą nazwą jest techniką stosowaną w systemach UNIX. Polega ona na korzystaniu z sygnałów, pojawiających się asynchronicznie, generowanych przez procesy lub jądro. Sygnał generowany jest w momencie wystąpienia określonego zdarzenia. Sygnał taki dociera do procesu, który może go odpowiednio obsłużyć lub zignorować. Do obsługi sygnałów służą funkcje, które należy zaimplementować w samym kodzie programu, Potoki mechanizm umożliwiają prosty, synchroniczny sposób wymiany danych między procesami. Potok należy uznać za plik specjalnego typu, który może służyć do przechowywania ograniczonej ilości danych i do którego dostęp może odbywać się jedynie w trybie FIFO (ang. First In First Out), Kolejki komunikatów dane przeznaczone do wymiany są umieszczane we wcześniej zdefiniowanej strukturze komunikatu. Proces tworzący nowy komunikat określa jego typ i umieszcza go w kolejce komunikatów, która obsługiwana jest przez system operacyjny. Procesy uzyskujące dostęp do kolejki mogą odczytywać komunikaty określonego typu, postępując zgodnie z protokołem FIFO. Zgodnie z tym protokołem najpierw pobierane są wiadomości, które trafiły do kolejki jako pierwsze. Kolejki komunikatów umożliwiają multipleksowanie danych z różnych źródeł, Semafory są to zaimplementowane na poziomie sytemu operacyjnego struktury danych, służące do wymiany małych ilości informacji między procesami. Dokładniej rzecz biorąc, semafor jest nieujemną liczbą całkowitą, na której mogą działać operacje czekaj (ang. wait) i sygnalizuj (ang. signalize). Obie operacje traktowane są jako niepodzielne, co daje gwarancje, że w momencie wykonywania ich, żaden inny proces nie zmieni wartości semafora. Poniżej przedstawiono definicje działania tych operacji: 24

sygnalizuj wynikiem tej operacji jest zwiększenie wartości semafora o jeden. Jeśli pod semaforem były uśpione jakieś procesy następuje obudzenie pierwszego, który trafił do kolejki procesów oczekujących pod semaforem, czekaj wynikiem tej operacji jest zmniejszenie wartości semafora o jeden, pod warunkiem, że semafor miał wartość dodatnią. Jeśli przed wykonaniem operacji semafor miał wartość równą zero proces zostaje uśpiony i przeniesiony do kolejki procesów oczekujących pod semaforem. Pamięć wspólna (dzielona) w tym wypadku dane wymieniane są przez procesy, poprzez pewien obszar pamięci. Obszar takiej pamięci musi być w specjalny sposób zaalokowany. Służą do tego specjalne funkcje zaimplementowane w systemie operacyjnym. Jest to najszybsza metoda komunikacji między procesami. Pamięć pozwala kilku procesom uczestniczącym uzyskać swobodny dostęp do jednego segmentu pamięci. Do synchronizowania dostępu tego typu często wykorzystuje się semafory. 2.3.2 Obsługa urządzeń System operacyjny, który nie umożliwia obsługi urządzeń wejścia/wyjścia jest zupełnie bezużyteczny. Za pomocą tychże urządzeń, system komunikuje się z użytkownikiem, archiwizuje wyniki swojej pracy itp. Brak obsługi tych urządzeń jest wykluczony także dlatego, że sam system jest z nich ładowany! Tradycyjnie uważa się, że obsługa urządzeń jest najpaskudniejszą częścią projektowania i implementacji systemu operacyjnego. Pogląd taki jest prawdziwy, a wynika on z konieczności napisania programów do obsługi rozmaitych urządzeń w jednej konfiguracji komputerowej mogą występować (i zazwyczaj występują) urządzenia o odmiennych parametrach technicznych i różnych trybach pracy. Ponieważ typowy system operacyjny jest platformą, na której uruchamiane są aplikacje użytkownika, a od dostępności tych aplikacji zależy popularność systemu system musi udostępnić programistom dobre mechanizmy do obsługi urządzeń. Z tego powodu większość współczesnych systemów udostępnia funkcje, które mogą operować na różnego typu urządzeniach. Rozwiązanie tego typu, ujednolicające dostęp do urządzeń różnego typu, jest dużym ułatwieniem dla programistów, którzy nie muszą martwić się o wewnętrzną realizacje wykonywanych operacji. W większości systemów dąży się 25

do sytuacji, kiedy każde urządzenie z punktu widzenia programu będzie po prostu plikiem. Dlatego w dalszej części rozdziału używane będą zamiennie określenia urządzenie / plik. Do podstawowych operacji wykonywanych na plikach należy zaliczyć: tworzenie część urządzeń (typu karta sieciowa, klawiatura) jest dostępnych już po uruchomieniu sytemu. Aby tworzyć (ang. create) własne urządzenia system musi udostępniać funkcje, za pomocą której możemy dokonywać tego typu operacje, usuwanie jeśli jakieś urządzenie jest już niepotrzebne można je usunąć (ang. delete) za pomocą tego typu funkcji, otwarcie aby wykonać jakąkolwiek inną operacje (poza utworzeniem nowego urządzenia) należy przedtem je otworzyć (ang. open). Funkcja ta zazwyczaj zwraca tzw. uchwyt (ang. handle), którym należy posługiwać się przy dokonywaniu innych operacji, zamknięcie po zakończeniu korzystania z urządzenia należy je zamknąć (ang. close). Powodowane jest to między innymi tym, że system może dokonywać buforowania operacji na urządzeniu. Nie mamy więc gwarancji, że wprowadzone dane do urządzenia znajdują się w nim fizycznie. W przypadku awaryjnego zakończenia się procesu (np. zawieszenia się) może dojść do utraty danych. Wywołanie tej funkcji powoduje też zwolnienie z zasobu, który był użytkowany, co może być istotne w przypadku urządzeń współdzielonych, odczyt za pomocą tego typu funkcji odczytywane są (ang. read) dane z urządzenia, zapis - za pomocą tego typu funkcji zapisywane są (ang. write) dane do urządzenia. 2.4 Funkcje w systemach UNIX Unix Time-Sharing System (pisane również jako UNIX ) to system operacyjny napisany w 1969 w Bell Labs. Rozwijany później w bardzo dynamiczny sposób, co zaowocowało powstaniem wielu odmian i implementacji. Dziś Unix - choć jest nazwą zastrzeżoną dla Open Group - jest synonimem całej rodziny systemów wywodzących się od pierwotnego pnia lub klonów kompatybilnych ze standaryzowaną normą POSIX (ang. Portable Operating System Interface). 26

Norma ta standaryzuje interfejs programistyczny i użytkownika. Definiuje też właściwości wymaganych usług systemowych takich jak: podstawowe wejście/wyjście (pliki, terminal i sieć). Ponieważ IEEE (organizacja, która stworzyła normę POSIX) pobierało bardzo wysokie opłaty za dokumentację i nie zezwalało na jej publikację w internecie, powstał standard Single UNIX Specification. Zawartość jest oparta w dużej mierze na specyfikacji POSIX, ale jest dostępna za darmo, udostępniana jest również w internecie. Wszystkie funkcje [7,16,5] w poniższych podrozdziałach są zgodne właśnie z normą POSIX oraz Single UNIX Specification [15]. 2.4.1 Tworzenie procesów W większości systemów pochodzących od UNIX wszystkie procesy (nie licząc pewnych procesów inicjacyjnych, tworzonych przez jądro podczas uruchamiania systemu [6]), tworzone są za pomocą funkcji fork. Jednak funkcja fork sama w sobie tworzy tylko klon bieżącego procesu, dlatego zazwyczaj używana jest w parze z funkcją execve 2.4.1.1 fork Funkcja fork tworzy proces potomny, który różni się od procesu macierzystego jedynie swoimi numerami PID i PPID (PID rodzica, ang. parent PID) oraz tym, że w rzeczywistości użycie przez niego zasobów jest ustawione na 0. Blokady plików i oczekujące sygnały nie są dziedziczone. Funkcja jest zaimplementowana za pomocą kopiowania stron pamięci przy zapisie, więc jedynymi mankamentami fork są czas i pamięć, wymagane do powielenia tablic stron rodzica i utworzenia unikalnej struktury zadania dla potomka. fork(void); Zwracana wartość: int - po pomyślnym zakończeniu, w procesie macierzystym zwracany jest PID procesu potomnego, a w procesie potomnym zwracane jest 0. Po błędzie zwracane jest -1 w kontekście rodzica i odpowiednio ustawiane jest errno 27

Wartość errno: EAGAIN - została osiągnięta maksymalna liczba procesów dla jednego użytkownika, bądź dostępna pamięć systemowa dla surowych operacji wejścia/wyjścia jest za mała. ENOMEM - funkcja nie mogła zaalokować niezbędnych struktur jądra z powodu niedostatecznej ilości pamięci. Przykład: #include<stdio.h> #include<sys/types.h> #include<unistd.h> void main(void){ printf( Czesc\n ); fork(); printf( Hey\n ); } Wynikiem działania będzie: Czesc Hey Hey 2.4.1.2 execve Funkcja execve w zasadzie nie tworzy nowego procesu, lecz podmienia wykonywany przez proces program. Jednak ze względu na częstość stosowania w programach pisanych dla środowiska POSIX, została umieszczona w niniejszym rozdziale. execve(const char *filename, char *const argv [], char *const envp[]); Funkcja po pomyślnym wywołaniu nie powraca, a segmenty text, data, bss oraz segment stosu procesu wywołującego zostają nadpisane przez odpowiedniki ładowanego programu. Wywoływany program dziedziczy PID procesu wywołującego, oraz wszelkie deskryptory otwartych plików, które nie są ustawione jako "close on exec". Sygnały oczekujące na proces wywołujący zostają wyczyszczone. Jeżeli plik programu, 28

który ma zostać uruchomiany posiada ustawiony bit set-uid, to efektywny identyfikator użytkownika procesu wywołującego jest ustawiany na właściciela pliku programu. Podobnie, jeżeli dla pliku programu ustawiony jest bit set-gid, to efektywnemu identyfikatorowi grupy procesu wywołującego jest przypisywana grupa pliku programu. Parametry wywołania: filename - nazwa programu do uruchomienia. Plik musi być albo wykonywalnym programem binarnym, albo skryptem powłoki, rozpoczynającym się od linii w postaci #!, argv - jest tablicą łańcuchów przekazywanych jako argumenty nowego programu. argv musi być zakończone wskaźnikiem pustym (NULL), envp - jest tablicą łańcuchów postaci klucz=wartość, która jest przekazywana jako środowisko do nowego programu. envp musi być zakończone wskaźnikiem pustym (NULL). Zwracana wartość: int - po pomyślnym zakończeniu funkcja nie powraca. Po błędzie zwracane jest -1 i odpowiednio ustawiane errno. Przykład: int ret; char *cmd[] = { "ls", "-l", (char *)0 }; char *env[]={ "HOME=/usr/home", ( char )*0 }; ret = execve ("/bin/ls", cmd, env); Działania: Przykład podmienia uruchomiony proces, na proces wykonujący program ls. Do nowego programu przekazane zostają parametry -l oraz zmienna środowiskowa HOME. Zmienna ret zawiera kod błędu w przypadku niepowodzenia podczas uruchamiania programu ls. 29

2.4.2 Komunikaty Aby możliwe było komunikowanie się między procesami za pomocą komunikatów w systemie POSIX, konieczne jest uprzednie utworzenia kolejki komunikatów. W przypadku, gdy taka kolejka została już utworzona, wszystkie procesy chcące z niej skorzystać muszą wpierw uzyskać do niej dostęp. Do obu wyżej wymienionych operacji, systemy POSIX korzystają z funkcji msgget. Właściwa komunikacja przeprowadzana jest za pomocą funkcji msgsnd (ang. message send), która umieszcza wiadomość w kolejce, oraz funkcji msgrcv (ang. message receive), która służy do pobierania wiadomości z kolejki. Komunikat jest strukturą zawierające dane opisujące typ oraz treść komunikatu. struct msgbuf{ long m_type; /* typ komunikatu*/ char m_text[1]; /* treść komunikatu*/ }; 2.4.2.1 msgget Funkcja służy do tworzenia, lub uzyskiwania dostępu do kolejki wiadomości. msgget(key_t key, int msgflg); Lista ważniejszych stałych, używanych jako parametry funkcji: IPC_PRIVATE utworzenie kolejki komunikatów (lub jakiegokolwiek innego zasobu IPC) o niepowtarzalnej wartości identyfikatora. Zasób utworzony w ten sposób może być wspólny dla grupy procesów, IPC_CREATE nakazuje utworzenie zasobu IPC, jeśli do tej pory nie istniał w systemie. Jeśli zasób już istnieje, ale nie został utworzony za pomocą IPC_PRIVATE, zwracany jest identyfikator, IPC_EXCL jeśli zostanie użyte w połączeniu z IPC_CREATE, a zasób IPC istnieje już w systemie, zostanie wygenerowany błąd. Parametry wywołania: key - struktura wykorzystywana niepowtarzalnego do skonstruowania identyfikatora kolejki komunikatów. Może być określona bezpośrednio 30

przez użytkownika, lub wygenerowana funkcją ftok. Jeśli jako argumentu użyje się stałej IPC_PRIVATE zostanie utworzona nowa kolejka komunikatów, - najmłodsze bity argumentu (jest ich 9) określają msgflg uprawnienia dostępu do kolejki. Dodatkowe atrybuty należy wyspecyfikować, używając wyrażenia złożonego ze stałych typu IPC_CREATE, IPC_EXCL, połączonych operatorem logicznym OR. Zwracana wartość: int - W przypadku powodzenia zwracany jest nieujemny identyfikator kolejki komunikatów, skojarzony z kluczem key. Jeśli funkcja zakończy się fiaskiem - funkcja zwraca -1 i odpowiednio ustawiane errno. Przykład: key_t key = ftok(.,0); int msq = msgget(key, IPC_CREATE 0660); Działania: Utworzenie nowej kolejki. Właściciel i użytkownicy należący do tej samej grupy co właściciel, będą mogli czytać/wysyłać komunikaty z/do kolejki. 2.4.2.2 msgsnd Umieszczenie w kolejce komunikatów nowej wiadomości. msgsnd(int msqid, const void * msgp, size_t msgsz, int msgflg); Parametry wywołania: msgidq - identyfikator kolejki komunikatów zwróconym wcześniej przez funkcje msgget, msgp - wskaźnik na strukturę komunikatu, którą należy przesłać, msgsz - określa rozmiar (w bajtach) wysyłanej wiadomości, 31

- parametr może wynosić zero lub IPC_NOWAIT. Jeśli msgflg ustawiony jest na IPC_NOWAIT i system osiągnie limit długości kolejki, funkcja nie wyśle komunikatu, tylko powróci natychmiast do procesu wywołującego, ustawiając wartość zmiennej errno na kod EAGAIN. Jeśli ustawione jest zero proces zostaje zablokowany do czasu, gdy długość kolejki zostanie zmniejszona poniżej limitu systemowego. Zwracana wartość: int - w przypadku pomyślnego wysłania wiadomości zwraca 0. W przeciwnym wypadki zwraca -1 i odpowiednio ustawiana wartość errno. Przykład: msgrcv - przykład. 2.4.2.3 msgrcv Pobranie wiadomości z kolejki komunikatów msgrcv(int msqidq, const void * msgp, size_t msgsz, long msgtyp, int msgflg); Parametry wywołania: msgidq - identyfikator kolejki komunikatów zwrócony wcześniej przez funkcje msgget, msgp - wskaźnik na miejsce w pamięci, w którym ma być umieszczona treść otrzymanego komunikatu, msgsz - maksymalny rozmiar komunikatu (w bajtach). Jeśli komunikat będzie dłuższy, dojdzie do jego obcięcia oraz wygenerowania błędu, msgtyp - wartość interpretowana zgodnie z poniższym wypunktowaniem: równa 0 pobierze pierwszy komunikat dowolnego typu, 32

większa niż 0 pobierze pierwszy komunikat, którego typ będzie równy wartości msgtyp, mniejsza niż 0 pobierze pierwszy komunikat, którego typ będzie mniejszy, lub równy wartości msgtyp, - Parametr może przyjmować wartość dwóch stałych: msgflg IPC_NOWAIT jeśli w kolejce nie znaleziono żądanego komunikatu, proces nie jest blokowany, obcięcie treści komunikatu MSG_NOERROE wykraczającego poza zakres nie powoduje generowania błędu. Zwracana wartość: int - W przypadku pomyślnego wykonania funkcji, zwracana jest długość odebranej wiadomości. W przeciwnym wypadki zwraca -1 i odpowiednio ustawiana wartość errno Przykład: #include <sys/types.h> #include <sys/ipc.h> #include <sys/shm.h> #define BUFFSIZE 128 #define PERMS #define KEY ((key_t) 7777) 0666 main(){ int i, msqid; struct { long m_type; char m_text[buffsize]; } msgbuffs, msgbuffr; if ((msqid = msgget(key, PERMS IPC_CREAT)) < 0) perror("blad msgget"); msgbuffs.m_type = 1L; 33

strcpy(msgbuffs.m_text,"jakas nudna wiadomosc"); if (msgsnd(msqid, &msgbuffs, BUFFSIZE, 0) < 0) perror("blad msgsnd"); printf("wyslana wiadomosc: %s \n", msgbuffs.m_text); if (msgrcv(msqid, &msgbuffr, BUFFSIZE, 0L, 0)!= BUFFSIZE) perror("msgrcv error"); printf("odebrana wiadomosc: %s \n", msgbuffr.m_text); exit(0); } Efekt działania: wyslana wiadomosc: jakas nudna wiadmosc odebrana wiadomosc: jakas nudna wiadmosc 2.4.3 Semafory Semafory, jak wiadomo służą do wymiany małych ilości danych. Stosowane są w mechanizmach służących do synchronizowania procesów (lub wątków). 2.4.3.1 semget semget(key_t key, int nsems, int semflg); Funkcja służy do tworzenia, lub uzyskiwania dostępu do zestawu kilku semaforów (w szczególności jednego). Parametry wywołania: key - struktura wykorzystywana do skonstruowania niepowtarzalnego identyfikatora zestawu semaforów. Może być określona bezpośrednio przez użytkownika, lub wygenerowana funkcją ftok. Jeśli jako argumentu użyje się stałej IPC_PRIVATE zostanie utworzony nowy zestaw semaforów, nsems - liczba semaforów w zestawie, 34