Usługi systemu operacyjnego

Transkrypt

Usługi systemu operacyjnego
Projektowanie
oprogramowania
systemów
USŁUGI I ZASOBY SYSTEMU OPERACYJNEGO
plan

rola systemu operacyjnego w zarządzaniu zasobami procesów

Unix - filozofia „wszystko jest plikiem”

usługi i zasoby systemowe

API dostępu do plików

zarządzanie pamięcią

zegary
rola systemu operacyjnego w
zarządzaniu zasobami procesów

System operacyjny jest środowiskiem do uruchamiania i kontroli
zadań użytkownika

Zajmuje się zarządzaniem zasobami komputera

planowaniem oraz przydziałem czasu procesora (scheduling)

przydziałem pamięci operacyjnej zadaniom

dostarcza mechanizmy IPC

obsługuje sprzęt i zapewnia współbieżnym zadaniom pozbawiony
interferencji do niego dostęp

zarządza plikami

…
zarządzanie zasobami

interferencja procesów – szkodliwe zjawisko będące efektem
wielozadaniowości

różne procesy równocześnie uzyskują dostęp do tego samego sprzętu i
w przypadkowej kolejności wykonują na nim różne operacje

rolą systemu operacyjnego jest szeregowanie dostępu do zasobów i
zapewnienie spójności operacji poszczególnych procesów

system operacyjny jako „strażnik” zasobów

warstwa abstrakcji sprzętu (HAL) – każdy z procesów „ma wrażenie”,
że pracuje na własnym komputerze, z własnymi zasobami – w tym
również nieistniejącymi (wirtualnymi)
rola systemu operacyjnego w
zarządzaniu zasobami

Przydział zasobów

Tworzenie i usuwanie deskryptorów zasobów

Realizacja żądań przydziału i zwolnienia zasobów

Synchronizacja dostępu do zasobów (zapobieganie interferencji)

Autoryzacja dostępu do zasobów (system uprawnień)

Odzyskiwanie zwolnionych zasobów

Accounting (gromadzenie danych statystycznych o wykorzystaniu
zasobów)
zarządzane zasoby



procesy

czas procesora

obiekty synchronizacji i IPC
pamięć operacyjna

przydział pamięci do procesów

ochrona pamięci
pliki

tworzenie i kasowanie plików i katalogów

operacje odczytu i zapisu

urządzenia wejścia/wyjścia

nośniki danych
filozofia Unix – „wszystko jest
plikiem”

prawie wszystkie zasoby systemowe w Uniksie reprezentowane są
przez pliki istniejące w systemie plików

pozwala to w spójny sposób nadawać użytkownikom i procesom
uprawnienia do tych zasobów i zarządzać ich dostępem

ten sam zestaw narzędzi może być wykorzystywany z wieloma
rodzajami narzędzi

dostęp do zasobów może być realizowany poprzez otwarcie
deskryptora pliku reprezentującego dane urządzenie w systemie
plików

pliki reprezentujące urządzenia mogą istnieć w fizycznym systemie
plików (na dysku) lub w wirtualnych systemach plików,
montowanych do rzeczywistych lokalizacji na dysku (np. /proc)
„wszystko jest plikiem”


zasoby będące reprezentowane przez pliki

rzeczywiste pliki i katalogi

gniazda sieciowe i potoki

urządzenia we/wy (sterowniki urządzeń): system plików /dev

urządzenia znakowe (niebuforowany dostęp)

urządzenia blokowe (buforowany dostęp)

pseudo-urządzenia (np. /dev/null, /dev/random – generator liczb losowych)

obiekty synchronizacji (semafory, mutexy, …)

pamięć współdzielona (shmfs)

procesory, pamięć fizyczna, procesy, timery,… (system plików procfs)
wiele wywołań systemowych możliwych jest do zrealizowania jako
operacja odczytu/zapisu określonego pliku
„wszystko jest plikiem”

wiele systemów nie-Unixowych naśladuje w ograniczonym zakresie
podejście „wszystko jest plikiem” Unixa

CP/M, DOS, OS/2: pliki specjalne CON$, PRN$, NUL$, CLOCK$, LPTn,
COMn

Windows NT

wewnętrznie system jest podobny do Unixa, większość urządzeń i
zasobów również może być otwarta jako plik (uchwyt pliku, HANDLE)

zasoby nie są widoczne jako pliki na dysku, ale istnieją w swoich
osobnych przestrzeniach nazw (np. \\.\pipes\ - dla nazwanych
potoków)
API dostępu do plików

Systemy operacyjne dostarczają warstwę abstrakcji, dzięki której
dostęp do plików znajdujących się w różnych systemach plików
(lokalnych, sieciowych, wirtualnych, przenośnych, wymiennych …)
odbywa się w identyczny sposób

Różnice w realizacji operacji w różnych systemach plików są
obsługiwane przez sterownik w sposób przezroczysty dla
użytkownika, przy pomocy pojedynczego zestawu wywołań
systemowych

System operacyjny dostarcza ujednolicony, ustandardyzowany
interfejs programistyczny (API)
typowe operacje na plikach

Otwieranie i zamykanie plików (tworzenie deskryptorów plików lub
uchwytów dla obiektów znajdujących się w systemie plików)

deskryptor pliku – abstrakcyjny identyfikator (liczba), zwykle indeks do
tablicy deskryptorów zarządzanej przez OS dla danego procesu

Odczyt, zapis i pozycja w pliku

Zarządzanie metadanymi pliku (czas ostatniej modyfikacji, tagi, etc)

Zarządzanie katalogami

Określanie zasad współdzielenia otwartych
plików między procesami, blokowanie

Uprawnienia dostępu do plików

Szyfrowanie
warstwy API plikowych

systemy operacyjne, języki programowania i biblioteki dostarczają
warstwowego modelu dostępu do plików zapewniającego
abstrakcję sprzętu, platformy, języka oprogramowania
C++ standard library
UNIX
Windows
C++97 iostream library (fstream class)
C++97 iostream library (fstream class)
C89 stdio library (FILE pointer)
C89 stdio library (FILE pointer)
C standard library
emulated POSIX file API (int file
descriptor)
Native OS API
Unix/POSIX file API (int file descriptor)
Win32 file API (HANDLE)
warstwy API - niskopoziomowe
POSIX

natywne API: open()/creat() –
zwracają deskryptor pliku, tablica
deskryptorów zarządzana przez OS

niskopoziomowe operacje na
plikach

brak buforowania na poziomie
biblioteki – każda operacja na
deskryptorze jest osobnym
wywołaniem systemowym

buforowanie na poziomie cache
systemu operacyjnego
Windows

natywne API: CreateFile()

open()/creat() – zaimplementowane
w bibliotece standaardowej na
bazie natywnego API, tablica
deskryptorów zarządzana przez
bibliotekę

odwzorowanie 1:1 pomiędzy
emulowanymi funkcjami API POSIXa
a wywoływaniami systemowymi
Win32 API

większa kontrola nad uprawnieniami
i blokowaniem plików za pomocą
API Win32

emulacja API POSIX dla zgodności z
biblioteką standardową C89
warstwy API – buforowane I/O

Biblioteka standardowa C89 na bazie niskopoziomowego API
zgodnego z POSIX buduje podsystem buforowanego wejścia/wyjścia
oparty na strukturze FILE

buforowanie ma na celu uniknięcia (kosztownych) wywołań
systemowych dla każdej podstawowej operacji I/O

jednorazowo wczytywane/zapisywane są większe porcje danych

odczyt/zapis pojedynczych znaków nie wiąże się z częstymi wywołaniami
systemowymi – zysk wydajności

Struktura FILE zawiera w sobie informacje (book keeping) na temat
właściwości otwartego deskryptora pliku

Możliwe jest uzyskanie deskryptora pliku z e struktury FILE (funkcja
fileno()), jednakże pozycja „w deskryptorze” jest inna niż w strumieniu
FILE*
otwieranie pliku

istnieje szereg sposobów na otwarcie deskryptora pliku, w zależności
od rodzaju pliku

przykład (Unix):

open() – otwarcie dowolnego pliku

creat() – utworzenie dowolnego pliku

socket() – utworzenie gniazda sieciowego

accept() – utworzenie gniazda poprzez zaakceptowanie połączenia
przychodzącego

socketpair() – utworzenie pary połączonych gniazd

pipe() – utworzenie pary połączonych gniazd Unixowych (potoku)

opendir() – otwarcie deskryptora identyfikującego katalog
porównanie operacji - odczyt
operacja
typ
charakterystyka
open()/creat() int (file descriptor)
natywne API posix, niebuforowane,
niskopoziomowe I/O
CreateFile()
HANDLE
natywne API Win32, niebuforowane,
niskopoziomowe I/O
fopen(),
_wfopen(),
freopen()
FILE*
buforowane API strumieniowe
dostarczane przez bibliotekę std C w
oparciu o API niskopoziomowe
fdopen()
FILE*
utworzenie buforowanego I/O w oparciu
o otwarty deskryptor pliku
porównanie operacji – zamykanie
pliku
operacja
typ
charakterystyka
close()
int (file descriptor)
natywne API posix
CloseHandle()
HANDLE
natywne API Win32, funkcja zamyka
dowolny uchwyt (nie tylko pliku)
fclose()
FILE*
buforowane API C stdio, powoduje
zapisanie zawartości buforów i zwolnienie
deskryptora pliku
podstawowe operacje I/O
operacja
typ
charakterystyka
read()/write()
int (file
descriptor)
natywne API posix
ReadFile()/WriteFile()
HANDLE
natywne API Win32
ReadFileEx()/
WriteFileEx()
HANDLE
natywne API Win32 – umożliwiają
asynchroniczny odczyt/zapis plików (w
tle)
fread(),fwrite()
FILE*
buforowane API C stdio
fflush()
FILE*
buforowane API C stdio – zapis buforów
pliku na dysk (synchronizacja)
operacje I/O na pojedynczych
znakach
operacja
typ
charakterystyka
getc()
FILE*
buforowane API C stdio – odczyt
następnego znaku ze strumienia
putc()
FILE*
buforowane API C stdio – zapis
pojedynczego znaku do strumienia
ungetc()
FILE*
buforowane API C stdio – „cofnięcie”
odczytu ostatniego znaku
odczyt i zmiana pozycji w pliku
operacja
typ
charakterystyka
lseek(), lseek64()
int (file
descriptor)
oczyt lub zmiana pozycji wskaźnika pliku
(położenia kursora odczytu/zapisu)
fseek()/ftell()/
rewind()/fgetpos()/
fsetpos()
FILE*
buforowane API C stdio – odczyt (ftell(),
fgetpos()) lub zmiana pozycji (fseek(),
rewind(), fsetpos()) wskaźnika w
buforowanym strumieniu – nie musi
wiązać się ze zmianą pozycji wskaźnika
pliku!
GetFilePointer()/
SetFilePointer()/
SetFilePointerEx()
HANDLE
odpowienik lseek64() w natywnym API
Win32
sprawdzenia/ustawienie końca
pliku
operacja
typ
charakterystyka
feof()
FILE*
sprawdzenie czy pozycja strumienia jest
na końcu pliku
ftruncate()
int (file
descriptor)
ucięcie pliku na określonej pozycji
SetEndOfFile()
HANDLE
ustawienie końca pliku w aktualnej
pozycji (Win32)
przeglądanie katalogów - POSIX
operacja
typ
charakterystyka
opendir(), fdopendir() DIR*
otwarcie katalogu o określonej
ścieżce/deskryptorze pliku
readdir()
DIR*
odczyt rekordu w katalogu
seekdir()
DIR*
przewijanie do określonego rekordu w
katalogu
scandir()
DIR*
wyszukiwanie rekordu spełniającego
określony warunek
closedir()
DIR*
zamknięcie katalogu
przeglądanie katalogów – Win32
operacja
typ
charakterystyka
FindFirstFile()
HANDLE
rozpoczyna enumeracje plików
spełniających określony warunek (np.
znajdujących się w określonym katalogu)
FindNextFile()
HANDLE
kolejne iteracje wyszukiwania
FindClose()
HANDLE
zakończenie wyszukiwania
przeglądanie katalogów przykłady
Windows
POSIX
tworzenie i usuwanie katalogów i
plików
operacja
typ
charakterystyka
CreateDirectory()/
CreateDirectoryEx()
tworzy katalog o podanej ścieżce
(Win32)
RemoveDirectory()
usuwa istniejący (pusty) katalog
DeleteFile()
usuwa plik
mkdir()
tworzy katalog o podanej ścieżce
(POSIX)
rmdir()
usuwa istniejący (pusty) katalog
unlink()
usuwa nazwę z systemu plików, plik
zostanie usunięty po zamknięciu
ostatniego aktywnego deskryptora
odnoszącego się do niego
remove()
usuwa plik lub katalog – woła unlink() dla
plików, rmdir() dla katalogów
tworzenie plików tymczasowych
operacja
typ
charakterystyka
GetTempPath()
pobiera ścieżkę katalogu
tymczasowego, do którego proces ma
prawo zapisu
GetTempFileName()
generuje nazwę pliku tymczasowego i
opcjonalnie tworzy plik o unikatowej
nazwie
mkdtemp()
tworzy unikatowy katalog tymczasowy,
do którego proces ma prawo zapisu
mktemp()
generuje unikatową nazwę pliku
tymczasowego (nie używać! – wyścigi)
mkstemp()
int
tworzy plik tymczasowy o unikatowej
nazwie i zwraca jego deskryptor
tmpfile()
FILE*
tworzy plik tymczasowy o unikatowej
nazwie i zwraca otwarty FILE* do niego
operacje na plikach przenośnie

Język C – używaj API POSIX dla niebuforowanego I/O, stdio dla
buforowanego

C++ - używaj biblioteki Boost.Filesystem

Java, C# - zawierają warstwę abstrakcji dla OS
zarządzanie pamięcią

proces podczas tworzenia otrzymuje od OS pewien obszar „pamięci
wolnej”, który potocznie nazywany jest „areną”

arena zarządzana jest przez alokator pamięci procesu, który jest
implementowany przez dany język programowania/bibliotekę
standardową

alokator procesu jest używany do przydzielania pamięci na:


stosy wątków procesu

obiekty i struktury tworzone w pamięci wolnej („na stercie”)
rozmiar areny może być powiększany wraz z rosnącymi potrzebami
procesu

odbywa się to w sposób niewidoczny dla użytkownika – alokator procesu
sam wywołuje funkcje systemu operacyjnego służące do powiększania
areny
alokator procesu


Interfejs alokatora procesu dostarczany przez bibliotekę
standardową języka C składa się z:

malloc() – alokuje ciągły region pamięci o zadanym rozmiarze ze sterty

calloc() – alokuje ciągły obszar pamięci dla N-elementowego wektora
obiektów o zadanym rozmiarze

realloc() – zmienia rozmiar przydzielonego wcześniej bloku pamięci, być
może przenosząc go w inne miejsce

free() – zwalnia pamięć zalokowaną przez malloc(), calloc() lub
realloc()
Na bazie powyższego API tworzone są bardziej zaawansowane
mechanizmy alokacji pamięci – odśmiecacze, alokator C++
(new/delete)
działanie alokatora procesu

Pamięć zalokowana przy pomocy alokatora procesu pozostaje
zajęta do momentu wywołania free()

Pamięć nie zwolniona stanowi „wyciek” - jest tracona do momentu
zakończenia procesu

Alokacja wielu małych bloków prowadzi do fragmentacji pamięci,
która obniża wydajność alokatora wraz z działaniem programu
zegary

Zegary są niezbędną usługą systemu dla programów, które
przeprowadzają pewne operacje okresowo, np. służących do
przetwarzania multimediów online, wykonujących pomiary itp.

Programowalne zegary umożliwiają uruchamianie operacji po
określonym czasie lub z zadaną częstotliwością

W systemach POSIX zegary implementuje się za pomocą


sygnałów

deskryptorów plików
Na Windows zegary implementuje się za pomocą

obiektów synchronizacji

funkcji callback

komunikatów okien
zegary - POSIX

System POSIX umożliwia stworzenie „budzików” interwałowych w
oparciu o kilka zegarów

zegar czasu rzeczywistego – jego odczyt może się zmienić w trakcie
działania programu poprzez przestawienie aktualnej godziny

zegar monotoniczny – odczyt nigdy się nie zmienia – zalecany do
tworzenia aplikacji działających synchronicznie

zegar czasu procesora procesu – mierzy tylko czas, który OS spędził
wykonując aktualny proces

zegar czasu procesora wątku – mierzy tylko czas, który OS spędził w
aktualnym wątku
zegary POSIX - sygnały

Tworzymy „budzik” za pomocą funkcji timer_create() podając
rodzaj zegara i wypełniając strukturę sigevent określającą
identyfikator sygnału, który zostanie uruchomiony

Instalujemy w procesie procedurę obsługi w/w sygnału

Nastawiamy „budzik” jako pojedynczy lub okresowy za pomocą
timer_settime()

„Budzik” spowoduje wyemitowanie sygnału i w efekcie
uruchomienie procedury jego obsługi

Niepotrzeby „budzik” zwalniamy przez wywołanie timer_delete()
zegary POSIX - pliki

Tworzymy „budzik” za pomocą funkcji timerfd_create() podając
rodzaj zegara, funkcja zwraca deskryptor pliku reprezentujący zegar

Nastawiamy „budzik” jako pojedynczy lub okresowy za pomocą
timerfd_settime()

Używamy deskryptora pliku w pętli I/O w wywołaniu funkcji do
multipleksacji I/O – select() lub poll()

„Budzik” spowoduje obudzenie select() lub poll() i zwrócenie
deskryptora zegara jako posiadającego dane do odczytu

Niepotrzeby „budzik” zwalniamy przez wywołanie close()

„Wszystko jest plikiem”...
zegary Windows

API Win32 jest niezbyt spójne – niektóre rodzaje zegarów pozwalają
tylko określić czas jako relatywny w stosunku do teraz (odporny na
przestawienie zegara systemowego), inne tylko jako absolutny
zegary Windows – „multimedialny”

Ustawiamy „budzik” relatywny w stosunku do teraz za pomocą
funkcji timeSetEvent() jako pojedynczy lub okresowy

W zależności od parametru budzik może:

uruchomić funkcję typu callback (w swoim własnym wątku
zarządzanym przez OS)

zasygnalizować obiekt zdarzenia (Windows Event) poprzez SetEvent()
lub PulseEvent()

Jeśli budzik ma sygnalizować zdarzenie, to uruchamiamy funkcję
oczekującą na zdarzenie np. WaitForMultipleObjects()

Kiedy budzik już nie jest potrzebny wyłączamy go za pomocą
timeKillEvent()
zegary Windows - nazwane

Tworzymy budzik za pomocą CreateWaitableTimer(), opcjonalnie
podając jego nazwę

Nastawiamy budzik za pomocą SetWaitableTimer(), podając czas
jako absolutny lub relatywny, okres kolejnych wywołań i opcjonalną
funkcję callback

Po upłynięciu czasu uchwyt zegara (HANDLE) stanie się
zasygnalizowany i zostanie uruchomiona opcjonalna funkcja
callback

Na zegar możemy czekać przy pomocy funkcji
WaitForMultipleObjects()

Niepotrzebny zegar niszczymy za pomocą CloseHandle()
zegary Windows - okna

Zegar możemy powiązać z oknem UI Windows, wówczas po upływie
określonego czasu zostanie wysłany przez OS komunikat okna
WM_TIMER

Ustawiamy budzik za pomocą funkcji SetTimer() podając uchwyt
okna, do którego ma zostać wysłany komunikat oraz identyfikator
komunikatu zegara

Po upływie określonego czasu (tylko relatywnego) wysyłany jest
komunikat okna WM_TIMER wraz z identyfikatorem zegara

Niepotrzebny zegar niszczymy funkcją KillTimer()

Przydatne rozwiązanie dla tworzenia animacji w obiektach UI

Podobne dokumenty