Rozdział 5
Jądro systemu
Jądro stanowi tę część systemu Unix, która faktycznie nadzoruje przydział czasu komputera,
obszaru pamięci i kanałów komunikacyjnych różnym zadaniom, które mogą być równocześnie
wykonywane w danej chwili na rzecz wielu użytkowników. W skład jądra wchodzi centralny
program nadzorujący wspierany przez procedury usługowe troszczące się o takie ważne szczegóły,
jak pobieranie znaków z klawiatury, współpraca z pamięcią zewnętrzną i nadzór nad zegarem
systemowym.
Zasadniczą część użytecznych prac w systemie Unix wykonują oddzielne, autonomiczne pro-
gramy. Większość z nich jest bezpośrednio widoczna dla użytkowników (chociaż istnieją wyjątki).
5.1 Krótka charakterystyka jądra
System Unix, oglądany z zewnątrz, wygląda tak, jakby się składał z dwóch części: dużego zbioru
programów, odpowiadających poleceniom oraz interpretatora, przyjmującego polecenia użytkow-
nika i inicjującego wykonywanie programów. Wewnętrzną część Unixa również można podzielić
na dwie składowe: duży zbiór procedur usługowych wykonujących funkcje ściśle związane z ob-
sługą sprzętu i oprogramowania oraz jądro, troszczące się o współpracę procedur z pracującymi
procesami. Procedury usługowe wywołuje się jedynie wówczas, gdy są potrzebne, większość ko-
du programu nadzorującego znajduje się natomiast na stałe w pamięci. Program ten tworzy
podstawowe środowisko dla wszystkiego, co dzieje się w systemie, a równocześnie zajmuje mało
miejsca w pamięci, pozostawiając tyle, ile tylko możliwe procesom użytkowym.
5.1.1 Funkcje jądra
Przeciętny użytkownik komputera nie interesuje się zbyt dogłębnie budową sprzętu, którym
posługuje się przy rozwiązywaniu swojego problemu ani szczegółami oprogramowania systemo-
wego. System Unix dostosowano do tego braku zainteresowania umieszczając osłonę shell
28 pazdziernika 2001 roku wersja 0.2 69
pomiędzy użytkownikiem a komputerem, aby użytkownik komunikował się z wirtualną maszyną
unixową, której jedyną widoczną częścią jest właśnie program shell.
Jądro Unixa odgrywa podobną rolę, ale na niższym poziomie: ukrywa ono maszynę fizyczną
przed programami i zaawansowanymi użytkownikami, chcącymi od czasu do czasu korzystać
z usług niskiego poziomu systemu. Jądro definiuje maszynę wirtualną o własnościach przypomi-
nających własności dużego zbioru maszyn fizycznych. Rzeczywiste komputery opisuje się zatem
poprzez maszyny wirtualne, wstawiając pomiędzy użytkownikiem i maszyną program. Własno-
ści maszyny fizycznej są rzadko bezpośrednio widoczne. Taka struktura pozwala na przenoszenie
oprogramowania. Ponieważ nawet shell korzysta z maszyny wirtualnej, można go zainstalować
na nowym komputerze wymieniając jedynie programy najbliższe sprzętowi, przekształcające ma-
szynę fizyczną w maszynę wirtualną1.
Maszyna wirtualna zdefiniowana przez jądro pełni trzy podstawowe funkcje, które są ściśle zwią-
zane ze strukturą sprzętu komputerowego:
1. szereguje, koordynuje i zarządza wykonywaniem procesów;
2. zapewnia usługi systemowe, takie jak operacje wejścia-wyjścia i zarządzania plikami;
3. obsługuje wszystkie inne operacje związane ze sprzętem.
5.1.2 Struktura jądra
Istotna część jądra dotyczy zarządzania pamięcią, kolejkowania użytkowników i szeregowania
procesów, a także nadzoru nad stosem, rejestrami oraz innymi elementami środowiska podczas
ładowania procesu do pamięci. Odpowiada ponadto za przerwania spowodowane np. sprzętowy-
mi błędami pamięci. Tę główną część jądra, napisano w języku C. Korzysta się w niej prawie
wyłącznie z maszyny wirtualnej, nie powinno być zatem problemu z przenoszeniem jej na dowol-
ną maszynę, dla której istnieje kompilator języka C. Duża część kodu jądra jest więc identyczna,
w różnych wersjach i mutacjach systemów przeznaczonych do pracy na zbliżonych rodzajach
komputerów.
Zarządzanie pamięcią i sterowanie wykonaniem procesów wymagają szybkiego reagowania.
Dlatego odpowiadające za nie części jądra rezydują na stałe w pamięci. Z kolei procedury usłu-
gowe, których jest bardzo dużo, nigdy nie są potrzebne natychmiast, można je więc ładować
do pamięci tylko w miarę potrzeby.
1
Pomijam problemy związane z przeniesieniem języka, w którym napisane są programy wchodzące w skład
systemu.
28 pazdziernika 2001 roku wersja 0.2 70
Podprogramy obsługi urządzeń, bezpośrednio sięgające do ich rejestrów sterujących, tworzą
kolejną istotną część jądra. Obsługują przerwania zgłaszane przez urządzenia zewnętrzne i trosz-
czą się o obsługę błędów. Podprogramy obsługi są całkowicie zależne od sprzętu w końcu całe
ich zadanie polega na przesyłaniu właściwych danych do właściwych rejestrów urządzenia. Więk-
szość unixowych podprogramów obsługi napisano w języku C.
Trzecia istotna część jądra, a przy tym jedyna, która musi być napisana w asemblerze, sta-
nowi zbiór elementarnych procedur obsługi sprzętu. Tworzą one właściwą maszynę wirtualną.
Procedury te wpisują znaki do bufora drukarki, blokują i odblokowują przerwania, czytają in-
formacje z dysku, itp.
5.1.3 Funkcje systemowe
Shell oraz duże części jądra unixowego, zostały napisane w języku wysokiego poziomu. W pro-
gramach tych, a także w innych tworzonych przez użytkowników, czasami trzeba posłużyć się
różnymi operacjami, dla których nie istnieją standardowe instrukcje w języku wysokiego pozio-
mu. Są to na przykład:
zainicjowanie nowego procesu;
otwarcie pliku z zamiarem czytania;
zapisanie informacji w pliku;
odczytanie zegara systemowego;
zakończenie procesu;
zmiana praw dostępu do pliku.
Na ogół takie operacje zależne od maszyny są z natury proste, a te tworzące maszynę wir-
tualną zdefiniowaną przez jądro Unixa należą do najprostszych. Dostęp do nich uzyskuje się
za pomocą funkcji systemowych (ang. system calls), których działanie polega na pobieraniu
informacji z systemu, zapisywaniu informacji do rejestrów sprzętowych lub sięganiu do tablic
systemowych. Wywołania funkcji systemowych można traktować jako instrukcje, wykonywane
przez maszynę wirtualną można też powiedzieć, że to zbiór funkcji stanowi maszynę wirtualną.
Wywołania funkcji systemowych są poleceniami dla jądra. Można odwoływać się do nich
z programów pisanych w języku C, Fortranie, Pascalu czy wielu innych językach programowania,
dokładnie tak, jakby były zwykłymi funkcjami danego języka.
Lista funkcji systemowych rośnie stosunkowo wolno w miarę poprawiania i rozszerzania Uni-
xa. Niektóre operacje będące wynikiem wywołania funkcji można także wykonać wydając od-
28 pazdziernika 2001 roku wersja 0.2 71
powiednie polecenie shell-owi. Program wykonujący takie polecenie jest na ogół bardzo krót-
ki i ogranicza się do przeformułowania żądania i przekazania go do jądra w postaci wywoła-
nia funkcji systemowej. Naturalnymi przykładami są: chdir, kill, mount, sleep, umask,
wszystkie polecenia wykonania prostych operacji na katalogach, tablicach procesów, urządze-
niach zewnętrznych i zegarze.
Dalej opisano skrótowo kilka często używanych funkcji. Stanowią one jedynie wierzchołek
góry lodowej, reszta góry jest jednak pociągająca tylko dla specjalistów. Jeśli ktoś potrzebuje
pełniejszej informacji dotyczącej listy funkcji oraz trybu ich użycia, nie uniknie spędzenia paru
dni z dokumentacją2.
Zainteresowanych dokładniejszą budową jądra, oraz algorytmami używanymi do sterowania
Unixem powinni zajrzeć do książki M. J. Bacha [?].
5.2 Zarządzanie procesami
W systemie wielodostępnym równocześnie może wykonywać się wiele programów różnych użyt-
kowników. Ponieważ komputer zazwyczaj jest wyposażony tylko w jeden procesor, więc w rzeczy-
wistości tylko jeden program może działać w danej chwili. Określenie równocześnie w poprzed-
nim zdaniu oznacza, że wykonywanie programów przeplata się w czasie, a centralny procesor
jest przyznawany kolejno każdemu programowi na krótki czas. Mimo to w pamięci, o ile jest
ona wystarczająco pojemna, może równocześnie przebywać kilka programów. Jądro odpowia-
da za sposób wykorzystania przez programy czasu procesora i obszarów pamięci, innymi słowy
odpowiada za szeregowanie procesów i zarządzanie pamięcią. To ostatnie obejmuje nie tylko
dzielenie dostępnych części pamięci, lecz także decydowanie, czy i kiedy usunąć proces z pamięci
na dysk i sprowadzić go z powrotem do pamięci.
5.2.1 Inicjowanie procesu
W systemie Unix rozróżnia się proces od programu, przy czym proces można identyfikować jako
program wykonywany w ustalonym środowisku3. Przez środowisko rozumie się zbiór otwartych
plików, praw dostępu do nich, wartości przypisane zmiennym interpretatora, nazwę właściciela
procesu, a także szereg innych danych, niezbędnych do wykonywania programu, ale nie stano-
wiących jego integralnej części.
Zainicjowanie procesu w systemie Unix jest zawsze wynikiem działania innego procesu
jeden proces uruchamia drugi. Naturalnym efektem takiej koncepcji jest hierarchiczna struktu-
2
Szerzej wybrane funkcje systemowe opisane zostały w rozdziale ??.
3
Dla uproszczenia pominięto przypadki, w których wykonywany program składa się z więcej niż jednego pro-
cesu.
28 pazdziernika 2001 roku wersja 0.2 72
a) b)
c)
Rys. 5.1. Rozwidlanie procesów:
a) zastąpienie procesu tym samym procesem i jego potomkiem;
b) powstanie wielu procesów współbieżnych po rozwidleniu;
c) wielokrotne rozwidlenia tworzące hierarchię procesów
ra procesów. Hierarchia ta powstaje dzięki mechanizmowi rozwidlania (ang. fork). Polega on
na tym, że jądro zastępuje istniejący proces dwoma innymi (rys. ??a). Jednym jest oryginalny
proces, drugim proces zupełnie nowy. Oryginalny proces nazywa się przodkiem, nowo dodany
zaś potomkiem. Potomek zazwyczaj korzysta ze wszystkich plików swego przodka. Po rozwi-
dleniu oba procesy działają niezależnie, chyba że przodek jawnie zechce czekać na zakończenie
pracy potomka. Jeśli potomek (proces 2) musi uruchomić jeszcze jeden proces, może to uczynić
również rozwidlając się. Wynikiem są w tym przypadku trzy procesy działające równocześnie,
por. rys. ??b. Najnowszy proces (proces 3) jest traktowany jako potomek procesu 2. Będzie on
miał dostęp do plików otwartych przez dwa poprzednie procesy, choć one mogą nie mieć dostę-
pu do plików otwartych przez niego. Ogólnie pliki są dostępne dla procesów położonych niżej
w hierarchii.
Po rozpoczęciu sesji, jądro uruchamia proces shella, pracujący jako proces użytkownika
(np. proces1, rys. ??c). Proces 2 powstaje wtedy, gdy użytkownik wydaje pewne polecenie
interpretatorowi, proces ten może uruchomić proces 3. Tak więc proces 3 wykonuje się równo-
cześnie nie tylko z procesem 2, lecz także z procesem interpretatora. Interpretator może zresztą
ponownie rozwidlić się (np. jeśli procesy 2 i 3 są procesami drugoplanowymi), tworząc proces 4.
W ten sposób łatwo można tworzyć złożone hierarchie procesów.
28 pazdziernika 2001 roku wersja 0.2 73
5.2.2 Hierarehia procesów
Bieżącą strukturę hierarchii procesów można obejrzeć za pomocą polecenia ps. Po wyborze
odpowiednich opcji polecenie to wypisuje w zasadzie wszelkie informacje o procesach znanych
w danej chwili systemowi. Przykładową listę dla systemu z trzema pracującymi użytkownikami
(nieco poprawioną usunięto z niej zbędne informacje) pokazano na rys ??.
S UID PID PPID PRI SZ TTY TIME CMD
S 0 0 0 0 2 ? 0:02 swapper
S 0 1 0 30 15 ? 0:02 init
S 201 31 1 30 23 co 0:26 csh
S 201 32 1 28 20 02 0:13 sh
S 0 18 1 40 12 ? 0:24 update
S 14 23 1 26 26 ? 0:02 lpsched
S 0 27 1 26 26 ? 0:12 cron
S 0 33 1 30 20 03 0:16 sh
S 0 34 1 28 15 04 0:04 getty
S 201 175 31 28 46 co 5:55 vi
S 202 21l 32 30 20 02 0:02 sh
S 202 219 217 30 22 02 0:00 sh
S 202 220 217 26 7 02 0:02 tee
S 202 221 219 26 35 02 0:13 sed
S 202 222 220 26 14 02 0:13 deroff
R 202 223 220 54 66 02 0:10 sort
S 202 224 220 26 18 02 0:01 spelipro
S 202 225 220 26 65 02 0:02 spellpro
S 202 226 220 26 6 02 0:01 comm
R 0 227 33 54 26 03 0:14 ps
Rys. 5.2. Przykładowa lista procesów w systemie z trzema terminalami aktywnymi i jednym uśpionym
Lista zawiera informacje o stanie każdego procesu (S ang. sleeping śpiący, R run-
ning działający), numer identyfikacyjny użytkownika (UID), numer identyfikacyjny procesu
(PID), numer identyfikacyjny procesu przodka (PPID), priorytet procesu (PRI), jego rozmiar
(SZ) w blokach, numer terminala (TTY), z którym proces jest związany, czas (TIME) zużyty
do tej chwili przez proces, wreszcie polecenie (CMD), które spowodowało uruchomienie procesu.
Procesy systemowe nie są związane z żadnym terminalem, stąd znak zapytania w odpowiedniej
kolumnie.
28 pazdziernika 2001 roku wersja 0.2 74
Rys. 5.3. Struktura procesów w systemie cztero-terminalowym, w którym jeden użytkownik poprawia tekst, drugi
sprawdza pisownię, trzeci odczytuje stan procesów, terminal czwarty nie jest używany
W pierwszej chwili liczba równocześnie istniejących procesów może się wydać duża, ponadto
może dziwić fakt, że niektóre z nich nie są związane z terminalami. Takie procesy należą do
systemu i znajdują się na samej górze hierarchii procesów. Lepszy obraz struktury procesów
otrzymuje się rysując drzewo zależności przodek-potomek (rys. ??).
Procesy z lewej strony rysunku i na górze, opatrzone numerami poniżej 30 oraz proces 34
są procesami systemowymi. Pozostałe są wynikiem pracy użytkowników. Początkowe przygo-
towania poczynione przez system mają na celu zapewnienie możliwości inicjowania procesów,
przesyłania ich między pamięcią a dyskiem oraz uaktualnianiem informacji systemowej. Pro-
gram lpsched zajmuje się szeregowaniem informacji przesyłanych na drukarkę. Program cron
jest demonem zegarowym, pilnującym czasu i inicjującym akcje związane z jego upływem. Do
systemu jest dołączony jeszcze czwarty terminal, w danej chwili nieaktywny. Jednak program
getty od czasu do czasu sprawdza, czy ktoś nie próbuje rozpocząć z niego sesji.
Przedstawione drzewo odpowiada stanowi systemu, w którym jeden użytkownik (przy kon-
soli) poprawia jakiś tekst edytorem vi, a drugi (terminal 2) właśnie uruchomił program spraw-
dzający poprawność pisowni. Program spell inicjuje kilka innych procesów, proces 32 jest więc
korzeniem całego poddrzewa procesów, w tym dwóch procesów interpretatora poleceń (sh).
Wszystkie te procesy są uśpione, z wyjątkiem procesu sortującego (proces 223), który prawdo-
podobnie w danej chwili sortuje słowa. Terminalowi 3 przypisano także proces interpretatora.
To właśnie ten interpretator uruchomił program wykonujący polecenie ps.
5.2.3 Przydzielanie pamięci
Unix jest systemem wieloprogramowym, który umożliwia równoczesną pracę wielu procesów,
wykonujących wiele programów znajdujących się równocześnie w pamięci. W normalnych wa-
runkach każdy program jest ładowany do innego obszaru pamięci operacyjnej. Operacje związane
z podziałem czasu mogą się wówczas odbywać bez zapisywania obrazu pamięci na dysk i z po-
wrotem. Każdy program wykonuje się w przyznanym przedziale czasu, w pozostałych okresach
pozostając milcząco w pamięci. Jedynie rejestry procesora muszą być dzielone między progra-
mami, a więc program zarządzający wymienia tylko zawartość tych rejestrów. Jądro systemu
śledzi procesy w danej chwili aktywne oraz decyduje czy i kiedy należy dokonać wymiany, a
także gdzie w pamięci umieścić nowo zainicjowane programy. Po zakończeniu procesu bada, czy
zakończył się on powodzeniem i ustawia odpowiednio kod zakończenia.
28 pazdziernika 2001 roku wersja 0.2 75
Programy unixowe mogą mieć jedną z dwóch postaci: wielowchodliwą (ang. re-entrant) lub
zwyklą. W przypadku tej pierwszej wszystkie instrukcje programu (a także rozmaite stałe) gru-
puje się w pamięci osobno oddzielając je od modyfikowalnych struktur danych. W drugim przy-
padku oba rodzaje informacji mogą być przemieszane. Podczas wykonywania procesów, których
kody są wielowchodliwe, przydziela się im trzy odrębne obszary pamięci: segment kodu, segment
danych i segment stosu. Pierwszy z nich zawiera kod programu, którego nie wolno modyfikować.
Drugi wszystkie struktury danych, zmienne itp. W trzecim natomiast gromadzi się informacje,
niezbędne do zachowania stanu procesu przy przyznawaniu mu i odbieraniu zasobów (proceso-
ra, pamięci operacyjnej, itp.)4. W programach nie mających postaci wielowchodliwej nie można
oddzielić stosu i danych od treści programu.
Kod i dane programu warto rozdzielać z dwóch powodów. Po pierwsze zmniejsza się w ten
sposób rozmiar informacji, przesyłanej między pamięcią a dyskiem, gdyż segment kodu nie musi
być kopiowany z pamięci na dysk. Ponieważ segment ten nigdy nie ulega modyfikacjom, to jego
zawartość zawsze pozostaje taka, jak w inicjalnym obrazie zapisanym na dysku. Po drugie, w
przypadku niektórych często używanych programów, takich jak edytor vi, w dużych systemach
zdarza się, że wielu użytkowników korzysta z nich w tej samej chwili. Dla samego interpreta-
tora musi istnieć co najmniej tyle procesów, ilu jest użytkowników systemu, gdyż dla każdego
użytkownika w chwili rozpoczynania sesji powstaje nowy proces. Jeśli kilku użytkowników chce
korzystać z tego samego kodu programu, nie trzeba tworzyć kilku kopii segmentu kodu, gdyż
wszystkie one będą identyczne. Wystarczy utworzyć jeden segment danych i jeden segment stosu
dla każdego użytkownika i w razie potrzeby przesyłać je między pamięcią a dyskiem. Wielkość
obu tych segmentów rzadko jest znacząca w porównaniu z kodem dużych programów.
5.2.4 Podział czasu i innych zasobów
W wieloprogramowym systemie operacyjnym na ogół nie pozwala się pojedynczym procesom
pracować aż do końca bez przerwy, lecz przydziela im się jedynie kwanty czasu procesora we-
dług jakiejś cyklicznej reguły. Co więcej zazwyczaj nie przydziela się kolejno równych kwantów,
zapotrzebowanie różnych procesów może się bowiem znacząco różnić [?].
Długość kwantów czasu przydzielonych pojedynczym procesom zależy od wielu czynników,
wśród nich od priorytetu procesu, dostępności jego danych wejściowych oraz urządzeń wyjścio-
wych. O niektórych czynnikach decyduje się na podstawie analizy samego procesu. Na inne mają
wpływ potrzeby sąsiednich procesów (np. w przypadku oczekiwania na dane utworzone przez
inny program) albo stan sprzętu (np. drukarka może być zajęta). Unix, tak jak większość syste-
mów operacyjnych, przydziela kwanty czasu w taki sposób, aby zmaksymalizować wykorzystanie
4
Pamiętane tam są także informacje używane przy wykonywaniu podprogramów, zwłaszcza rekurencyjnych.
28 pazdziernika 2001 roku wersja 0.2 76
zasobów systemu dając równocześnie pierwszeństwo zadaniom o najsurowszych wymaganiach.
Czas współzawodniczącym procesom przydziela zgodnie z ich priorytetami jądro unixa. Prio-
rytety wyraża się liczbowo, przy czym im większa liczba, tym niższy priorytet. Zadaniom naj-
ważniejszym przypisuje się wartości najmniejsze. Listę bieżących wartości priorytetów można
uzyskać wydając polecenie ps. Wartości te są zmieniane cyklicznie, na ogół w odstępach kilku-
sekundowych. Procesy z wysokim stosunkiem zużytego czasu procesora do czasu rzeczywistego
przesuwa się w dół, natomiast te wymagające mniej obliczeń w górę. W ten sposób użyt-
kownicy, którzy mają do wykonania dużo pracy interakcyjnej (np. wprowadzający program z
klawiatury) otrzymują wysoki priorytet, co zapewnia im prawie natychmiastową odpowiedz sys-
temu. Równocześnie można przyjąć, że w przypadku zadania z dużą ilością obliczeń użytkownik
i tak będzie musiał trochę poczekać na odpowiedz, a więc niewielkie wydłużenie czasu oczekiwa-
nia nie będzie dla niego utrudnieniem. Z podobnych powodów zadania systemowe mają wyższy
priorytet niż zadania użytkowników. Ponieważ priorytety są regularnie aktualizowane, procesy
zmieniające dynamicznie swój charakter będą miały niewłaściwy priorytet bardzo krótko. Na
przykład zadaniu obliczeniowemu uruchomionemu z wysokim priorytetem system wkrótce ob-
niży priorytet zwiększając jego wartość. Na odwrót, proces mający początkowo niski priorytet,
jeśli mało korzysta z procesora, natomiast dużo z terminala, będzie się przesuwał w górę.
Użytkownicy mogą dwojako wpływać na wysokość priorytetu procesu: deklarując proces jako
drugoplanowy lub żądając zwiększenia wartości priorytetu (zwanego w slangu użytkowników nice
number). Służy do tego polecenie nice. Normalny użytkownik może tylko zwiększyć wartość
priorytetu, przywilej zmniejszania jej jest zastrzeżony dla administratora systemu.
Po upływie czasu przyznanego procesowi, gdy nadchodzi kolej na następny proces, może
się okazać, że aby pomieścić w pamięci operacyjnej drugi, trzeba usunąć z niej ten pierwszy.
Usunięcie procesu polega na zapisaniu jego obrazu w pliku dyskowym. Umieszczając proces po-
nownie w pamięci, kopiuje się do niej zawartość owego pliku i odtwarza taki stan komputera,jaki
był w chwili usuwania procesu. W ten sposób proces może wznowić wykonywanie dokładnie od
miejsca, w którym je przerwał, jedynie z pewnym opóznieniem. Obraz procesu zapisany w pliku
obejmuje wszystkie modyfikowalne obszary pamięci, zawartość rejestrów maszynowych, nazwę
bieżącego katalogu, listę otwartych plików i trochę innych istotnych informacji.
5.2.5 Funkcje fork, exec i wait
Aby wyjaśnić sposób funkcjonowania praw dostępu i ustalania priorytetów procesów unixowych,
należy przyjrzeć się nieco dokładniej procedurze rozwidlania. Funkcja fork tworzy nowy proces,
przy czym po rozwidleniu zarówno przodek, jak i potomek są aktywni. Terminów przodek i
potomek użyto w znaczeniu bardzo umownym, gdyż oba procesy są takie same. Inaczej mówiąc
funkcja fork powołuje do istnienia parę procesów, wyposażonych w ten sam program i w prawie
28 pazdziernika 2001 roku wersja 0.2 77
takie samo środowisko prawie, gdyż jeden z nich jest bowiem wpisany do tablic systemowych
jako potomek drugiego. Procedura ta może się wydać nieco dziwna, nie jest jednak pozbawiona
logiki: ponieważ oba procesy są identyczne, nie trzeba ładować do pamięci ani usuwać z niej kodu
programu, tworzyć fizycznej kopii czegokolwiek z wyjątkiem modyfikowalnych obszarów danych.
Do zmiany wykonywanego programu służy funkcja exec, określająca nazwę programu. Właściwie
należałoby mówić o kilku funkcjach, mających podobny skutek, lecz różniących się sposobem
przekazywania argumentów. Tandem fork--exec najpierw małym kosztem tworzy kopię procesu
macierzystego, a następnie zastępuje kod programu procesu potomnego programem, który miał
być wywołany.
Podczas normalnej interakcyjnej pracy, z klawiatury, proces jest uruchamiany natychmiast
po wydaniu odpowiedniego polecenia, proces uruchamiający (zazwyczaj jest to shell) czeka na
zakończenie pracy procesu potomnego. Samo rozwidlenie tu nie wystarczy. Aby przenieść proces
w stan oczekiwania, trzeba użyć funkcji wait (czekaj ). Powoduje ona, że proces macierzysty
zawiesza się aż do chwili, gdy jądro da znać, że proces potomny zakończył pracę.
Szczegółowo przebieg rozwidlania procesów omawia rozdział ??.
5.2.6 Identyfikator skuteczny użytkownika
Tandem funkcji fork-exec tworzy nowy proces, dziedziczący środowisko procesu macierzyste-
go, a następnie podmienia kod procesu. Jeśli nie dokonano świadomych zmian, to parametry
środowiska pozostają takie, jak w procesie macierzystym: ten sam właściciel, ten sam kata-
log bieżący, ten sam terminal. Takie rozwiązanie jest dobre w większości przypadków. czasami
jednak występują problemy dotyczące praw dostępu.
Przedstawiony zostanie tu krótko przykładowy problem oraz sposób jego rozwiązania.
Niektóre programy zainstalowane w systemie Unix są wprawdzie ogólnie dostępne, ale po
uruchomieniu muszą korzystać z plików o ściśle ograniczonych prawach dostępu. Na przykład
każdy może uruchomić program passwd, ale do pliku z hasłami użytkowników (/etc/passwd i
/etc/shadow) ma normalnie dostęp jedynie administrator systemu. Pojawia się konflikt: użyt-
kownik kazio mógłby uruchomić program passwd, lecz ten program nie miałby prawa zmiany
zawartości pliku z hasłami, gdyż dostęp do niego ma tylko jego właściciel, czyli administra-
tor. Problem tej samej natury pojawia się w wielu zastosowaniach komercyjnych, np. gdy kilku
urzędników ma prawo dopisywania informacji do bazy danych, jednak żaden z nich nie powi-
nien mieć prawa nieograniczonego dostępu do całego pliku zawierającego bazę. Problem ten
rozwiązuje się używając pewnego triku. Zazwyczaj wtedy, gdy program shell, z którego ko-
rzysta użytkownik kazio, inicjuje nowy proces wykonujący program passwd, proces potomny
dziedziczy użytkownika kazio jako swojego właściciela, co grozi konfliktem przy próbie dostępu
do plików stanowiących własność samego systemu. Jednak zmiana identyfikatora użytkownika
28 pazdziernika 2001 roku wersja 0.2 78
w procesie potomnym na identyfikator administratora, spowodowaloby, że proces miałby dostęp
do wszystkich plików. Pozorny właściciel procesu powinien być więc różny od właściciela proce-
su macierzystego. W żargonie unixowym identyfikator użytkownika procesu potomnego nazywa
się identyfikatorem skutecznym użytkownika, w przeciwieństwie do identyfikatora faktycznego,
odziedziczonego z procesu macierzystego. Identyfikator skuteczny należy do środowiska procesu
potomnego, a nie macierzystego, a więc jego ważność kończy się wraz z zakończeniem pracy
procesu potomnego.
Każdy proces może zatem należeć albo do tego samego użytkownika, co jego proces macie-
rzysty, albo do właściciela pliku zawierającego program, wykonywany przez proces. W chwili
uruchamiania nowego procesu następuje ustawienie identyfikatora skutecznego zgodnie z iden-
tyfikatorem właściciela pliku, jeśli plik ten ma ustawiony bit SUID (ang. set user identity).
Wówczas na liście zawartości katalogu plik jest oznaczony nie tylko jako wykonywalny, lecz
także jako wykonywalny ze zmianą identyfikatora, co sygnalizuje litera s użyta zamiast litery x:
-rws--x--x 1 sysinfo 12826 df
-rws--x--x 1 root 6898 mkdir
-rws--x--x 1 root 19168 passwd
-rws--x--x 1 sysinfo 25525 ps
-rws--x--x 1 root 7181 rmdir
Możliwość ustawiania identyfikatora skutecznego właściciela jest niebezpieczna dla mechani-
zmów ochrony zasobów w systemie. Jeśli np. program należący do administratora rozwidli się i
utworzy nowy shell, to ów shell odziedziczy identyfikator właściciela i będzie miał nieograniczony
dostęp do całego systemu!
5.2.7 Demon zegarowy
Procesy, których wykonanie jest zależne od czasu, w systemie Unix synchronizuje zegar systemo-
wy za pośrednictwem programu cron, wykonywanego jako proces opisywany w podręcznikach
pod nazwą demon zegarowy5.
Podczas pierwszego uruchomienia demon sprawdza w tablicach systemowych, kiedy nastąpi
pierwsze zdarzenie, wymagające jego udziału. Każde zdarzenie określa proces, który należy za-
inicjować w zadanej chwili. Po sprawdzeniu listy demon usypia i budzi się dokładnie wówczas,
gdy przychodzi pora na rozwidlenie pierwszego zapisanego procesu. Uruchomiwszy proces demon
ponownie szuka w tablicy następnego zdarzenia i usypia. Administratorzy systemu intensywnie
5
Po angielsku proces ten nazywa się daemon, co oznacza herosa z mitologii greckiej; proces byłby więc bogiem
czasu, zapewniającym właściwą kolej rzeczy. Po polsku słowo demon pasuje tu chyba lepiej niż heros, choć
oczywiście nabiera nieco złowieszczej wymowy [?].
28 pazdziernika 2001 roku wersja 0.2 79
korzystają z programu cron w zadaniach administracyjnych, gdyż demon o niczym nie zapomina
ani nie ma nic przeciw uruchamianiu np. programów rozliczeniowych w nocy przed świtem, gdy
komputer jest słabiej obciążony. Nie tylko administrator, lecz także zwyczajni użytkownicy mogą
zlecać demonowi troskę o swoje procesy. Do zapisania w odpowiednich tablicach systemowych
programu, który ma być uruchamiany cyklicznie, służy polecenie crontab.
Ważną zaletą programu cron jest to, że wszelka informacja, którą uruchomiony proces wpisu-
je do standardowego strumienia wyjściowego w czasie, gdy zleceniodawca nie korzysta z systemu
jest przesyłana mu pocztą elektroniczną. Nic nie zostanie więc stracone. Wadą natomiast to, że
wyłączanie komputera w chwili, gdy powinien być zainicjowany zlecony proces, spowoduje zapo-
mnienie go na zawsze nie będzie on bowiem wznowiony po ponownym włączeniu komputera.
5.3 Operacje wejścia-wyjścia
Dla większości programów (np. sh) wszelkie operacje wejścia-wyjścia wyglądają tak, jak operacje
na plikach. Nic nie wiadomo w nich o istnieniu jakichkolwiek urządzeń zewnętrznych. Dla jądra
natomiast zupełnie odwrotnie, jego zadanie polega właśnie na ukryciu szczegółów prawdziwych
urządzeń fizycznych pod postacią pozornych plików.
5.3.1 Niezależność od urządzeń
W większości dzisiejszych systemów operacyjnych do plików znajdujących się w różnych urzą-
dzeniach fizycznych można odwoływać się w podobny sposób. O systemach takich mówi się, że są
w dużej mierze niezależne od urządzeń. Niezależność tę osiąga się definiując fikcyjne, wirtualne
urządzenie, funkcjonalnie odpowiadające dyskowi, na którym umieszcza się wszystkie pliki, nie-
zbędne do uruchomienia programów. Oczywiście żadne prawdziwe urządzenie nie ma wszystkich
cech takiego urządzenia wirtualnego. Dla każdego urządzenia należy więc stworzyć specjalny
podprogram obsługi (ang. device driver), który będzie tłumaczył żądane operacje urządzenia
wirtualnego na dostępne operacje urządzenia fizycznego. Programy użytkownika mogą wówczas
komunikować się z dowolnymi nowymi urządzeniami dołączonymi do komputera, jeśli tylko na-
pisano dla nich podprogram obsługi.
Uniezależniając Unix od urządzeń wprowadzono zasadę, że wszystkie urządzenia fizyczne w
systemie mają być dla użytkownika po prostu normalnymi plikami. Ponieważ jednak takie pliki
różnią się co nieco od plików stworzonych przez użytkownika, nazywa się je plikami specjalnymi.
Dla plików specjalnych również określa się prawa dostępu, są one zresztą niezbędne, np. drukarka
nadaje się tylko do pisania i wobec tego należy wszystkim zabronić możliwość czytania z niej.
Pliki specjalne znajdują się w katalogi /dev.
28 pazdziernika 2001 roku wersja 0.2 80
5.3.2 Pierwszy poziom obsługi przerwań
Wiele zdarzeń, zachodzących w wielodostępnym systemie komputerowym, ma związek z upły-
wem czasu rzeczywistego i wymaga natychmiastowej reakcji. Takim zdarzeniem jest np. naciśnię-
cie przez użytkownika klawisza na klawiaturze terminala. Po kilkuset milisekundach użytkownik
może nacisnąć następny klawisz, a więc akcja związana z pierwszym klawiszem musi się w tym
czasie zakończyć.
Zdarzenia wymagające natychmiastowej reakcji są sygnalizowane jądru przez sprzęt. Każde
takie zdarzenie powoduje przerwanie: program wykonywany w danej chwili, czymkolwiek by
był, zostaje wstrzymany po zakończeniu cyklu wykonywania bieżącej instrukcji maszynowej, a
sterowanie jest przekazywane do innego, zazwyczaj bardzo małego podprogramu zwanego pro-
cedurą obsługi przerwania. Procedura określa rodzaj przerwania, wykonuje wszelkie niezbędne
operacje stanowiące następstwo przerwania, a wreszcie przekazuje sterowanie ostatnio wykony-
wanemu programowi. Obsługa przerwania trochę jest podobna do wywołania podprogramu
program główny pozostaje w stanie oczekiwania, w tym czasie natomiast wykonuje się pewien
inny ciąg instrukcji. Jednakże w przeciwieństwie do podprogramu wykonanie procedury obsługi
przerwania jest inicjowanie przez sprzęt i przebiega asynchronicznie, w odpowiedzi na pewne
zdarzenie zewnętrzne.
Ponieważ w systemie wielodostępnym może być wiele terminali a każdy z nich może przesyłać
kilka znaków na sekundę, czas obsługi pojedynczego przerwania jest mały. Prawie wszystkie sys-
temy operacyjne stosują więc dwupoziomową obsługę znaków z klawiatury. Program pierwszego
poziomu obsługi przyjmuje kolejny znak z klawiatury, sprawdza, czy nie jest to znak specjal-
ny wymagający natychmiastowej obsługi (np. DELETE), umieszcza go w buforze klawiatury,
a następnie wypisuje echo znaku na terminalu. Użytkownik może odnieść wrażenie, że znaki
są magazynowane przez terminal, program obsługi przerwań bardzo szybko bowiem wypisuje
ich echo. Zresztą nawet przy kilku użytkownikach naciskających klawisze w szalonym tempie
komputer będzie miał dużo czasu na wykonywanie innych zadań.
Jeśli okaże się, że znak wprowadzony z klawiatury wymaga podjęcia jakichś akcji, program
pierwszego poziomu przywołuje program drugiego poziomu. Program ten określa rodzaj koniecz-
nej akcji i wykonuje ją. Na przykład po otrzymaniu znaku końca wiersza muszą być znalezione
w nim znaki usuwania i kasowania, a następnie dokonane odpowiednie poprawki. Przetwarzany
tekst jest potem umieszczany w kolejce, z której przesyła się go do programu czekającego na
dane. Ta operacja prawdopodobnie będzie trwała dłużej, niż samo magazynowanie podanych
poleceń.
28 pazdziernika 2001 roku wersja 0.2 81
5.3.3 Pliki specjalne: blokowe i znakowe
Istnieją dwa podstawowe rodzaje plików specjalnych (zdefiniowane w rozdziale ??): blokowe i
znakowe. Wzorcami dla nich są odpowiednio pliki dyskowe i terminale. Innymi słowy, Unix roz-
poznaje dwa rodzaje urządzeń zewnętrznych dysko-podobne i terminalo-podohne. Wszystkie
inne system upodabnia najpierw na siłę do jednego z tych urządzeń, a następnie sprawia, że
wszystkie urządzenia wyglądają dla użytkownika jak pliki dyskowe.
W blokowym wejściu-wyjściu korzysta się ze zbioru buforów danych. Na ogół jest ich kilkana-
ście lub więcej. Podczas normalnej pracy przyznaje się, je użytkownikom w miarę potrzeby. Jeśli
proces zażąda danych, jądro szuka ich w jednym z buforów. Po znalezieniu są one przekazywane
procesowi bez przesyłania ich między pamięcią a dyskiem. Z kolei żądanie zapisania danych jest
żądaniem zapisania do buforu. Zawartość buforu przesyła się na dysk dopiero pózniej, gdy bufor
jest potrzebny do innego celu, albo gdy ktoś zażąda opróżnienia buforów. Ponieważ bufory nie
mają ustalonych właścicieli, zawartość buforu wyjściowego nie jest przesyłana natychmiast po
jego napełnieniu, lecz dopiero po napełnieniu wszystkich buforów, gdy jakiś proces zażąda na-
stępnych. Bufory wejściowe są zapełnione na skutek wyprzedzającego czytania danych, tworzenie
wyników wiąże się natomiast z opóznionym zapisywaniem. Operacje wejścia-wyjścia przebiegają
więc asynchroniczne względem programów, programy jednak rzadko muszą czekać na przesłanie
danych.
Operacje wejścia-wyjścia związane z urządzeniami przetwarzającymi znaki przebiegają w
sposób następujący: podprogram obsługi zajmuje się tylko pojedynczymi znakami. Dla znaków
o specjalnym znaczeniu wykonuje konieczne akcje, pozostałe znaki przekazuje dalej. Na przykład
znak powrotu karetki, czyli końca wiersza w pliku unixowym, musi być przesłany do terminalu
jako para znaków, powrotu karetki i przejścia do następnego wiersza. Podobnie jeśli terminal nie
rozpoznaje znaku tabulacji, program obsługi musi przesłać odpowiedni ciąg spacji, a terminale i
drukarki nie przesuwające kursora lub papieru do następnej strony muszą zamiast tego otrzymać
odpowiednią liczbę znaków przejścia do następnego wiersza. Wszystkie takie przekształcenia
wykonuje podprogram obsługi.
Dla blokowych urządzeń wejścia-wyjścia podprogramy obsługi są zazwyczaj bardzo proste,
gdyż wszystkie operacje dotyczą standardowych buforów. Urządzenia znakowe wymagają bar-
dziej skomplikowanych podprogramów, oprogramowanie zarządzające buforami może być nato-
miast mniej wyszukane.
Sposób organizacji unixowego wejścia-wyjścia jest na ogół niewidoczny dla użytkownika,
któremu może się wydawać, że operacje te wykonują się synchronicznie z programem, tzn. dane są
czytane, a wyniki zapisywane dokładnie w takiej kolejności, w jakiej to wynika z tekstu programu.
Jednak w razie jakiejkolwiek awarii złożoność unixowego schematu buforowania może stać się
28 pazdziernika 2001 roku wersja 0.2 82
Rys. 5.4. Fizyczna struktura plików (budowa i-węzła) w systemie Unix
kłopotliwa. Na przykład giną wyniki, znajdujące się w buforach w porę nie wydrukowane
lub zapisane w pliku, chociaż z punktu widzenia programu ich tworzenie zostało całkowicie
zakończone.
5.3.4 Fizyczna struktura plików
Użytkownicy rzadko muszą się troszczyć o fizyczną strukturę plików, ponieważ system Unix
sprawia, że każdy plik wydaje się być nieprzerwanym ciągiem bajtów (znaków). Jednak czasami
choćby pobieżna znajomość fizycznej struktury plików pozwala na napisanie sprawniejszych
programów użytkowych.
Pliki dyskowe w systemie Unix fizycznie dzieli się na bloki. W pierwszych wersjach Unixa
bloki miały niezmiennie po 512 bajtów. Ostatnio dopuszcza się zarówno bloki 512, 1024 bajtowe
i dłuższe. Każdemu plikowi przydziela się całkowitą liczbę bloków, przy czym początek każdego
bloku przypada na wielokrotność 512 bajtów od początku pliku. Kolejne bloki pliku nie muszą
koniecznie sąsiadować ze sobą na dysku, mogą leżeć gdziekolwiek. Zaletą tego rozwiązania jest
to, że nie trzeba nigdy specjalnie scalać wolnych obszarów. Równocześnie Unix stara się przydzie-
lać plikom spójne obszary, gdyż dostęp do spójnych plików jest znacznie szybszy. Taki stopień
fragmentacji pamięci dyskowej, przy którym każdy plik składa się z niewielkich kawałków roz-
sypanych po całym dysku zdarza się tylko wówczas, gdy dysk jest prawie całkowicie zapełniony.
Zarządzanie rozproszonymi plikami wymaga jednak więcej finezji, niż tylko stworzenie prostej
listy adresów dla plików ciągłych. Wiele przykładów rozwiązań dostarczają książki z zakresu
teorii systemów operacyjnych np. [?].
Pliki zwykle i katalogi można podzielić na cztery klasy ze względu na ich rozmiar: do 10,
266, 65.802 i 16.843.018 bloków (zakładając 1024 bajtowy rozmiar bloków). Przyczyną tej na
pozór niezrozumiałej klasyfikacji jest zarezerwowanie w metryczce pliku6 miejsca na zapamię-
tanie numerów 13 bloków każdego pliku. Znaczenie tych numerów dla różnego rozmiaru plików
przedstawia rys. ??. W rysunku tym przyjęto 13 pozycyjną tablicę i-węzła, wielkość i-węzła
1 kB, numery bloków jako liczby 32 bitowe (4 bajty), wówczas blok może pomieścić 256 takich
adresów.
Adresowanie małych plików jest bardzo proste. Jeśli plik zajmuje nie więcej niż 10 bloków,
to obszar zarezerwowany w metryczce służy do zapamiętania faktycznych numerów bloków.
Na przykład niech plik zajmuje 3 bloki, wówczas 13 zapisanych pól w i-węzle ma następujące
6
Metryczka pliku ang. i-node, w polskojęzycznej literaturze często spotyka się również nazwę i-węzeł.
28 pazdziernika 2001 roku wersja 0.2 83
wartości: pierwsze 3 wskazują adresy fizycznych bloków na dysku, w pozostałe pola wpisywane
są zera, które oznaczają nie wykorzystane pola. W ten sposób można zaadresować 10 bloków,
3 ostatnie numery są bowiem zarezerwowane do innych celów. Ten schemat jest piękny w swej
prostocie, ale dotyczy tylko małych plików. Dziesięć bloków odpowiada 10 kB adnych.
Większe pliki wymagają adresowania pośredniego. Pierwsze 10 z 13 pól w i-węzle służy do
zaadresowania pierwszych bloków zawierających dane, dokładnie tak samo jak w przypadku
małych plików. Jedenaste pole nie jest adresem danych, lecz bloku w którym zapamiętuje się
adresy 256 dalszych bloków, również zawierających dane. Ponieważ może istnieć 256 dalszych
bloków, plik zapisany w ten sposób będzie zajmować nie więcej niż (10+ 256) = 266 bloków,
czyli 266 kilobajtów. Z tego pierwszych 10 bloków adresuje się bezpośrednio, a pozostałe
pośrednio poprzez jedenasty blok.
Do zapamiętania jeszcze większych plików wprowadza się drugi poziom pośredni. Znaczenie
pierwszych jedenastu numerów bloków jest dokładnie takie, jak poprzednio; dwunasty adres
wskazuje na i-węzeł zawierający do 256 adresów bloków (i-węzłów), w których z kolei umieszcza
się numery 256 bloków danych. W ten sposób można zaadresować maksymalnie 65.802 bloki
danych, a więc całkowity obszar adresowanych danych wynosi bloki,
czyli z grubsza 64 megabajty.
Największe pliki są zorganizowane podobnie z tym że trzynasty numer bloku służy do adreso-
wania trzy-poziomowego. Pozwala to zaadresować dodatkowo 16.777.216 (256*256*256) bloków,
największy możliwy plik ma więc rozmiar ok. 16 gigabajtów.
Metodę tę można w zasadzie rozszerzać o bloki poczwórnie-pośrednie, pięciokrotnie-pośrednie,
itd. W praktyce adresowanie potrójnie-pośrednie wystarcza w większości zastosowań Unixa.
Katalogi traktuje się tak samo, jak pliki zwykłe, chociaż rzadko osiągają one bardzo duże
rozmiary.
W przypadku plików specjalnych pierwszy z trzynastu numerów bloków ma inne znaczenie.
Jest on traktowany jako słowo, którego górna połowa zawiera identyfikator typu urządzenia
fizycznego (np. stacja taśmy magnetycznej), a dolna numer urządzenia (np. stacja numer 7).
To ograniczenie rozmiaru plików specjalnych nie sprawia kłopotów, gdyż i tak nie przekraczają
one kilku kilobajtów.
5.3.5 Dostęp sekwencyjny i swobodny
System Unix traktuje wszystkie urządzenia i nośniki tak samo jako magazyny plików skła-
dających się z ciągów znaków. Różne nośniki fizyczne różnią się jednak swoimi cechami, a więc
w praktyce różne są sposoby dostępu do plików.
Na przykład dysk magnetyczny jest urządzeniem o dostępie swobodnym, czas dostępu do
różnych obszarów jest prawie identyczny, wszystkie znaki na dysku są zatem jednakowo dostęp-
28 pazdziernika 2001 roku wersja 0.2 84
ne. Czytanie i pisanie może odbywać się w różnej kolejności, także kilkakrotnie w tym samym
miejscu. Trzeba więc pilnować, gdzie (skąd) należy przesłać kolejny znak. Służy do tego bar-
dzo prosty mechanizm: wskaznik pozycji, początkowo ustawiony na początek pliku, a następnie
przesuwany wraz z każdym zapisem lub odczytem. System zawsze czyta i pisze dane w miejscu
wskazanym przez wskaznik pozycji, dostęp swobodny zatem polega jedynie na zmianie wartości
wskaznika.
Niektóre urządzenia, np. klawiatura czy drukarka, są ściśle sekwencyjne. Nie można odczytać
znaków raz wysłanych do drukarki, czytanie (lub pisanie) z innych urządzeń zawsze odbywa się
w kolejności wymuszonej przez samo urządzenie. W Unixie jednolicie traktuje się wszystkie urzą-
dzenia za cenę udawania, że są one plikami i wyposażania ich we wskaznik pozycji. Oczywiście
wskaznik związany z urządzeniem sekwencyjnym może być przesuwany tylko w przód.
Urządzenia o cechach pośrednich, np. taśmy magnetyczne, logicznie nie różnią się od plików
dyskowych. Różna jest natomiast ich reakcja fizyczna: używanie powiedzmy taśm magnetycznych
jako urządzeń o dostępie swobodnym może wymagać częstego przewijania i powodować skrajnie
powolne działanie.
Oprócz swych ściśle sekwencyjnych cech, pewne urządzenia (np. klawiatury czy drukarki)
narzucają również inne ograniczenia z jednych można tylko czytać, na inne tylko pisać.
Te własności fizyczne nie powodują jednak żadnych problemów logicznych, gdyż Unix i tak
korzysta z systemu praw pisania lub czytania. Urządzenie fizyczne, z którego nie można czytać
jest równoważne plikowi bez prawa czytania. Tak więc urządzenia jednokierunkowe mieszczą się
bez kłopotów w ogólnej strukturze Unixa.
W systemie Unix można korzystać z plików o dostępie swobodnym w programach napisanych
w językach wysokiego poziomu, np. w C, a przeciętny użytkownik nigdy nie musi się troszczyć
o to, jak odbywa się fizyczny dostęp.
5.3.6 Buforowanie
O buforowaniu obsługi plików wspominał już rozdział ??. Z punktu widzenia zwykłego użyt-
kownika operacje czytania i pisania plików przebiegają synchronieznie z programami. Wszystkie
instrukcje typu read() i write() programu w C lub odpowiednie instrukcje w innych językach
programowania, według użytkownika są wykonywane dokładnie w chwili i w miejscu pojawienia
się ich w programie. Nie widać buforowania, każda operacja czyta lub pisze dokładnie podaną
liczbę bajtów, niezależnie od rozmiaru bloku dyskowego.
Ponieważ pliki są fizycznie podzielone na bloki, które nie muszą znajdować się obok siebie
na dysku, takie synchroniczne i niebuforowane zachowanie można osiągnąć korzystając przy
czytaniu i pisaniu z faktycznego pośrednictwa buforów. Buforowanie nie jest proste. Na przykład
instrukcja czytania powoduje odczytanie z dysku całego bloku do bufora w pamięci, chociaż
28 pazdziernika 2001 roku wersja 0.2 85
następnie z bufora do obszaru programu kopiuje się tylko żądaną liczbę bajtów. Pisanie również
polega na skopiowaniu znaków z obszaru programu do bufora, jednak dopiero po wypełnieniu
bufora informacja jest faktycznie przesyłana na dysk. Przypuśćmy, że program użytkowy czyta
kolejne 64-znakowe rekordy instrukcją read(). Informacje z dysku będą przesyłane tylko raz na
kilka żądań czytania, przy założeniu, że są to żądania dostępu do kolejnych rekordów. Jeśli jednak
będą to żądania przeczytania dowolnie rozmieszczonych 64-znakowych rekordów mieszczących
się każdy w innym bloku, to każde żądanie będzie wymagać osobnego dostępu do dysku.
Ponieważ w wielu programach użytkowych czyta się i pisze kolejne rekordy danych, dla
Unixa zaprojektowano bardziej złożony schemat buforowania, zgodnie z którym kolejny blok
jest odczytywany zanim jest on potrzebny. Dane dla większości żądań czytania znajdują się już
w buforze, nie trzeba zatem czekać na fizyczny ruch głowic lub taśmy. Takie przyspieszenie jest
widoczne, zwłaszcza w przypadku dużych plików wymagających kilku poziomów pośredniego
adresowania w celu znalezienia kolejnego bloku.
Dane przechowuje się zawsze w blokach. Programy intensywnie korzystające z wejścia-wyjścia,
o ile czyta się w nich i pisze dane porcjami, będącymi wielokrotnościami lub dzielnikami rozmia-
ru bloku, teoretycznie powinny pracować nieco szybciej. Jednak unixowy schemat buforowania
zmniejsza ewentualne przyspieszenie i większość programistów nie dba o szczegóły na tym po-
ziomie, może z wyjątkiem przypadków zupełnie swobodnego dostępu do pliku.
Buforowanie danych przesyłanych do lub z plików specjalnych plików blokowych i znako-
wych odbywa się odmiennie. Pliki blokowe, tzw. pliki specjalne odpowiadające urządzeniom
o strukturze blokowej, obsługuje się tak samo, jak pliki zwykłe. Pliki specjalne o strukturze
znakowej muszą oczywiście manipulować pojedynczymi znakami. Ich schemat buforowania jest
zatem całkiem prosty, bez wielu wymyślnych szczegółów buforowania blokowego.
5.3.7 Mechanizmy dostępu do plików
Zanim program będzie mógł odczytać lub zapisać dane w pliku, należy ów plik otworzyć albo
jeśli dotychczas nie istniał stworzyć. Odpowiednio, należy zamknąć plik przed zakończeniem
programu, który go otworzył. Do otwierania i zamykania plików służą funkcje systemowe open()
i close(). Do zamykania pliku sluży funkcja close(). Dwie podstawowe funkcje, read() i
write(), powodują przesyłanie znaków z pliku (być może specjalnego) do podanego obszaru
buforu w pamięci albo odwrotnie.
Z każdym plikiem związana jest liczba zwana deskryptorem pliku, która wiąże konkretny
proces z plikiem. Niektóre deskryptory plików przypisuje się procesom automatycznie. Plik 0
odnosi się do standardowego strumienia wejściowego, czyli domyślnie do klawiatury. Plik l jest
standardowym strumieniem wyjściowym i domyślnie odpowiada ekranowi terminala. Plik 2 jest
standardowym strumieniem diagnostycznym, także kierowanym na ekran terminala. Służy on
28 pazdziernika 2001 roku wersja 0.2 86
do przekazywania rozmaitych informacji systemowych oraz diagnostycznych. Dzięki rozdzieleniu
tego strumienia od strumienia wyjściowego, można wypisywać komunikaty na ekranie terminala
nawet wówczas, gdy strumień wyjściowy został skierowany na dysk lub na drukarkę. Użytkow-
nikowi wolno zmienić przypisanie strumieni. Ponieważ normalne zasady rozwidlania odnoszą się
także do programu shell, wszystkie pliki otwarte przez shell są dostępne dla każdego utworzo-
nego przez niego programu dla jego potomków, ich potomków, itd. Standardowe przypisanie
strumieni procesu macierzystego dziedziczą więc wszystkie pokolenia potomków. Oczywiście
przypisanie można zmienić z jakiegokolwiek na dowolne inne.
5.3.8 Identyfikatory plików
Pliki identyfikuje się w katalogach Unixa za pomocą indeksów (w żargonie unixowym i-
numerów). Indeks dla danego wolumenu fizycznego (dysku, taśmy, itp.) jest w rzeczywistości
indeksem do tablicy, zwanej i-listą zapisanej na tym samym urządzeniu. I-listę danego urządzenia
tworzy zbiór metryczek zwanych i-węzłami (ang. i-node, dlatego czasami i-lista jest nazywana
tablicą i-nodów). Każda metryczka opisuje jeden plik i zawiera m.in. następujące informacje:
1. numer identyfikacyjny użytkownika, który utworzył plik;
2. kod ochronny pliku (prawa dostępu);
3. trzynaście numerów bloków z danymi zajmowanymi przez plik;
4. rozmiar pliku;
5. czas ostatniej modyfikacji pliku;
6. liczbę dowiązań do pliku;
7. bity oznaczające rodzaj pliku (plik zwykły, katalog, plik specjalny).
Informacja o tym, ile dowiązań do pliku jest zapisanych w katalogach ma istotne znaczenie,
gdyż pliki różne od katalogów mogą być zapisane w katalogach wielu użytkowników. Jeśli jeden
z nich zechce usunąć plik ze swojego katalogu, musi ograniczyć się do usunięcia pozycji w ka-
talogu, ponieważ plik może być nadal dowiązany do innych katalogów. Jeśli natomiast pozycję
w katalogu usuwa ostatni z użytkowników pliku, powinien on również usunąć sam plik, tzn.
zwolnić obszar zajęty przezeń na dysku. W niektórych sytuacjach użytkownik może zechcieć
zbadać i-numery plików, np. gdy potrzebuje informacji dotyczących obszaru na dysku. Do wy-
pisania i-numerów służy polecenie fs z opcją -i. Wynik jest taki, jak zazwyczaj w przypadku
tego polecenia, ale dodatkowo dla każdego pliku podaje się jego i-numer.
28 pazdziernika 2001 roku wersja 0.2 87
Odwzorowanie nazwy pliku na jego i-numer nazywa się dowiązaniem (ang. link) i stanowi
podstawowe narzędzie identyfikowania plików i ich pielęgnowania. Można powiedzieć, że polece-
nie mv przenosi dowiązanie, rm usuwa je, a ln tworzy nowe.
28 pazdziernika 2001 roku wersja 0.2 88
Wyszukiwarka
Podobne podstrony:
UNIX omowienie 1UNIX omowienie 6UNIX omowienie 2UNIX omowienie 4Active Directory omówienie domyślnych jednostek organizacyjnychUnix lab 9materialy pomocnicze unixKasy fiskalne 2014 z omowieniem ekspertow CMS Cameron McKennaBerkeley Unix SummaryOmówienie metodyki prowadzenia poszczególnych analiz problemowych naUnix Wprowadzenie Internet i inne sieciĆwiczenia Active Directory omówienie jednostek organizacyjnychInstalacja pracowni omówienieSystemy Operacyjne Unix Linux solarka2OMÓWIENIE INTERFEJSÓW I KLAS ABSTRAKCYJNYCH W JĘZYKU JAVA2 Omowienie pakietu MS Office i Open Office Ogolne wlasciwosci?ytora tekstuwięcej podobnych podstron