Rozdział 4, Moja twórczość, Wybrana


Rozdział IV

Struktura i własności systemu

W niniejszym rozdziale przedstawiono opis unixowego systemu plików, rozwijając pojecie' pliku wzmiankowane ogólnie w rozdziale 1.6, oraz pojęcia takie jak: katalogi, pliki specjalne i zarządzanie przestrzenią dyskowa.

4.1. System plików

Plik (ang .file) to pewna porcja danych, przechowywana w pamięci masowej (zwykle na dysku lub taśmie magnetycznej), potocznie dość często nazywana zbiorem danych. Jest widziany przez system jako ciąg bajtów, gdzie bajt to 8 bitów, a hit to najmniejsza (zero-jedynkowa) jednostka informacji. W plikach tekstowych bajty „pamiętają" poszczególne znaki tekstu.

Zorganizowany zgodnie z wymaganiami systemu Unix zestaw plików przechowywanych w jednym wydzielonym obszarze pamięci dyskowej nazywany jest systemem plików. System Unix może obsługiwać wiele takich systemów, jednakże muszą być one połączone w jedną hierarchię przy pomocy mechanizmów dołączania systemu plików (popularnie: montowania;

poleceniem mount). System plików głównego dysku systemowego (zawierającego system operacyjny) obejmuje również urządzenia zewnętrzne, reprezentowane w systemie Unix przez pliki specjalne.

System plików systemu Unix (podobnie jak w DOSie) jest zorganizowany hierarchicznie, co powoduje, że jest on zrozumiały dla użytkownika z uwagi na postać hierarchicznego drzewa, gdzie w każdym węźle tego drzewa mogą być opisane zarówno pliki danych, jak i katalogi niższych poziomów, grupujące informacje o pewnych zestawach plików. Jest on uniwersalny, ponieważ nie zakłada niczego o wewnętrznej strukturze danych w pliku.

System plików charakteryzuje się minimalnym czasem dostępu do danych i posiada wygodną i zrozumiałą strukturę katalogów. Ponadto jest uniwersalny, posiada identyczne mechanizmy dostępu tak do plików dyskowych, jak i do urządzeń zewnętrznych, reprezentowanych przez wymienione wyżej pliki specjalne. Ponadto zapewnia ochronę przed nieuprawnionym dostępem i posiada różne metody dostępu do danych.

W systemie Unix istnieją trzy rodzaje plików, do których dostęp jest organizowany w analogiczny sposób:

• zwykłe pliki dyskowe;

• katalogi;

• pliki specjalne.

Pliki specjalne mogą dotyczyć:

• urządzeń zewnętrznych;

• kolejek FIFO, służących do organizacji nazwanych łączy komunikacyjnych (rozdział 4.2.3.8);

gniazd komunikacyjnych (ang. sockets), służących do wymiany komunikatów pomiędzy procesami i pochodzących z BSD Unixa.

4.1.1. Nazwy plików

Pliki zwykłe, katalogi i pliki specjalne mają nazwy składające się standardowo z maksymalnie 14-tu znaków ASCII. Wiele odmian Unixa, w tym SCO UNIX począwszy od wersji 3.2.4.0 i HP-UX począwszy od wersji 8.0, dopuszcza również nazwy długie (do 256 znaków, ale jest to ustalane na etapie kreowania systemu plików (zwykle przy instalacji systemu).

Nazwy plików specjalnych, pomimo że podlegają standaryzacjom, są dość często określone indywidualnie w ramach danej odmiany Unixa i mogą się różnić pomiędzy poszczególnymi jego odmianami.

W nazwie pliku zwykłego i katalogu mogą wystąpić dowolne znaki ASCII. Wielkie i matę litery są rozróżniane. Ponieważ część znaków ma dla shella specjalne znaczenie, zaleca się używać w nazwach tylko znaków przenaszalnych: liter, cyfr, podkreślenia i kropki. Znaki o specjalnym znaczeniu dla shella (zwane niekiedy metaznakami):

• ? ; | & \ <> [] ' ' "

o ile miałyby być użyte w nazwach plików, w poleceniach odwołujących się do takich nazw muszą być „cytowane" z użyciem: znaku backslash \, apostrofów ' lub cudzysłowów ".

Jeżeli nazwa pliku zaczyna się znakiem kropki, to dla pewnej grupy poleceń plik jest traktowany jako plik ukryty. Przykładowo, polecenie Is normalnie pomija tego typu pliki (o ile nie użyto opcji -a).

Wykonanie polecenia zawierającego w nazwie pliku znaki specjalne przebiega w ten sposób, że najpierw znajdowane są nazwy wszystkich istniejących w odpowiednim katalogu plików, pasujące do podanego wzorca, a następnie wykonywane jest właściwe polecenie, dla tak powstałej listy nazw plików.

Powyższe zagadnienia są rozwijane w rozdziale „Shell Bourne'a", poświęconym opisowi standardowego shella unixowego.

Rozszerzenia nazw plików

Nazwy plików w systemie Unix są po prostu ciągami znaków: system nie klasyfikuje nazw ze względu na ich postać. Natomiast w niektórych innych systemach operacyjnych pliki zawierające tekst, dane lub programy rozróżnia się dodaniem kilkuliterowego przyrostka (rozszerzenia) do bazowej nazwy pliku. Przykładowo DOS dla różnych klas plików wykonywalnych stosuje rozszerzenia: .exe, .com i .bat.

Jednakże to, że Unix nie rozpoznaje żadnych rozszerzeń, nie oznacza, że nie rozpoznają ich inne programy lub polecenia ani że użytkownicy nie mogą ich stosować. Nie ma nic niewłaściwego w takich praktykach, wręcz przeciwnie — zwyczajowo stosuje się je przy

różnych okazjach, co pozwala w długiej liście różnych plików zwrócić uwagę te, które posiadają nazwy zawierające pewne przyrostki i poddawać je selektywnie pewnym operacjom, na przykład kopiowania.

w Unixie istnieje wiele przyrostków nazw plików rozpoznawanych przez powszechnie używane programy, na przykład:

.a — archiwa dla poleceń ar i Id (odpowiedniki dosowych *.lib);

.c — pliki z programami źródłowymi w języku C;

.f — pliki z programami źródłowymi w języku Fortran;

.h — pliki z wierszami nagłówkowymi dla programów w języku C;

o — pliki modułów wynikowych (odpowiedniki dosowych *.obj);

.p — pliki z programami źródłowymi w języku Pascal;

.r — pliki z programami źródłowymi w języku Ratfor;

.s — pliki z programami źródłowymi w asemblerze;

.Z — pliki skompresowane poleceniem compress.

4.1.2. Pliki zwykle

Pliki zwykłe służą do przechowywania danych i programów. Są to pliki tekstowe lub binarne, z którymi użytkownik ma najczęściej do czynienia. Są one zapisane na dysku i zawierają te informacje, które wpisuje do nich użytkownik w wyniku wykonania poleceń systemowych lub programów użytkowych. Składają się one z bajtów danych uporządkowanych w postaci jednowymiarowej tablicy. System nie nakłada na informację przechowywaną w pliku żadnych ograniczeń czy też wymagań strukturalnych (mogą to robić programy użytkowe).

Podział na pliki tekstowe i binarne wynika bardziej z ich wzrokowej czytelności dla użytkownika (oglądającego je na ekranie lub wydruku) niż z uwarunkowań systemowych. Czytelność plików tekstowych jest wynikiem podziału pliku na wiersze opatrzone znacznikami końca wiersza (patrz poniżej).

Odczytywanie i zapisywanie danych w pliku rozpoczyna się od miejsca wskazanego przez wskaźnik bieżącej pozycji pliku. Nie można wstawić bajtów do środka pliku (rozpychając go), a także usuwać bajtów ze środka (kasując lukę). Plik można skrócić do zerowej długości, a w tych systemach, które dysponują funkcją systemową chsize, również zmniejszyć go do dowolnej pośredniej długości. Z danego pliku może jednocześnie korzystać (do zapisu lub odczytu) kilka procesów, jednakże ostateczny stan pliku zależy w tym przypadku od kolejności występowania poszczególnych operacji wejścia/wyjścia. W nowszych realizacjach Unixa istnieją mechanizmy pozwalające panować systemowi nad taką sytuacją. Jest to:

• blokowanie dostępu do plików;

• blokowanie dostępu do rekordów;

• semafory.

Pliki tekstowe, zawierające teksty różnych dokumentów lub teksty źródłowe programu, składają się ze znaków (bajtów) i tworzone są zwykle przy pomocy edytorów. Wiersze zwartego w nich tekstu są oddzielone od siebie symbolem nowego wiersza (którym jest

pojedynczy znak LF, a nie CRLF —jak to bywa w wielu innych systemach operacyjnych, na przykład w MS DOS). Pomijamy tutaj rzadko wykorzystywaną możliwość systemu HP-UX wielobajtowej reprezentacji pojedynczych znaków. Pliki tego typu są wizualnie łatwo czytelne dla użytkownika i mogą być przez niego tworzone i modyfikowane przy pomocy standardowych edytorów tekstowych.

Pliki binarne nie zawierają standardowego tekstu, lecz przechowują programy wykonywalne lub pewne rodzaje danych. Plik z programem binarnym (plik wykonywalny) reprezentuje sobą ciąg bajtów w takim porządku, w jakim program jest ładowany do pamięci w celu jego wykonania. Niektóre polecenia systemowe tworzą bardziej złożone struktury danych, np. kompilator języka C generuje, a program konsolidujący (inaczej: łączący — ang. linker) przyjmuje do przetwarzania określony format pliku wynikowego. Podobnie bywa w przypadku systemów zarządzania bazami danych i wielu innych programów. Format pliku wynikowego jest znany tylko tym programom. W ten sposób strukturą plików sterują programy, a nie system Unix.

Z punktu widzenia systemu, zwykły plik jest niestrukturalnym ciągiem bajtów o dostępie bezpośrednim.

Poniżej przedstawiono przykłady typowych poleceń dotyczących plików zwykłych (literą f oznaczono nazwę pliku):

cat f — wyprowadzenie na standardowe wyjście (domyślnie ekran terminala) zawartości pliku o nazwie f;

cat f1 f2 — wyprowadzenie na standardowe wyjście (ekran) połączonej zawartości plików o nazwach f1 i f2;

chmod 0-w f — odebranie (-) innym (o — other) użytkownikom systemu prawa do zapisu (w — wille) i modyfikacji pliku f;

chown ewa f — przypisanie plikowi f właściciela ewa;

chgrp pro f — przypisanie plikowi f grupy współużytkowników o nazwie pro;

cp f1 f2 — utworzenie kopii pliku f1 i nadanie jej nazwy f2;

diff f1 f2 — wyprowadzenie różnic dotyczących zawartości plików f1 oraz f2;

file f — rozpoznanie typu pliku f;

find / -name f -print — poszukiwanie pliku f w całym systemie plików;

grep wz f — wyświetlenie wierszy pliku f, zawierających napisy określone przez argument wz;

ln f1 f2 — utworzenie dodatkowej nazwy f2 dla pliku f1;

Ip f — wydrukowanie pliku o nazwie f;

Is — wyświetlenie nazw plików katalogu bieżącego;

more f — przeglądanie na ekranie pliku o nazwie f;

mv f1 f2 — zmiana nazwy pliku f1 na nazwę f2;

mv f1 /tmp — przeniesienie pliku o nazwie f1 do katalogu /tmp;

rm f — usunięcie z bieżącego katalogu opisu pliku o nazwie f (dokładniej: usunięcie dowiązania opisu pliku, omówionego w dalszej części podręcznika);

sort f — wyświetlenie posortowanych wierszy pliku f;

wc f — zliczenie wierszy, słów i znaków pliku f.

4.1.3. Katalogi

Katalog (inaczej: kartoteka — ang. directory) jest pojęciem wprowadzonym po to, ab> umożliwić porządkowanie i grupowanie danych przechowywanych na dysku. Katalog widziany przez użytkownika grupuje wydzielony (tematycznie lub użytkowo) zestaw plików. Dotychczas mówiliśmy o katalogach jako miejscach przechowywania plików. W rzeczywistości katalogi zawierają nie pliki, lecz informacje niezbędne do ich odszukania, Katalog przypomina trochę teczkę, która zamiast dokumentów zawiera spis informacji o tym, gdzie te dokumenty można odnaleźć, w szczególnym przypadku mogą to być odesłania do innych teczek.

Katalog jest plikiem zawierającym listę nazw innych plików, przy czym z każdą nazwą jest związany (niewidoczny bezpośrednio dla użytkownika) odsyłacz do dokładnego opisu pliku (nazywanego i-wezłem), omówionego dokładniej w kolejnych podrozdziałach. Każda z pozycji katalogu może być również katalogiem, czyli katalog może zawierać podkatalogi niższego poziomu. Posiada nazwę budowaną wg. reguł ogólnie obowiązujących dla plików.

4.1.3.1. Struktura drzewa katalogów

Informacje zapisywane na dysku w postaci plików i katalogów tworzą pewną strukturę hierarchiczną (drzewiastą). Określenie „hierarchiczna" i „drzewiasta" nie jest jednak w pełni ścisłe, ponieważ w Unixie mogą istnieć (opisywane dalej) dowiązania danego pliku do różnych katalogów, jak i różne dowiązania do tego samego katalogu (cechy tej nie posiada system DOS). Ponieważ jednak wspomniany mechanizm jest w praktyce stosowany dość oszczędnie, w dalszym opisie stosowane będą terminy drzewo plików i struktura drzewiasta/hierarchiczna.

System plików głównego dysku systemowego w większości odmian Unixa zawiera niżej wymienione katalogi.

/ — katalog główny;

/bin — katalog bazowego zestawu poleceń;

/dev — katalog plików specjalnych;

/etc — katalog poleceń dla administratora systemu;

/lib — katalog bibliotek systemowych;

/usr — katalog podkatalogów systemowych;

/usr/bin — podkatalog uzupełniającego zestawu poleceń;

/usr/lib — podkatalog bibliotek narzędziowych;

/usr/tmp — podkatalog roboczy dla użytkowników;

/tmp — katalog roboczy przeznaczony głównie dla potrzeb systemu;

/users — katalog podkatalogów użytkowników (w niektórych systemach, na przykład SCO UNIX, są one domyślnie umieszczane

w katalogu /usr).

W każdym głównym unixowym systemie plików (zawierającym bazową część systemu operacyjnego) istnieje katalog główny o systemowej nazwie /, w którym są zapisane zarówno informacje o plikach, jak i podkatalogach niższego poziomu.

Katalogi zapewniają łączność pomiędzy nazwami plików i samymi plikami, tworząc strukturę systemu plików. W odróżnieniu od zwykłych plików, użytkownik nieuprzywilejowany nie może stosować do nich zwykłych operacji zapisu i czytania ani modyfikować ich przy pomocy edytorów. Do operacji na katalogach istnieje więc wyodrębniony (opisywany dalej) zestaw poleceń. Pod wszystkimi innymi względami (z punktu widzenia systemu) katalog jest normalnym plikiem systemu plików.

Unixowy system plików jest bardzo rozbudowany; w zależności od odmiany systemu i zainstalowanego oprogramowania obejmuje od kilku do kilkunastu tysięcy plików.

4.1.3.2. Ścieżki dostępu do plików

W systemie Unix stosowana jest uniwersalna konwencja identyfikowania plików, wykorzystująca pojęcie ścieżki dostępu do pliku, przy czy termin ścieżka odnosi się do drzewiastego obrazu systemu plików i obejmuje wszystkie nazwy węzłów tego drzewa (nazwy katalogów), przez które należy przejść, aby dotrzeć do pliku.

0x01 graphic

Rysunek 4. l. Przykładowa struktura katalogów

Ścieżka dostępu jest zapisywana jako ciąg nazw katalogów, oddzielonych znakiem /. Wyróżnia się (patrz rysunek 4.1):

ścieżkę pełną (inaczej: bezwzględną), zaczynającą się od katalogu głównego (na przykład /usr/teksty/dok1);

ścieżkę względną, .określoną względem katalogu bieżącego (na przykład:

./dane/dl — względem katalogu /usr/programy — kropka oznacza katalog bieżący).

Pełna nazwa pliku (określana również jako ścieżkowa nazwa pliku) składa się ze ścieżki pełnej i z bazowej nazwy pliku, na przykład dla pliku o bazowej nazwie d3, jego pełną nazwą zgodnie z rysunkiem 4.1 jest nazwa /iisr/programy/dane/d3.

Wszystkie katalogi, za wyjątkiem głównego, mają swój katalog nadrzędny (w którym są umieszczone). Mogą zawierać również katalogi podrzędne (podkatalogi).

Przykładowo, w celu obejrzenia pliku prog2 znajdującego się w katalogu programy, który z kolei znajduje się w katalogu usr umieszczonym w katalogu głównym, jak na rysunku 4.1, można użyć polecenia

more /usr/programy/prog2

W przedstawionym na rysunku 4.1 fragmencie struktury plików, dla katalogu programy, katalog usr jest katalogiem nadrzędnym, katalog dane — katalogiem podrzędnym, natomiast katalog teksty — katalogiem sąsiednim.

Jeżeli nazwa pliku zaczyna się od znaku /, to poszukiwanie pliku rozpoczyna się od katalogu głównego, którego podstawa umieszczona jest na głównym dysku systemowym. Ścieżka, która nie rozpoczyna się od znaku / ogranicza zakres poszukiwań, rozpoczynając je w katalogu bieżącym, ustawionym bądź w sposób jawny (poleceniem cd), bądź przez procedurę otwarcia sesji pracy (login:). Jeśli zgodnie z ostatnim rysunkiem założyć, że katalogiem bieżącym jest katalog programy, to plik prog2 można identyfikować przez podanie prog2 (lub ./prog2), jako że znajduje się on w katalogu bieżącym.

Przy zakładaniu katalogu tworzone są w nim dwa standardowe zapisy o nazwach: . oraz ...

Nazwa . w każdym katalogu oznacza dany katalog. Tak więc. program może czytać dane z katalogu bieżącego, nie znając jego pełnej nazwy, odwołując się do niego przez nazwę . — nazwą tą posługujemy się najczęściej kopiując pliki- z innego katalogu do katalogu bieżącego, na przykład z katalogu o nazwie work, jak w poniższym poleceniu:

cp work/*.

Nazwa .. identyfikuje katalog nadrzędny, to znaczy katalog, w którym utworzono katalog bieżący. Pozwala to w łatwy sposób „poruszać się" poleceniem cd po drzewie plików. Na przykład jeśli dla katalogu bieżącego katalogiem sąsiednim jest katalog o nazwie programy, to można przejść do niego przy pomocy polecenia:

cd ../programy

Katalog zawierający tylko nazwy .,.. jest traktowany jako katalog pusty.

4.1.3.3. Przykłady poleceń do obsługi katalogów

W niniejszym podrozdziale zamieszczono krótki przegląd typowych poleceń służących do obsługi katalogów:

pwd — (ang. print working directory) — wyświetlenie nazwy aktualnego katalogu roboczego; bardzo przydatne, szczególnie jeżeli korzystamy z shella Bourne'a (sh), którego trudno jest zmusić do wyświetlania aktualnej ścieżki dostępu w symbolu gotowości;

cd kat — (ang change directory — zmień katalog) przejście do katalogu o nazwie kat, umieszczonego w katalogu bieżącym;

mkdir kat — (ang. make directory — utwórz katalog) utworzenie w katalogu bieżącym katalogu o nazwie kat;

rmdir kat — (ang. remove directory — usuń katalog) usunięcie pustego katalogu o nazwie kat z katalogu bieżącego;

mv k1 k2 — (ang. move — przenieś) zmiana nazwy katalogu kl na nazwę k2, o ile kl jest katalogiem;

mv k1 p/k2 — przeniesienie całego katalogu kl w inne miejsce, wyznaczoneprzez ścieżkę, dostępu p i nadanie mu tam nazwy k2 (przy założeniu, /. kl jest nazwą katalogu, a katalog k2 tam nie istniał);

Is — wyprowadzenie (żargonowo: listowanie) zwartej listy nazw

plików katalogu bieżącego (bez nazw plików ukrytych);

Is -l — wyprowadzenie nazw i opisów plików katalogu bieżącego (bez

plików ukrytych) w formacie pełnym (-1 od ang. long format)', Is -l / — wyprowadzenie nazw i opisów wszystkich plików katalogu

głównego;

ls -a — wyprowadzenie nazw wszystkich plików z katalogu bieżącego,

w tym nazw plików ukrytych;

Is -R kat — wyprowadzenie nazw plików katalogu kat i z wszystkich jego

podkatalogów.

Ponieważ katalogi są dla Unixa plikami zwykłymi, może się do nich odnosić wiele poleceń stosowanych do plików, takich jak:

chmod — zmień prawa dostępu;

chown — zmień właściciela;

chgrp — zmień grupę użytkowników.

4.1.3.4. Porównanie poleceń DOSa i Unixa

W poniższej tabeli przedstawiono porównanie wybranych poleceń Unixa i DOSa z zakresu obsługi katalogów (przez kat oznaczono nazwę katalogu).

Tabela 4.1. Porównanie wybranych poleceń Unixa i DOSa

Polecenie

Unix

DOS 6.x

nazwa katalogu

bieżącego

.

.

nadrzędnego

..

..

zmiana katalogu bieżącego

cd katalog

cd katalog

wyświetlenie ścieżki dostępu

pwd

utworzenie katalogu

mkdir katalog

mkdir katalog

usunięcie pustego katalogu

rmdir katalog

rmdir katalog

usunięcie drzewa katalogów

rm -r katalog

deltree katalog

kopiowanie katalogu

HP-UX

cp -r kat1 kat2

xcopy kat1 kat2/s

SCO UNIX

copy kat1 kat2

xcopy kat1 kat2/s

przeniesienie katalogu

mv kat1 sciezka/katl

xcopy kat1 sciezka\kat1/s

wyprowadzenie zawartości bieżącego katalogu

Is ls-1

dir/w dir

kasowanie zawartości katalogu

dowolnego

rm katalog/*

del katalog\*.*

bieżącego

rm*

del *.*

4.1.3.5. Wyprowadzana zawartość katalogu

Jak już wiemy, katalog jest plikiem zwykłym, zawierającym opisy innych plików i podkatalogów. Poszczególne pozycje katalogu opisują pojedyncze pliki. Pomimo tego, że jedna pozycja katalogu zawiera jedynie nazwę pliku i odsyłacz do i-węda, zawierającego pozostałe informacje o pliku (patrz następny podrozdział), to polecenia wyświetlające pełną zawartość katalogu wyprowadzają dość dużo informacji o poszczególnych plikach (gdyż pobierają je ze wspomnianego i-węda).

Załóżmy, że bieżącym katalogiem jest katalog główny. Wprowadzenie polecenia:

I (SCO UNIX);

II (HP-UX);

Is -l (dowolny Unix);

spowoduje wyświetlenie opisu plików tego katalogu.

Zawartość katalogu głównego w większości wersji Unixa jest zbliżona do przedstawionej poniżej (przykład dotyczy systemu HP-UX):

0x01 graphic

l * — dla plików zwykłych — liczba dowiązań;

— dla katalogów — liczba opisanych w nim katalogów (łącznie z nazwami.,..).

Na początku wiersza, w polu „typ pliku", może wystąpić jeden z poniżej podanych znaków (dokładniejsze informacje podano w opisie polecenia Is):

- — plik zwykły;

d — katalog;

b — plik specjalny urządzenia obsługiwanego blokowo;

c — plik specjalny urządzenia obsługiwanego znakowo.

Kolejne trójki znaków w polu „prawa dostępu" wyznaczają prawa dostępu do pliku, kolejno dla: właściciela pliku, grupy do której należy właściciel i dla pozostałych użytkowników systemu Unix.

Znaki rwx umieszczone na odpowiedniej pozycji pola praw dostępu oznaczają, że użytkownik danego typu (właściciel pliku — „właściciel"; grupa użytkowników do której należy właściciel — „grupa"; pozostali użytkownicy — „inni") może:

r — (ang. reacf) czytać plik, wyświetlać zawartość katalogu (o ile katalog ma atrybut x; jeżeli katalog nie ma atrybutu x dla danego typu użytkownika, to w niektórych odmianach Unixa, w przypadku próby odczytania zawartości katalogu wyprowadzana jest tylko lista nazw plików, z których każda jest opatrzona komunikatem not found — plik niedostępny);

w — (ang. write) pisać w pliku, kasować go, a jeżeli plik jest katalogiem —zapisać w nim nowy plik lub utworzyć podkatalog, o ile katalog dla danego typu użytkownika ma atrybut x;

x — (ang. execute) wykonać program zawarty w pliku, a jeżeli plik jest katalogiem wejść do niego (polecenie Cd) lub wyświetlać jego zawartość (polecenie Is).

Powyższe kody literowe podawane są zawsze w kolejności rwx. Jeżeli w danej trójce tych znaków na pozycji danej litery występuje znak minus (-), to oznacza to, że użytkownik określonego typu nie posiada danego prawa dostępu.

Aby system ochrony plików miał sens, operację zmiany praw dostępu do pliku może wykonać tylko jego właściciel lub użytkownik uprzywilejowany. Prawa można nadawać i odbierać.

Odebranie samemu sobie prawa zapisu we własnym pliku (prawa w) — o ile nie jest przewidywana zmiana jego treści — jest jednym ze sposobów zabezpieczenia pliku przed przypadkowym jego skasowaniem.

W przedstawionej pełnej postaci opisu plików katalogu w specyficzny sposób wyprowadzana jest data i czas ostatniej modyfikacji pliku:

miesiąc n r_dn i a godz:min — jeżeli jest to data z zakresu ostatnich 12-tu miesięcy;

miesiąc nr_dnia rok —jeżeli jest to data sprzed ostatnich 12-tu miesięcy.

4.1.3.6. Wewnętrzna struktura katalogu

Wewnętrzna struktura katalogu jest bardzo prosta: dla każdego pliku lub katalogu niższego poziomu system tworzy w katalogu rekord zawierający dwa pola:

i-numer (inaczej: numer i-węzła — ang. i-node-number), zajmujący dwa bajty i wskazujący i-węzeł (ang. i-node), zawierający szczegółowy opis pliku, l-węzły są zapisane w tablicy i-węzłów, przechowywanej w początkowej części obszaru dyskowego zawierającego system plików. Tablica ta nazywana jest i-listą, a każdy jej element (i-węzeł) opisuje pojedynczy plik. Zajmuje ona kilka procent pojemności dysku (w przypadku stosowania długich nazw plików — do 10%).

nazwa pliku — 14 lub 256 znaków, zależnie od tego czy są stosowane standardowe krótkie czy długie nazwy plików (w SCO UNIX długie nazwy są dopuszczalne dopiero począwszy od wersji 3.2.4.0).

Liczba pozycji katalogu jest limitowana jedynie przez liczbę wolnych elementów i-listy. Struktura katalogu jest zdefiniowana w pliku /usr/include/sys/dir.h. Szkicową postać zawartości pliku katalogu przedstawiono na rysunku 4.2.

0x01 graphic

Rysunek 4.2. Szkic wewnętrznej postaci katalogu

Jedna pozycja katalogu niekiedy nazywana bywa dowiązaniem pliku do katalogu.

Katalogi są zapisywane tak samo jak pliki zwykłe, z tym że są one w swoim i-wężlee oznaczone jako katalogi. Tak więc i-węzłem odpowiadającym danej nazwie w katalogu może być i-węzeł innego katalogu. Oznacza to nic innego jak to, że każda z pozycji katalogu może być nazwą innego (podrzędnego) katalogu. Pozwala to budować hierarchiczną strukturę katalogu.

4.1.3.7. Dowiązania

Ponieważ z różnymi pozycjami katalogu może być związany ten sam i-węzeł opisujący konkretny plik, łatwo zauważyć, że ten sam plik może mieć wiele nazw. W celu dowiązania do pliku nowej nazwy można użyć polecenia In (od ang. link — dowiązać, połączyć). W najprostszym przypadku ma ono następującą składnię:

In istniejąca_nazwa nowa_nazwa

Dowiązania do pliku w ramach tego samego katalogu muszą określać jego różne nazwy, Jednakże ponieważ można tworzyć dowiązania do plików zapisanych w innych katalogach, ten sam plik może mieć w danym systemie plików kilka tych samych nazw, istniejących Jednakże w różnych katalogach. Wszystkie dowiązania są równoprawne. Aby usunąć plik z dysku, trzeba usunąć wszystkie jego dowiązania. Plik jest usuwany dopiero z chwilą usunięcia ostatniego dowiązania.

Pokazane poniżej polecenie

In /usr/piotr/work/* /usr/piotr/protect

wykonuje nieco bardziej złożone działania: dla wszystkich plików z katalogu /usr/piotr/work tworzy dowiązania i umieszcza je pod oryginalnymi nazwami w istniejącym katalogu /usr/piotr/protect.

W systemach bazujących na Unixie BSD i SVR4.x dostępne są również dowiązania symboliczne (ang. symbolic links); nazywane również dowiązaniami miękkimi (ang. soft links). Dowiązania takie mogą zawierać nazwy plików i katalogów znajdujących się w innym systemie plików. Są one jedynie naśladownictwem zwykłych dowiązań i nie są traktowane jak prawdziwe nazwy plików. Aby utworzyć dowiązanie symboliczne, w poleceniu In należy użyć opcji -S. Pliki będące dowiązaniami symbolicznymi można przetwarzać jak normalne pliki. W liście plików wyprowadzanej poleceniem Is -l, dowiązania symboliczne są na początku wiersza opatrzone literą l, a obok ich nazwy umieszczony jest symbol -> wskazujący na „pierwotną" nazwę pliku. Jeżeli pliki, do których dowiązano w ten sposób nazwy, zostaną skasowane, to dowiązane nazwy będą się odwoływać do już nieistniejących plików (dowiązania te można skasować poleceniem rm).

4.1.4. Pliki specjalne

Unifikacja dostępu do zwykłych plików i do urządzeń zewnętrznych została w systemie Unix osiągnięta przez zastosowanie analogicznej konwencji nazewniczej i proceduralnej w odniesieniu do zwykłych plików dyskowych jak i urządzeń. Urządzenia zewnętrzne są obsługiwane jako abstrakcyjne pliki, nazywane plikami specjalnymi. Są one zgrupowane w katalogu /dev, przy czym ze względu na dużą ich liczbę pliki dotyczące dysków i taśm magnetycznych wyodrębniono w kolejnych podkatalogach (w podkatalogach o nazwach zaczynających się od litery r (ang. raw) zgrupowane są pliki używane do obsługi urządzeń w trybie znakowym, natomiast w pozostałych podkatalogach — pliki używane do obsługi urządzeń w trybie blokowym).

0x01 graphic

Rysunek 4.3. Typowa struktura katalogu /dev

Pliki specjalne określają metodę dostępu do urządzeń wejścia/wyjścia, które reprezentują. Zapis w pliku specjalnym jest przez system przekształcany na zapis kierowany do konkretnego urządzenia (podobnie odczyt), o ile urządzenie jest go w stanie wykonać. Plik specjalny reprezentuje albo pewien rodzaj urządzenia (dysk, taśmę magnetyczną, łącze terminalowe), albo kolejkę prostą FIFO, stanowiącą mechanizm do przekazywania danych pomiędzy procesami.

Istnieją dwa rodzaje plików specjalnych dotyczących urządzeń: blokowe i znakowe.

W blokowych plikach specjalnych urządzenie jest widziane jako abstrakcyjna tablica bloków danych o stałej długości (zazwyczaj 512, 1024 lub 2048 bajtów), a pula buforów jądra systemu służy jako szybka pamięć przyśpieszająca operacje wejścia/wyjścia.

W znakowych plikach specjalnych urządzenie jest widziane jako abstrakcyjna tablic;

znaków (bajtów). Wykonywane operacje we/wy mogą się odnosić zarówno do bardzo małych (pojedyncze znaki), jak i bardzo dużych porcji danych.

Uwaga:

Z tym samym urządzeniem fizycznym mogą być związane zarówno blokowe, jak i znakowe pliki specjalne, co jest regułą w przypadku dysków. Standardowo dla zwykłych plików i katalogów system stosuje dostęp poprzez blokowy plik specjalny.

Plik specjalny ma swój i-wezeł, nie wskazuje on jednak na bajty danych na dysku, natomiast jego odpowiednie pole zawiera numer urządzenia, będący indeksem tablicy, której jądro systemu stosuje do odszukania procedury stanowiącej podprogram obsługi danego urządzenia (ang. driver). Aktualne znaczenie numeru urządzenia zależy od konkretnej implementacji systemu. Zazwyczaj jednak jego młodszy bajt jest traktowany jako numer drugorzędny (ang. minor), a starszy bajt — jako numer nadrzędny (ang. maior). Numer nadrzędny wskazuje na odpowiedni podprogram obsługi urządzenia, natomiast drugorzędny określa konkretne urządzenie danego typu. W pełnej liście nazw plików specjalnych, wyświetlanej poleceniem Is -l, numery te są pokazywane w miejscu, w którym dla pozostałych rodzajów plików pokazywana jest ich wielkość.

Procedury obsługi urządzeń są wykonywane w ramach jądra systemu, a nie jako procesy użytkowe. Dodanie do jądra nowych procedur obsługi urządzeń w większości realizacji Unixa wymaga zwykle rekompilacji jądra. Wymóg ten nie istnieje w systemach bazujących na Unix SVR4). Pliki specjalne tworzone są przy pomocy funkcji systemowej mknod.

Operacje na dyskowym pliku specjalnym mogą być w zasadzie wykonywane za pomocą zwykłych funkcji systemowych, realizujących operacje we/wy w taki sam sposób, jak w przypadku plików zwykłych. W obrębie dysku nie mamy jednak w tym przypadku do czynienia z katalogami, plikami, i-węzłami, właścicielami, czasami dostępu itp. Istnieje wyłącznie tablica ponumerowanych bloków. W praktyce zwykli użytkownicy nie mogą jednak wykonywać na pliku specjalnym żadnych operacji z powodu braku uprawnień.

Jak już wiemy, pliki specjalne są zgrupowane w katalogu /dev. Ich nazwy w poszczególnych odmianach Unixa mogą być różne, co ilustruje tabela 4.2.

Poniżej wymieniono kilka nazw przykładowych poleceń, odnoszących się do plików specjalnych (ich pełny opis można uzyskać poleceniem man).

Tworzenie plików specjalnych

mknod — tworzenie pojedynczego pliku specjalnego;

insf — (HP-UX) tworzenie w bieżącym katalogu plików specjalnych dla wszystkich zainstalowanych lub wskazanych urządzeń;

mksf — (HP-UX) tworzenie pojedynczego pliku specjalnego.

Tabela 4.2. Przykładowe pliki specjalne w systemach SCO UNIX i HP-UX

HP-UX v.8.xx

SCO UNIX 3.2.x

Uwagi

/dev/mem

/dev/mem

pamięć operacyjna

/dev/swap

/dev/swap

obszar wymiany obrazów procesów

/dev/null

dev/null

urządzenie „puste" (pusty plik)

Tenninale

/dev/console

/dev/console

konsola operatora

/dey/ttyla; ...1A

port szeregowy l, COM1; ... A — modem

/dev/tty2a; ...2A

port szeregowy 2, COM2; ... A — modem

Multipleksery

/dev/tty0p0

/dev/tty...1

port szeregowy 0

/dev/tty0pl

/dev/tty...1

port szeregowy l

/dev/tty0p2..16

/dev/tty...1

port szeregowy 2.. 16

1 — nazwy są zależne od stosowanych kart (adapterów)

Drukarki

/dev/lp0

/dev/lp

drukarka, port równoległy

Dyski

w HP-UX: L; — numer dysku; N — numer partycji; w SCO UNIX: L — numer dysku według konwencji SCO UNIX/XENIX; N — numer parytcji (0 — cały dysk; l — pierwsza partycja; 2 — druga)

/dev/root

/dev/root

główny system plików

/dev/dsk/cLd0sN

/dey/hdLN

zawartość dysku, obsługa blokowa

/dev/rdsk/cLd0sN

/dey/rhdLN

zawartość dysku, obsługa znakowa

Dyskietki

ds — double; ss — single sided

brak dyskietek w standardowych konfiguracjach

/dev/fd0; /dev/fdl

dyskietka A, B

/dev/rfd048ds9

360 KB (0 — A; l — B; r — obsługa blokowa)

/dev/rfd096dsl5

1.2 MB (0 — A; l — B; r: — obsługa blokowa)

/dev/rfd0135dsl8

1.4 MB (0, — A; l — B; r: — obsługa blokowa)

Taśmy magnetyczne (streamery)

/dey/rmt/Om

DDS/DAT (1.2 - 4.8 GB)

/dev/rct/c2d0s2

/dev/rct0

cardridge tape; SCO QUIC tape

/dev/rft0

OUIC-40 tape device

/dev/rctmin

mini-cardridge tape device

0x01 graphic

Rysunek 4.4. Struktura unixowego wejścia/wyjścia

Wyprowadzanie informacji

df — wyprowadzanie informacji o podłączonych systemach plików:

Issf — (HP-UX) wyprowadzanie charakterystyk plików specjalnych;

loscan — (HP-UX) wyprowadzanie nazw plików specjalnych dla wszystkich lub wybranych urządzeń;

diskinfo — (HP-UX) wyprowadzanie informacji dotyczącej wskazanego dysku;

hwconfig — (SCO) wyprowadzanie informacji o zainstalowanych urządzeniach (ale nie o plikach specjalnych).

Tworzenie nowego systemu plików

mkfs — we wszystkich odmianach Unixa;

newfs — w niektórych odmianach Unixa. na przykład w HP-UX:

newfs /dev/rdsk/cld0s10 hp7937

Pliki opisujące systemy plików (dyski)

/etc/disktab — (HP-UX) opis parametrów różnych dysków;

/efc/checklist — opis systemów plików automatycznie „dołączanych" i weryfikowanych po stwierdzeniu utraty integralności;

/etc/mnttab — opis aktualnie „dołączonych" systemów plików.

Sprawdzanie integralności systemu plików

icheck — sprawdzanie integralności listy wolnych bloków, poprawności numerów bloków w i-wezłach plików, ich zdublowanie itp.;

dcheck — sprawdzanie i korekcja poprawności struktury katalogów;

/etc/łsck — globalne testowanie systemu plików, które łączy funkcje

własne z funkcjami poleceń icheck i dcheck;

/tcb/bin/ihtegrity — (SCO) testowanie integralności logicznej systemu plików.

4.1.5. Zarządzanie przestrzenią dyskową

4.1.5.1. Składowe przestrzeni systemu plików

System plików Unixa może być utworzony na dowolnych nośnikach, pozwalających swobodnie operować blokami danych. Tradycyjnie są to dyski magnetyczne. Cała przestrzeń dyskowa (lub jej wydzielona cześć, nazywana partycją lub sekcją) zarezerwowana na system plików traktowana jest jako ciąg N bloków (w SCO UNDC: 0.5. l i 2 KB: standardowo 1 KB). W najprostszym (praktycznie już historycznym) przypadku, przestrzeń dysku zawierającego tylko jeden system plików jest podzielona na 5 (opisywanych dalej) części:

program ładujący jądro systemu (ang. bootstrap);

• opis obszaru dyskowego systemu plików (superblok);

lista i-węzłów (i-lista);

• obszar plików;

• obszar wymiany obrazów procesów (ang. swappingu).

Bootstrap

superblok

i-lista

obszar plików

obszar wymiany

Rysunek 4.5. Struktura obszaru pamięci systemu plików

W sytuacji, gdy dysk składa się z kilku partycji (sekcji) podział ten jest bardziej złożony. Dotyczy to głównie systemu HP-UX, wymagającego na dysku systemowym trzech partycji — dodatkowe są wymagane dla bootstrapu i obszaru wymiany obrazów procesów. Bardziej złożone jest to również w przypadku systemów Unix dla komputerów PC, instalowanych na dysku, na którym w odrębnych partycjach mogą być zainstalowane inne systemy operacyjne (na przykład system DOS). Dalsza złożoność, na przykład w przypadku systemu SCO UNIX, może wynikać również z faktu, że w ramach tej samej partycji w trakcie

instalacji systemu może być tworzony wydzielony system plików (standardowo widziany jako katalog /u). Kolejne odstępstwa od powyższego schematu występują również w przypadku stosowania macierzy dyskowych oraz wolumenów logicznych (dostępnych w HP-UX v.9.x — system ten pozwala potraktować kilka dysków jako jeden duży wolumen logiczny).

Bootstrap to specjalny program inicjujący ładowanie systemu operacyjnego do pamięci wewnętrznej. W przypadku systemu SCO UNIX zajmuje on zerowy sektor dysku, natomiast w przypadku HP-UX — odrębną partycję (6 MB w HP-UX v.8.x).

Superblok (nazywany również blokiem głównym) jest zestawem informacji opisujących, obszar dyskowy systemu plików. Zestaw ten jest zdefiniowany przez odpowiednią strukturę danych. Jej warianty dla różnych odmian systemu są różne (w SCO UNIX — struktura filsys, w HP-UX — struktura/s). Ich definicje znajdują się w plikach systemowych:

• SCO UNIX: /usr/include/sys/filsys.h /us r/in cl ude/sys/fs/*

• HP-UX: /usr/include/sys/nisys.h /usr/include/sys/fs.h

Do najważniejszych informacji pamiętanych w superbloku należą:

• długość i-listy (liczba i-wezłów. tym samym maksymalna liczba plików w danym systemie plikowym);

• maksymalny numer bloku jaki może być przeznaczony do pamiętania danych;

• liczba, wielkość i lista wolnych bloków dysku;

• liczba i lista wolnych i-wezłów;

różnego rodzaju opcje, opisujące aktualny stan systemu plików i aktualne tryby jego wykorzystania.

I-lista zawiera listę i-wezłów, opisujących pojedyncze pliki. Długość i-listy jest zależna od wielkości obszaru dysku wydzielonego na system plików i jest przy instalacji systemu dobierana automatycznie tak, aby nie ograniczać zbytnio maksymalnej liczby plików.

Obszar plików. Bloki obszaru dyskowego występujące na rysunku 4.5 za i-lista są dostępne jako bloki danych. Początkowo ich numery są umieszczone w liście wolnych bloków. Kiedy plik jest zapisywany i rośnie jego wielkość, wtedy z listy tej są pobierane kolejne bloki i przydzielane danemu plikowi. Gdy nastąpi obcięcie lub skasowanie pliku, wówczas numery zajmowanych przez niego bloków będą dodane do listy wolnych bloków.

Obszar wymiany (inaczej: wymiatania ang. swap area) służy do chwilowego przechowywania obrazów procesów, usuniętych z pamięci operacyjnej w celu zwolnienia Jej dla innych procesów oczekujących w kolejce, przy czym przez obraz procesu rozumieć należy obszar pamięci operacyjnej przydzielonej procesowi.

W niektórych odmianach Unixa, na przykład HP-UX, obszar wymiatania jest tworzony w wyodrębnionej partycji dyskowej, w innych zaś systemach, na przykład SCO UNIX, wyodrębniany jest on z obszaru systemu plików. W obu przypadkach określanie wielkości obszaru wymiatania następuje w chwili instalowania systemu operacyjnego. Jeżeli instalacja me Jest przeprowadzana w trybie „automatycznym" (z domyślnymi parametrami), to zaleca ^e ustalić wielkość obszaru wymiatania nie mniejszą od wielkości zainstalowanej pamięci °peracyjnej, biorąc jednocześnie pod uwagę to, że w przyszłości pamięć operacyjna może być powiększana przez instalację jej kolejnych modułów.

W niektórych odmianach Unixa, na przykład w HP-UX, dzięki poleceniu swapon istnieje dodatkowo możliwość włączania dynamicznego obszaru wymiatania, skojarzonego z podanym katalogiem systemu plików.

I-węzeł (ang. i-node) zawiera informacje o typie pliku (katalog, zwykły plik, plik specjalny), jego kodzie trybu wykorzystania, długości, dacie i czasie utworzenia, a także o rozmieszczeniu danych .na dysku. Katalog zawiera zaś tylko wykaz nazw plików i związane z nazwami i-numery, czyli numery i-węzłów, opisujących poszczególne pliki.

Takie podejście, w którym nazwą pliku jest oddzielona od innych jego atrybutów, kontrolowanych przez system, pozwala elastycznie manipulować zewnętrznym obrazem hierarchii plików, nie zmieniając samych plików ani ich położenia na dysku. Pozwala to opisać ten sam plik w kilku pozycjach katalogu, które będą określać różne jego nazwy, ale wskazywać na ten sam jego opis (i-węzłów), czyli na ten sam plik. W szczególności bardzo prosto można umieścić nazwę jednego i tego samego pliku w kilku katalogach. Ich nazwy mogą być w tym przypadku dowolne (w szczególnym przypadku takie same), lecz będą one wskazywały na jeden i ten sam i-węzeł, który jest kluczem dostępu do danych w pliku.

Liczba pozycji katalogu (nazw plików) związanych z tym samym i-węzeł zapamiętywana jest w odpowiednim polu tego i-węzeł. Pozwala to systemowi śledzić stan zawartości dysku. Jeśli tylko licznik dowiązanych do pliku nazw zostanie wyzerowany (w wyniku usunięcia ostatniego opisu pliku z katalogu), i-węzeł pliku jest zwalniany, a obszar dysku zajęty przez plik jest oddawany do puli wolnego obszaru pamięci dyskowej, co powoduje fizyczne skasowanie pliku.

Powyższe uwagi można sformułować inaczej: „Wydanie polecenia rm dla poszczególnych dowiązań usuwa te dowiązania; plik istnieje dotąd, dopóki istnieje dla niego choć jedno dowiązanie. Z chwilą usunięcia ostatniego dowiązania usuwany jest również sam plik".

I-węzeł opisuje atrybuty pliku i jest zdefiniowany przez strukturę dinode. Jej definicje dla różnych odmian Unixa mogą różnić się w szczegółach, ale we wszystkich z nich są one umieszczone w plikach:

/usr/include/sys/ino.h /usr/include/sys/inode.h

Poniżej pokazano dla orientacji prostszy wariant dinode, zdefiniowany w SCO UNIX (będący praktvcznie podzbiorem wariantu stosowanego w HP-UX):

0x01 graphic

Pierwsze pole o nazwie di_mode zawiera bity charakteryzujące typ pliku i kod praw dostępu. Ich wartości są zdefiniowane i skomentowane w wyżej wymienionych plikach definicyjnych i opisane w rozdziale „Tryb wykorzystania pliku" (rozdział 4.3.2).

I-węzeł w ramach jednego systemu plików są ponumerowane począwszy od jedności, przy czym w wielu odmianach Unixa i-węzta o numerze l nie używa żaden plik; reprezentuje on bowiem pseudoplik zawierający listę uszkodzonych bloków wolumenu, zajętego przez system plików. Drugi i-węzeł zawsze dotyczy katalogu głównego oraz pozycji tego katalogu oznaczonych nazwami . i... Pozostałe i-węzeł nie mają wyróżnionego znaczenia i są przydzielane plikom w miarę potrzeby.

Rysunek 4.6 ilustruje standardowy sposób adresowania bloków pliku, wykorzystujący tablicę adresów di_addr, wskazaną w i-węźle pliku.

0x01 graphic

Rysunek 4.6. Schemat adresacji bloków pliku.

Biorąc pod uwagę stosowaną w danym systemie wielkość bloku, łatwo obliczyć maksymalną wielkość pliku w danym systemie. Historycznie pierwszą stosowaną wielkością bloku było 512 bajtów (w nowszych odmianach systemu są to zwykle 1024 bajty, co pozwala obsługiwać tym systemom partycje dyskowe i pliki o wielkości do 2 GB). W SCO UNIX 3.2.4.x pośrednie listy adresów bloków zamiast 128 zawierają 256 adresów.

4.1.5.2. Optymalizacja operacji dyskowych

Przy ocenie efektywności systemu plików Unixa należy mieć na uwadze to, że po otwarciu pliku jego i-węzeł jest wczytany do pamięci i w ten sposób system ma dostęp do części (lub całości) numerów bloków pliku. Maksymalna liczba wczytanych i-węzłów, czyli Jednocześnie otwartych plików zależy od parametrów generacji systemu. Ponadto, system intensywnie buforuje operacje wejścia/wyjścia, ograniczając liczbę odwołań do dysku, wskutek czego, dla tego samego pliku otwieranego kilka razy, w pamięci jest zwykle już "pamiętany" jego i-węzeł i część bloków.

Charakterystyki czasowe systemu plików określone są głównie przez szybkość działania pamięci dyskowych. Osobliwością systemu Unix jest to, że system posługuje się pamięcią podręczną (ang. cache memory), której idea polega na tym, że najczęściej używane bloki aktualnie przetwarzanego pliku są czasowo przechowywane w pamięci operacyjnej. Takie tymczasowe buforowanie bloków pliku, w połączeniu ze wstępnym czytaniem bloków nadmiarowych i wykorzystaniem odłożonego w czasie ich zapisu oraz efektywnych algorytmów dostępu do poszczególnych bloków plików dyskowych, pozwala na dostatecznie efektywne przetwarzanie plików zarówno metodami sekwencyjnego, jak i bezpośredniego dostępu. Na mechanizmach tych obecnie często są wzorowane podobne rozwiązania stosowane w innych systemach operacyjnych (na przykład smartdrv w systemie DOS).

Działanie pamięci podręcznej opiera się na teorii, że użytkownicy komputerów mają skłonność do używania w czasie pracy wciąż tych samych lub sąsiadujących danych i ich ostatnie odczyty z dysku zatrzymują się w buforze pamięci. Powiedzmy, że system potrzebuje po raz kolejny tej samej informacji. Najpierw szuka jej w pamięci podręcznej i jeżeli ją tam znajduje, wówczas dostajemy coś, co można by nazwać błyskawicznym dostępem. Jeżeli nie znajduje, to system wraca do poprzedniego stanu i wczytuje potrzebne dane z dysku, a to jest dość wolny proces. Twórcy Unixa poszli znacznie dalej: wprowadzili również buforowanie z opóźnionym zapisem. Metoda zapisu opóźnionego polega na tym, że system opóźnia proces zapisu na dysk i przechowuje dane w buforze aż do chwili, gdy będzie mógł dokonać zapisu na dysk bez konfliktu z operacjami wejścia/wyjścia innego zadania. W systemie DOS opóźniony zapis wprowadzono w dwadzieścia lat później (w smartdrv systemu Windows 3.1).

Metoda ta znacznie zwiększa efektywność obsługi dysku, ale jest niebezpieczna w przypadku zaniku lub wyłączenia zasilania komputera. Aby ten problem ograniczyć, co pewien czas (zwykle 30 sęk) system zapisuje zawartość buforów na dysk, bo częściowo zapisane dane byłyby bezużyteczne, a częściowo zapisane pliki mogłyby wywołać zakłócenie w działaniu systemu.

4.1.5.3. Kontrola integralności systemu plików

Intensywne korzystanie z opisanej w poprzednim rozdziale pamięci podręcznej podnosi efektywność systemu, jednakże w przypadku awarii systemu prowadzi do utraty informacji z pewnych bloków otwartych plików i nieintegralności i-wezłów. Aby złagodzić skutki tego, system wykonuje regularne (zwykle co 30 sekund) zapisy na dysk rezydujących w pamięci i-wezłów i buforowanych zapisów.

Kontrolę integralności systemu plików można przeprowadzić poleceniami:

icheck — sprawdza głównie integralność listy wolnych bloków, poprawność numerów bloków pamiętanych w i-wezlach plików, ich zdublowanie itp. oraz jest w stanie poprawić część błędów;

dcheck — sprawdza i koryguje poprawność struktury katalogów;

fsck — łączy funkcje własne z funkcjami poleceń

icheck i dcheck. Jest wykonywane „automatycznie" przy starcie systemu, jeżeli ten stwierdzi, że po ostatnim uruchomieniu — przed wyłączeniem komputera — nie wykonano polecenia Shutdown. Może być uruchamiane w trybie „ręcznym" i „automatycznym". W tym drugim przypadku samodzielnie koryguje większość problemów związanych z integralnością systemu plików. W niektórych odmianach Unixa do kontroli integralności systemu plików mogą być używane również inne polecenia, na przykład w systemie SCO UNIX polecenie /tcb/bin/integrity.

4.1.5.4. Odrębne systemy plików i ich dołączanie

System Unix pozwala obsługiwać wiele odrębnych systemów plików, zapisanych na odrębnych dyskach lub w ich wyodrębnionych fragmentach nazywanych partycjami (w HP-UX: sekcjami), traktowanych jako odrębne wolumeny dostępnej pamięci dyskowej. Aby mogły być one dostępne, muszą być zorganizowane w jeden globalny system plików poprzez tzw. dołączenie systemu plików jednego wolumenu w wyodrębnionym katalogu drugiego (poleceniem mount).

SCO UNIX może być instalowany na tym samym dysku co MS DOS, w odrębnej partycji dyskowej i może obsługiwać kilka partycji dyskowych, przy czym mogą się one znajdować narożnych dyskach. Pierwsza partycja dysku głównego musi być przeznaczona dla systemu MS DOS. Wszystkie te partycje mogą być połączone w jeden system plików.

Unixowy system plików może być „dołączany" i „rozłączany". Po dołączeniu staje się on częścią ogólnej hierarchii katalogów i jego użytkownicy bardzo często nie są świadomi tego, że ich katalogi osobiste aktualnie znajdują się na innym dysku lub jego partycji;

z zastrzeżeniem, że we wcześniejszych implementacjach systemu nie mogą tworzyć między różnymi wolumenami dowiązań symbolicznych (rozdział 4.1.3.7).

Katalog główny systemu plików znajduje się zawsze na dysku systemowym (zawierającym jądro i standardowe katalogi systemu), jednakże nie oznacza to, że wszystkie pliki muszą być także na nim przechowywane. Do organizacji połączeń w hierarchii plików umieszczonych na różnych dyskach i/lub ich partycjach, stosuje się dołączanie systemu plików, wykonywane poleceniem mount. Pierwszym parametrem tego polecenia jest nazwa pliku specjalnego, określającego dołączany system plików, a drugim nazwa dowolnego pustego katalogu. Polecenie to ma następującą składnię:

mount [opcje] plik_specjalny /katalog

Oto przykłady takiego polecenia:

mount /dev/dsk/c1d0s2 /mnt (hp-ux) mount /dev/dsk/hd02 /mnt (SCO UNIX)

0x01 graphic

Rysunek 4.7. Idea dołączania dodatkowego systemu plików

Operacja „dołączania" systemu plików sprowadza się do następujących działań:

• w głównym systemie plików wybiera się pewien istniejący pusty katalog (w powyższym przykładzie jest nim katalog /mnt), który po wykonaniu polecenia mount staje się katalogiem głównym dołączonego systemu plików (w powyższym przykładzie: /dev/dsk/cld0s2 lub /dev/dsk/hd02);

• poprzez ten katalog; wskazany system plików dołączony zostaje jako poddrzewo do ogólnego drzewa plików, przy czym po dołączeniu nie ma różnicy logicznej pomiędzy systemem podstawowym i dołączonym.

Należy pamiętać, że we wcześniejszych implementacjach systemu Unix istnieje istotne ograniczenie dotyczące dołączania systemu plików: nie wolno tworzyć dowiązań między plikami i katalogami znajdującymi się w odrębnych systemach plików. Ograniczenie to uwarunkowane jest tym, że takie związki bardzo skomplikowałyby zarządzanie plikami i wymagałyby śledzenia tych związków przy rozłączaniu systemu plików. Nie można również przenosić plików poleceniem mv pomiędzy systemami plików, połączonymi poprzez „dołączanie". W nowszych realizacjach systemu Unix (w HP-UX i w SCO UNIX od wersji 3.2.4.x) ograniczenie to zostało praktycznie wyeliminowane przez wprowadzenie dowiązań symbolicznych.

Oto przykład odłączenia systemu plików, dołączonego do katalogu /mnt:

umount /mnt

W poleceniu umount zamiast nazwy katalogu można podać również nazwę pliku specjalnego, identyfikującego odłączany system plików. Opisywane dołączanie systemu plików nie występuje w systemie DOS.

Idea „dołączania" systemu plików wnosi do Unixa szereg możliwości i korzyści:

• Pozwala dokonać podziału dużego dysku na kilka partycji, które mogą być poddawane odrębnie wybranym operacjom: testowaniu integralności (rozdział 4.1.5.3,13) lub archiwowaniu, a ponadto (po połączeniu) udostępniane w ramach globalnego systemu plików;

• Pozwala dość swobodnie rozbudowywać instalację komputerową o kolejne dyski, które można dołączać do globalnego systemu plików;

• Pozwala dołączać systemy plików z innych komputerów (nie tylko unixowych) i traktować je w danej chwili jako uzupełnienie głównego systemu plików — na przykład po zainstalowaniu odpowiedniego oprogramowania serwer Novella może udostępniać pliki systemom unixowym.

Dla użytkowników systemu SCO UNIX ciekawa może być możliwość dołączenia systemów plikowych DOSa zainstalowanego na tym samym dysku co Unix oraz możliwość podłączenia dyskietek dosowych.

Oto przykłady. Ddołączenie dosowej dyskietki umieszczonej w napędzie A do katalogu /mnt:

mount -r -f DOS /dev/dsk/fd0 /mnt Dołączenie dosowego dysku C: do katalogu /mnt:

mount -r -f DOS /dev/dsk/hd04 /mnt

Aby dołączanie nośników dosowych było możliwe, SCO UNIX wymaga zainstalowania obsługi tych nośników (poleceniem mkdev dos).

Dołączanie na danym komputerze systemu plików innego komputera, włączonego do sieci komputerowej, wymaga aby, na obu komputerach było zainstalowane i odpowiednio skonfigurowane oprogramowanie TCP/IP oraz NFS. Ponadto konieczne jest przygotowanie odpowiednich informacji w plikach:

/etc/hosts pliki niezbędne dla komunikujących się komputerów unixowych, zawierające adresy internetowe komputerów włączonych do sieci (hostów);

/etc/exports plik zawierający w kolejnych wierszach nazwy katalogów, które mogą by6 udostępniane innym komputerom, na przykład zawierający następujące nazwy katalogów:

/ /users

Wiersze pliku /etc/hosts mają następującą składnię:

<adres_internetowy> <nazwa_hosta> <synonimy_nazw> Oto przykład zawartości takiego pliku /etc/hosts:

0x01 graphic

Adresy internetowe komputerów muszą spełniać wymogi standardu Internę! (rozdział 2.2 i 16), zwykle są zestawem czterech liczb, oddzielonych znakiem kropki. Pierwsze trzy składniki zwykle identyfikują rodzaj sieci i mają znaczenie głównie wtedy, kiedy dany komputer lub ich sieć lokalna komunikuje się z siecią publiczną. Można przyjąć, że w sieciach lokalnych (LAN) istotne jest jedynie to, aby w ramach danej sieci pierwsze trzy składniki na każdym włączonym do sieci komputerze miały tę samą wartość a ostatnie numery jednoznacznie identyfikowały komputery włączone do sieci.

Przykładowo, uwzględniając powyższy przykład pliku /etc/hosts, chcąc do katalogu /HP lokalnego komputera podłączyć system plików innego komputera unixowego o nazwie systemowej hp832s — należy wykonać polecenie:

mount -f NFS hp832s:/ /HP

W poleceniu tym hp832s identyfikuje nazwę komputera (hosta), a występujący po dwukropku symbol / — jego katalog główny, podłączany do katalogu /HP.

4.2. Ogólna struktura systemu

Patrząc z zewnątrz na system Unix, dostrzec w nim można następujące składowe:

shella przyjmującego polecenia użytkownika i obsługującego ich wykonanie;

• zorganizowany hierarchicznie zbiór plików, nazywany systemem plików;

obszerny zbiór programów, odpowiadający poleceniom użytkownika.

Wewnętrzna część systemu jest o wiele bardziej skomplikowana od przedstawionej powyżej Można ją podzielić na trzy składowe:

zestaw funkcji systemowych (zwanych niekiedy wywołaniami systemowymi — ang. system calls) będący interfejsem pomiędzy programami a jądrem systemu i urządzeniami;

jądro systemu (ang. kernel) — zapewniające współpracę wszystkich elementów systemu;

zestaw procedur obsługi urządzeń. — wykonujących funkcje ściśle związane z obsługą sprzętu.

Znaczna część jądra stale rezyduje w pamięci operacyjnej, zajmując stosunkowo niewielki jej obszar, natomiast procedury usługowe są wywoływane zwykle jedynie wówczas, gdy są potrzebne. Stosowane w tym zakresie rozwiązania są jednak ściśle zależne od odmiany systemu. Kolejny przybliżony obraz struktury systemu pokazuje rysunek 4.8.

0x01 graphic

Rysunek 4.8. Warstwy systemu Unix

Jak wynika z rysunku 4.8, system posiada strukturę warstwową, przy czym najwyższą z nich stanowi shell, obsługujący komunikację użytkownika z systemem. Kolejną warstwą jest obszerny zestaw funkcji systemowych, przy pomocy których programy wystawiają swoje żądania do jądra systemu, natomiast najniższą z nich — procedury obsługi urządzęń zewnętrznych. Pionowe linie na rysunku 4.8 pokazują, jak głęboko w system może sięgać użytkownik danego typu.

W wielu instalacjach systemu Unix jego użytkownicy posługują się różnymi nakładkami na system, w tym interfejsami okienkowymi, jednakże nie oznacza to rezygnacji z usług standardowego shella. ponieważ jest on w nich dostępny albo w sposób „przezroczysty" (tak jak w przypadku programu Mix Commander, firmy KaNet, będącego odpowiednikiem Norton Commandera), albo jako proces uruchamiany w wydzielonym oknie.

Jednostką sterowania i wykorzystania zasobów systemu jest proces (inaczej: zadanie), przy czymi przez proces można rozumieć uruchomiony program wraz z aktualnie związanym z nim systemowym środowiskiem pracy. Przez środowisko to rozumie się zestaw zdlefiniowanych zmiennych shella, definiujących warunki procy poleceń, a ponadto zbiór otwartych przez program plików, praw dostępu do nich, nazwę właściciela procesu, a także inne obiekty zewnętrzne względem procesu, niezbędne do jego wykonania.

Znaczącą część prac w systemie Unix wykonują autonomiczne programy, zapisane w odrębnych plikach, których nazwy shell traktuje tak samo, jak nazwy poleceń systemowych.

Słowo program jest zwykle rezerwowane na oznaczenie pliku zwykłego, zawierającego wykonywalny kod binarny lub listę poleceń. Program binarny to zestaw instrukcji i danych. Pliki tego typu mają zwykle przyporządkowany „atrybut wykonalności".

4.2.1. Jądro systemu

Większość użytkowników systemu Unix komunikuje się tylko z shellem, a nie bezpośrednio z jądrem systemu, wobec czego znajomość budowy jądra i zasad jego pracy jest im w praktyce rzadko potrzebna. Rozdział ten przeznaczono głównie dla administratora systemu i tych użytkowników, którzy na temat wewnętrznej budowy systemu chcą wiedzieć coś

Jądro systemu (ang. kernel) to część systemu operacyjnego, której znaczna część w czasie pracy systemu stale rezyduje w pamięci operacyjnej. Nadzoruje ono przydział czasu

procesora, obszaru pamięci i urządzeń zewnętrznych różnym procesom, które mogą być

równocześnie wykonywane, przy czym każdy z procesów jest związany zwykle z innym

użytkownikiem. Część funkcji jądra systemu realizowana jest przez procedury obsługi urządzeń, wykonujące takie elementarne operacje, jak pobieranie znaków z klawiatury, współpracę z pamięcią dyskową i nadzór nad zegarem systemowym.

System Unix ukrywa przed użytkownikiem szczegóły budowy sprzętu i systemu poprzez shella pośredniczącego pomiędzy użytkownikiem a sprzętem i niższymi warstwami systemu. Na niższym poziomie podobną rolę spełnia jądro systemu, ukrywając realną

maszynę przed programami i zaawansowanymi programistami, dostarczając im uproszczony i ujednolicony (wirtualny) obraz tej maszyny. W ten sposób użytkownik posługuje się

wirtualną maszyną unixową, widzianą na najwyższym poziomie przez pryzmat własności

shella , a na niższym — przez pryzmat własności funkcji systemowych.

Większość odmian Unixa, wychodząc naprzeciw potrzebom różnych typów użytkowników,

udostępnia kilka alternatywnych shelli, z których najpopularniejsze są reprezentowane przez programy: sh, ksh i csh, opisane w dalszej części podręcznika.

Wirtualna maszyna unixowa, określona własnościami jądra, realizuje następujące funkcje:

• zarządza wykonaniem procesów, szeregując kolejność ich obsługi oraz przydzielając im pamięć i czas procesora;

• wykonuje operacje wejścia/wyjścia i zarządzanie plikami;

• obsługuje wszystkie inne operacje związane bezpośrednio ze sprzętem.

Jądro jest zapisane na dysku w pliku mającym atrybut „wykonywalny". Niekiedy przez jądro rozumie się pewną liczbę niezależnych procesów (wczytywanych z odrębnych plików), odpowiedzialnych za poszczególne zadania systemu.

Po załadowaniu jądra do pamięci przez program ładujący (ang. bootstrap), przejmuje ono sterowanie komputerem i wykonuje działania niezbędne do pełnego uruchomienia systemu. Jądro pracuje w trybie system. Zawiera ono procedury wykonujące zarządzanie zasobami systemu oraz struktury danych, wykorzystywane przez te procedury. Wewnętrzna struktura jądra jest stała, to znaczy liczba i rozmiar wykorzystywanych przez nie struktur danych (deskryptory procesów i plików, bufory systemowe) oraz zestawy dołączonych procedur obsługi urządzeń określane są podczas generacji systemu, co określa tym samym jego graniczne charakterystyki ilościowe (nowsze odmiany Unixa pozwalają na dynamiczne ładowanie procedur obsługi urządzeń). Generację jądro systemu ze zmienionymi parametrami może przeprowadzić tylko użytkownik uprzywilejowany.

Elementy jądra systemu Unix ściśle związane ze sprzętem (około 5 - 10% kodu jądro), dla każdego typu komputera zostały napisane w jego języku wewnętrznym (asemblerze). Pozostałe elementy Unixa, włącznie z shellami, dla komputerów danego typu tworzone były praktycznie w całości w języku C — popularnym obecnie języku programowania. Taka struktura systemu, w której nawet sam shell korzysta z pojęcia maszyny wirtualnej, ułatwia firmom (produkującym zarówno komputery, jak i systemy operacyjne) przenoszenie systemu Unix na różne platformy sprzętowe, określone przez różne typy komputerów, i jest powodem jego ciągle rosnącej popularności.

Wszystkie procesy wykonywane poza jądrem systemu są traktowane jako procesy wykonywane w trybie użytkownik.

4.2.2. Funkcje systemowe

Jądro koordynujące pracę całego systemu część zadań realizuje przez funkcje systemowe (nazywane również wywołaniami systemowymi — ang. system calls), udostępniające na poziomie języków programowania liczny zestaw funkcji. Należą do nich między innymi funkcje realizujące:

• operacje na systemie plików (kreowanie, dołączanie, zmiana, tworzenie i kasowanie katalogów itp.);

• operacje na plikach (kreowanie, przenoszenie, otwieranie, pobieranie i zapisywanie znaków itp.);

• operacje dotyczące procesów (inicjowanie, kończenie, komunikacja między procesami itp.);

• operacje dotyczące zegara systemowego i wielozadaniowości.

Część funkcji systemowych jedynie pobiera potrzebne im informacje od jądra systemu z jego tablic systemowych, inne zaś zapisują odpowiednie informacje do rejestrów sprzętowych i do wymienionych tablic lub tworzonych plików. Można odwoływać się do nich z programów napisanych w językach wyższego poziomu (C, Pascal, Fortran, itp.), dokładnie tak, jakby były funkcjami zdefiniowanymi w tych językach. Z funkcji systemowych korzysta również pewna grupa poleceń systemowych, takich jak: chdir, mkdir, rmdir, mount, kill, nice, sleep.

Funkcje systemowe definiuje Unix. Cała jego reszta — szczególnie polecenia — jest zbudowana na ich podstawie. Określając Unix jako system elegancki, prosty i przenośny, ma się zwykle na myśli nie polecenia (niektóre z nich nie mają żadnej z tych cech), lecz właśnie jądro i funkcje systemowe.

Z wcześniejszych rozdziałów wiemy już, że różne odmiany Unixa bazują na jego dwu podstawowych odmianach: Unix System V i BSD Unix. Obie odmiany mają wiele wspólnego i na poziomie standardowych shelli i poleceń niewiele się różnią. Istotne różnice występują jednakże na poziomie dostępnych funkcji systemowych, co jest interesujące głównie dla programistów piszących programy, szczególnie dla tych korzystających z języka C.

Funkcje systemowe do obsługi plików. Unix wykonuje wszystkie podstawowe operacje dotyczące plików przy pomocy funkcji systemowych. Ważnym pojęciem związanym z tymi funkcjami jest deskryptor pliku (ang. file descriptor). Jest to nieujemna liczba całkowita, jednoznacznie związana z każdym plikiem wykorzystywanym przez proces. Każdy proces ma do dyspozycji pewną pulę deskryptorów (co najmniej 20) ponumerowanych od 0. do N. Użycie jakiegokolwiek pliku wymaga najpierw jego otwarcia. W czasie otwierania pliku system wiąże z nim jeden z deskryptorów. Spełnia on rolę systemowego identyfikatora pliku, używanego w pozostałych funkcjach systemowych. Korzystając z tych funkcji w języku C nie należy mylić deskryptora ze wskaźnikiem typu FILE, używanym przez funkcje z serii fprintf, fscan itp.

Każdy proces po uruchomieniu otrzymuje od swego procesu macierzystego deskryptory trzech już otwartych plików (rozdział 4.2.3.7):

0 standardowy strumień wejściowy (domyślnie związany z klawiaturą);

1 standardowy strumień wyjściowy (domyślnie związany z ekranem);

2 standardowy strumień wyjścia diagnostycznego (również domyślnie związany z ekranem).

Z każdym plikiem jest związany wskaźnik bieżącej pozycji pliku, określający miejsce w pliku do odczytu lub zapisu kolejnego bąjtu. Każda operacja zapisu lub odczytu przesuwa wskaźnik o odpowiednią liczbę bajtów do przodu.

Poniżej wymieniono kilka ważniejszych funkcji systemowych, w konwencji ich wywołań w Języku C (oznaczając przez fd — deskryptor pliku):

create (ścieżka,prawa) — utworzenie pliku, zwraca wartość fd;

open (sciezka,tryb) — otwarcie pliku, zwraca wartość fd;

write (fd,bufor,n) — zapis w pliku n bajtów;

read (fd,bufw,n) — odczyt z pliku;

lseek (fd,n,ltryb) — przesunięcie wskaźnika pozycji pliku;

close (fd) — zamknięcie pliku;

link (ścieżkal ,ścieżka2) — utworzenie dowiązania pliku;

unlink (ścieżka) — usunięcie dowiązania;

chdir (ścieżka) — zmiana katalogu.

Wywołanie funkcji systemowej powoduje zmianę trybu pracy procesu z „użytkowego" na „systemowy" i z powrotem. Wykonanie takich funkcji trwa zatem dłużej niż zwykłej funkcji programu, której kod znajduje się w przestrzeni adresowej procesu.

4.2.3. Zarządzanie procesami

Z wcześniejszych rozdziałów wiemy już, że Unix jest systemem wielodostępnym i wielozadaniowym, pozwalającym użytkownikom z poszczególnych terminali uruchomić jednocześnie wiele zadań (inaczej procesów). Ponieważ typowy komputer wyposażony jest tylko w jeden procesor, w danej chwili w rzeczywistości może być wykonywany tylko jeden proces. Wcześniej użyte określenie „równocześnie" oznacza, że wykonywanie procesów przeplata się w czasie, na tyle szybko, że użytkownicy mają wrażenie jednoczesnej ich pracy. Realizowane jest to poprzez przydzielanie każdemu procesowi krótkiego odcinka czasu dostępu do procesora i innych zasobów sprzętowych komputera. W pamięci operacyjnej, o ile jest ona wystarczająco pojemna, mogą być równocześnie przechowywane obrazy wielu procesów. Jądro systemu odpowiada za sposób wykorzystania przez te procesy czasu procesora i obszaru pamięci, realizując szeregowanie procesów i zarządzanie pamięcią.

4.2.3.1. Inicjowanie procesów

Rzeczą podstawową, związaną z organizacją pracy procesów, jest przede wszystkim tworzenie nowych procesów. Zainicjowanie procesu w systemie Unix jest wykonywane zawsze przez inny proces.

Z chwilą gdy użytkownik rozpoczyna sesję swojej pracy (login:), jądro systemu przydziela mu proces shella. Następnie dla każdego wprowadzonego przez użytkownika polecenia (o ile nie jest to polecenie wewnętrzne), shell stara się odnaleźć odpowiedni program i (o ile taki program jest mu dostępny) wykonuje go w charakterze odrębnego procesu.

Uruchomiony program może wywoływać inne programy (aktywować inne procesy). Powstaje w ten sposób drzewiasta hierarchia wywołań procesów. Hierarchia ta jest tworzona przez mechanizm rozwidlania procesów (ang. fork — rozwidlenie, również nazwa wywołania systemowego, uruchamiającego ten mechanizm). Polega on na tym, że jądro zastępuje proces wywołujący dwoma innymi: wywołującym i wywoływanym.

Proces może być uruchomiony tylko przy pomocy systemowych wywołań z grupy fork i exec, przestaje istnieć (kończy się) z chwilą wykonania wywołania systemowego exit a także po wykonaniu polecenia lub wywołania systemowego kill. Kończąc swoją prac? proces zwraca odpowiedni dziesiętny kod zakończenia swojej pracy, który może być analizowany przez proces wywołujący. Wartość O (true) oznacza poprawne zakończenie

procesu. Proces, wykonujący wywołanie fork, nazywany jest w literaturze unixowej ojcem przodkiem lub procesem macierzystym — ang. purent process), a utworzony przez to wywołanie proces synem (potomkiem lub procesem potomnym — ang. child process). Syn zazwyczaj korzysta z wszystkich plików swego ojca i otrzymuje (dziedziczy) kopię jego środowiska pracy. Po rozwidleniu oba procesy pracują niezależnie, każdy w swoim własnym ze adresowym, chyba że ojciec jawnie żąda zakończenia pracy przez proces syna. Jeżeli proces syna chce wywołać kolejny proces, musi to zrealizować również poprzez mechanizm rozwidlania.

W Unixie istnieje tylko jeden proces pierwotny (w różnych odmianach Unixa ma różne nazwy: swapper — w HP-UX, sched — w SCO UNIX) nie posiadający przodka; każdy inny ma swojego przodka. Standardowo jest nim shell użytkownika, który proces uruchomił. Wyżej wymieniony proces pierwotny, będący praprzodkiem wszystkich procesów, jest odpowiedzialny za zarządzanie pamięcią i przenoszenie programów pomiędzy dyskiem a pamięcią operacyjną. Jego bezpośrednim procesem potomnym jest proces init, czytający plik /etc/inittab, w którym .opisana jest lista procesów aktywowanych po załadowaniu jądra systemu. Na podstawie tej listy proces init inicjuje właściwą pracę systemu; aktywuje między innymi proces cron (obsługa procesów zależnych od zegara) oraz procesy getty (obsługa terminali; dla każdego z nich jest powoływany odrębny proces getty).

4.2.3.2. Identyfikatory procesów i grupy procesów

Każdy utworzony proces otrzymuje po uruchomieniu swój unikatowy numer, nazywany identyfikatorem procesu PID (ang. process identification number) oraz ma dostęp do identyfikatora procesu swojego ojca: PPID (ang. parent process identification number), zwanego również procesem macierzystym.

Procesy aktywowane z danego terminala przez tego samego shella stanowią tzw. grupę

procesów o identyfikatorze PGID (ang. process group identification number; nie należy mylić z GID — identyfikatorem grupy użytkowników). Proces będący procesem

macierzystym grupy procesów (zwykle jest to shell) jest nazywany liderem grupy procesów (|ang. process group leader). Niekiedy złożone systemy oprogramowania użytkowego, na przykład zarządzania bazami danych, wygodnie jest zorganizować jako grupę pokrewnych procesów. W tym przypadku jeden z członków grupy może pełnić rolę lidera grupy.

Zakończenie wykonywania procesu lidera danej grupy procesów powoduje wysłanie sygnału "zerwania łączności" do wszystkich procesów potomnych danego lidera.

Istnieje polecenie ps (ang. process status), pozwalające obejrzeć listę pracujących procesów. Poecenie to wprowadzone bez argumentów wyświetla jedynie listę procesów danego

użytkownika.

4.2.3.3. Szeregowanie procesów

Szeregowanie procesów polega na cyklicznym przydzielaniu procesom kwantów czasu procesora oraz ustalaniu kolejności, w jakiej procesy ten czas otrzymują. Czynności te zależą od priorytetów procesów, aktualnej dostępności ich danych wejściowych oraz urządzeń zewnętrznych. Unix, tak jak większość innych systemów operacyjnych, przydziela kwanty czasu w taki sposób, aby zmaksymalizować wykorzystanie zasobów systemu (głównie procesora i pamięci), dając równocześnie pierwszeństwo procesom o największych wymaganiach.

Ustalanie priorytetów procesów realizowane jest dynamicznie przez system w wyniku analizy dotychczasowego zachowania się procesów i ich wcześniejszego zapotrzebowania na różne zasoby systemu. Priorytety procesów określane są liczbowo, przy czym ich większe wartości określają niższy priorytet. Priorytety związane z aktywnymi procesami można zobaczyć przy pomocy polecenia ps. Użytkownicy mogą wpływać na wysokość priorytetu procesu, deklarując go jako proces drugoplanowy, żądając obniżenia jego priorytetu poleceniem nice lub odkładając wykonanie polecenia w czasie dogodnym dla systemu.

Proces realizuje standardowo takie przetwarzanie sekwencyjne, że w jednym procesie nie mogą zajść żadne zdarzenia wykonywane jednocześnie. W szczególności wejście/wyjście związane z procesem realizowane jest asynchronicznie: proces zatrzymuje swoją pracę do czasu zakończenia operacji wejścia/wyjścia. Jeśli proces ma być wykonywany współbieżnie z zainicjowaną przez niego operacją wejścia/wyjścia, to wcześniej tworzy on inny proces dla wykonania tej operacji. W nowszych odmianach Unixa (NeXTStep, Solaris) jest to podstawa organizacji przetwarzania wielowątkowego (ang. multitherading), polegającego na tym, że podprocesy, z których składa się proces, mogą być wykonywane jednocześnie.

Aktywny proces użytkownika może być wykonywany w jednym z dwu trybów:

• w trybie użytkownik, kiedy wykonuje zwykłe instrukcje;

• w trybie system, kiedy czeka na zakończenie wykonania wywołanej funkcji systemowej; w tym. stanie proces nie może być przerwany (próby zmiany tej reguły podjęto dopiero w systemach SVR4 i OSF/1 oraz niektórych pochodnych od nich odmianach Unixa).

Ponadto proces może znajdować się między innymi w jednym ze stanów:

aktywnym (aktualnie wykonywany);

uśpionym na pewien odcinek czasu;

oczekującym na zakończenie procesu potomnego.

4.2.3.4. Procesy pierwszo- i drugoplanowe

Shell wywołując nowy proces standardowo czeka na jego zakończenie, o ile użytkownik nie zażyczy sobie inaczej. Proces taki jest nazywany procesem pierwszoplanowym. Oczekiwanie to może być anulowane, o ile polecenie inicjujące odpowiedni proces będzie wywołane w charakterze polecenia drugoplanowego, przez dopisanie znaku ampersand (&) na końcu polecenia. Możliwość ta jest osiągalna między innymi dzięki istnieniu opisanego wcześniej mechanizmu rozwidlania procesów.

Procesy pierwszoplanowe mogą być przerwane i usunięte z pamięci przez naciśnięcie klawiszy CtrI-C, Del lub Ctrl-\ (redefmiowalne), natomiast drugoplanowe — poleceniem kill z podanym numerem procesu (listę opisującą aktywne procesy można wyświetlić poleceniem ps). Kolejne informacje dotyczące procesów drugoplanywaych zamieszczono w rozdziale 5.4.

4.2.3.5. Procesy rezydentne — demony

Procesy, które po uruchomieniu stale rezydują w pamięci operacyjnej w terminologii unixowej, są nazywane demonami (ang. daemons); w dosowej: programami rezydentnymi.

Unixowe demony raz uruchomione pracują w „tle" całkiem samodzielnie, a kontakt z terminalem uzyskują tylko wtedy, kiedy informują użytkownika o zaistniałych zdarzeniach,

na przykład o pojawieniu się poczty nadesłanej przez innego użytkownika, wspomniana samodzielność demonów odróżnia je od dosowych rezydentów; te ostatnie wykorzystują przerwania systemowe, pobudzające je do wykonania pewnych działań (w ten sposób „udają" wielozadaniowość).

Istnieje między innymi demon obsługi poczty, nieustannie sprawdzający czy w systemie ktoś nadał jakąś wiadomość, którą należy przekazać innemu użytkownikowi, oraz demon obsługi drukarek, sprawdzający czy ktoś wydał polecenie drukowania. Wewnętrzny świat Unixa jest pełen demonów. Niektóre z nich, jak choćby wspomniany demon-listonosz, są zawsze aktywne i oczekują na obsługę kolejnej poczty. Inne zaś — na przykład demon zegarowy cron pozostają przez większość czasu uśpione, budząc się co pewien czas, aby sprawdzić czy jest dla nich jakieś zadanie do wykonania.

Większość demonów jest uruchamiana automatycznie w chwili ładowania (włączania) systemu lub przechodzenia w tryb pracy wieloużytkownikowej, i zapewnia usługi ogółowi użytkowników. Konkretne demony mogą być też aktywowane dopiero w chwili, gdy są potrzebne, na przykład demony do obsługi wielodostępnych baz danych; jawnie lub w tle wywołanego oprogramowania użytkowego.

Różne odmiany Unixa mogą być „nawiedzone" przez różne demony; wśród nich często spotykamy następujące:

cron wykonywanie poleceń w podanym czasie;

getty obsługa łącza terminalowego;

lpsched — kolejkowanie i wykonywanie wydruków w tle;

syslog zapisywanie komunikatów o błędach w odpowiednim pliku;

swapper zarządzanie pamięcią i chwilowym przenoszeniem obrazów procesów pomiędzy pamięcią operacyjną i dyskiem (w SCO UNIX 3.2v4.x ma on nazwę sched);

syncdaemon okresowe uaktualnianie systemu plików o informacje buforowane w pamięci podręcznej (ang. cache).

4.2.3.6. Przydzielanie procesom pamięci operacyjnej

Przydzielanie pamięci operacyjnej procesom jest ściśle związane z pojęciem obrazu procesu. Jest to obszar pamięci operacyjnej przydzielony uruchomionemu procesowi. Część pamięci, pozostała po załadowaniu jądra, wykorzystywana jest do ładowania obrazów procesów. W razie niedostatku pamięci operacyjnej, realizowane jest chwilowe przeniesienie obrazów tych procesów na dysk do obszaru wymiatania (ang. swcip aren) i załadowanie do zwolnionego obszaru obrazów innych procesów. Pamięć operacyjna przydzielana jest w sposób dynamiczny i użytkownik nie ma możliwości sterowania przydziałem obszarów pamięci o konkretnych adresach fizycznych.

Obraz procesu może mieć jedną z dwu postaci:

wielowejściową (procesy z dzielonym segmentem kodu);

• zwykłą.

W pierwszym przypadku instrukcje programu oraz stałe są przechowywane w segmencie Pamięci, oddzielonym od segmentów przeznaczonych dla modyfikowalnych danych.

Obraz procesu w postaci wielowejsciowej skіada sie z trzech czesci

segmentu kodu (obszaru instrukcji i stałych opisujących proces);

• segmentu danych (obszaru struktur danych i zmiennych);

segmentu danych systemowych (w tym stosu), przechowującego informacje niezbędne do zachowania stanu procesu przy przyznawaniu mu i odbieraniu zasobów (procesor, pamięć, itp.) oraz przy wywoływaniu funkcji systemowych i funkcji własnych.

W przypadku „zwykłej" postaci obrazu procesu powyższe jego elementy mogą być wzajemnie przemieszane. Możliwość rozdziału kodu i danych programu daje następujące korzyści:

• zmniejsza wymagania co do wielkości dostępnej pamięci operacyjnej, gdyż w przypadku wielu popularnych programów, często używanych w tym samym czasie przez wielu użytkowników (na przykład edytor vi), pozwala ten sam segment kodu programu udostępnić jako część wspólną obrazów procesów programu, uruchomionego odrębnie przez tych użytkowników;

• pozwala zminimalizować czas wymiatania programów, gdyż kod programu, który cały czas pozostaje niezmienny, nie musi być zapisywany na dysku w trakcie kolejnego przenoszenia obrazu procesu na dysk, a jedynie wczytywany przy ponownym przenoszeniu obrazu do pamięci operacyjnej.

Pomiędzy procesami o dzielonym segmencie kodu nie ma żadnego funkcjonalnego powiązania. O tym, że jakieś procesy współwykorzystują wspólny segment kodu, procesy te wcale „nie wiedzą".

4.2.3.7. Standardowe wejście/wyjście procesów

Każdy proces może posiadać dostęp do pewnej liczby zwykłych plików danych (zapisanych w pamięci masowej) oraz pewnej liczby urządzeń zewnętrznych. Jak już wiemy z poprzednich rozdziałów, .wszystkie urządzenia zewnętrzne w systemie Unix są widziane jako pliki specjalne. Oba rodzaje plików traktowane są jako strumienie danych. Strumienie danych związane zarówno z klawiaturą jak i ekranem są traktowane przez system jako pewne standardowe pliki danych (określane dalej jako standardowe wejście/wyjście).

Proces mający wykonać pewne działania na pliku zwykłym musi go najpierw otworzyć. Otwarcie pliku powoduje przyporządkowanie plikowi pewnej liczby całkowitej, nazywanej deskryptorem pliku lub numerem logicznym pliku.

Dane opisujące otwarte pliki są przechowywane w pamięci operacyjnej w tablicy plików (ang. file table) przez cały czas istnienia procesu.

Deskryptory o numerach 0, l i 2 mają zarezerwowane znaczenie i oznaczają:

0 — standardowe wejście — domyślnie jest nim strumień znaków wprowadzany

z klawiatury terminala, traktowany przez system jako standardowy plik

wejściowy (stdin — ang. standard input fi1e);

l — standardowe wyjście — domyślnie jest nim strumień znaków

wyprowadzanych na ekran, traktowany przez system jako plik do

wyprowadzania danych (stdout — ang. standard output flie);

2 — standardowe wyjście diagnostyczne — domyślnie jest nim również strumień znaków wyprowadzanych na ekran, traktowany przez system jako plik do wyprowadzania komunikatów o błędach (stderr — ang. standard error file).

Wymienione pliki są zawsze otwarte dla procesu, jednakże nie wszystkie z nich muszą być wykorzystywane. Powyższe przyporządkowanie deskryptorów może być zmienione przez użycie mechanizmów przeniesienia (przeadresowania) standardowego wejścia/wyjścia (rozdział 5.8) oraz mechanizmów przetwarzania potokowego (rozdział 4.2.3.8 oraz 5.9).

4.2.3.8. Komunikacja pomiędzy procesami

Pracujące procesy mogą się komunikować pomiędzy sobą. Znane w obecnych realizacjach Unixa mechanizmy stosowane do komunikacji między procesami pojawiały się wraz z kolejnymi wydaniami systemu. W niniejszym rozdziale dokonano krótkiego przeglądu sposobów komunikowania się procesów, spotykanych w odmianach Unixa bazujących na Systemie V.

Na poziomie interakcji z shellem procesy mogą się komunikować poprzez: pliki, łącza komunikacyjne i sygnały, dlatego w niniejszej książce poświęcono im więcej uwagi. Obok podanych niżej, wiadomości uzupełniające można znaleźć w rozdziale 5. Ponieważ pozostałe mechanizmy są przeznaczone głównie dla zaawansowanych programistów, zwłaszcza korzystających z języka C, opisano je bardzo pobieżnie. Obszerne informacje na temat komunikowania się procesów można znaleźć w książce M. J. Rochkind'a [10].

Komunikacja procesów poprzez pliki

Mechanizm ten jest dostępny w każdym systemie operacyjnym i jest na tyle oczywisty, iż często nie zdajemy sobie sprawy z jego istnienia. W najprostszej formie polega na tym. iż jeden proces tworzy plik, a drugi (uruchomiony po zakończeniu pierwszego) czyta dane zapisane w tym pliku. Ten sposób nie nadaje się jednak do komunikacji pomiędzy procesami pracującymi współbieżnie, dlatego też w przypadku wielodostępnych baz danych stosowane są bardziej wyrafinowane rozwiązania. Inną metodą może być jedynie testowanie czy plik o określonej nazwie istnieje. W tym przypadku może on spełniać rolę swoistego rodzaju wskaźnika (informującego o wystąpieniu pewnego zdarzenia) lub opisywanego dalej semafora.

Sygnały

Unixowy mechanizm obsługi sygnałów pozwala przesłać do wskazanego procesu (lub ich grupy) numer sygnału określający pewne zdarzenie, na które proces-adresat powinien odpowiednio zareagować. Służą one głównie do kończenia pracy procesów, przy czym sygnał skierowany do procesu może pochodzić od:

• jądra systemu (na przykład sygnał przekroczenia przydzielonej procesowi przestrzeni adresowej);

• od tego samego procesu (na przykład sygnał pobudki: proces ustawia zegar i po upływie określonego czasu następuje wysłanie tego sygnału);

• innego procesu (na przykład sygnał o zakończeniu pracy);

• od użytkownika (na przykład sygnał spowodowany naciśnięciem odpowiednich klawiszy lub wprowadzeniem polecenia kill).

Sygnały są zdefiniowane na poziomie jądra systemu i są niezależne od stosowanego shella.

Zbiory tych sygnałów podlegają standaryzacjom, jednakże w poszczególnych realizacjach Unixa definicje sygnałów o numerach powyżej 15 mogą się różnić (rozdział 5.16).

Dla użytkownika systemu szczególnie interesujące są te sygnały, którymi z terminala może wywołać określoną akcję — albo naciskając odpowiednie klawisze, albo wydając polecenie kill. Do sygnałów takich należą sygnały związane z następującymi sytuacjami:

sygnał 2 — przerwanie wykonania procesu nadane z terminala przez naciśnięcie znaku intr (zwykle klawisza Del lub CtrI-C);

sygnał 3 — zakończenie wykonania procesu nadane z terminala przez naciśnięcie znaku quit (zwykle klawiszy Ctri-\; w tym przypadku następuje zapisanie obszaru pamięci zajmowanej przez proces w pliku córę);

sygnał 9 — bezwarunkowe unicestwienie procesu poleceniem kill;

sygnał 15 — warunkowe zakończenie procesu poleceniem kill (proces może mieć zdefiniowaną obsługę tego sygnału).

Jądro systemu standardowo reaguje na sygnał dotyczący procesu operacją zakończenia pracy procesu. Jednakże dla każdego sygnału (za wyjątkiem sygnału „unicestwienia", który zawsze jest ostateczny) proces może mieć zdefiniowany jeden z trzech typów reakcji:

• ignorowanie sygnału (nie dotyczy sygnałów 4, 5 i 9);

• przechwycenie sygnału i jego obsługę;

• standardową reakcję na sygnał, polegającą na zakończeniu procesu.

Sygnały zapewniają łączność między procesami, a także między procesami i terminalami. Proces może wysłać sygnał do innego procesu przy pomocy wywołania systemowego kill. Proces aktywowany przez zwykłego użytkownika może przekazywać sygnały tylko do swoich procesów potomnych. Utworzony proces potomny przejmuje wszystkie bieżące typy reakcji na sygnały od procesu tworzącego. Proces uprzywilejowany (systemowy lub utworzony przez użytkownika uprzywilejowanego) może wysłać sygnał do dowolnego procesu. W innym przypadku procesy nadający i przyjmujący sygnały powinny mieć jednakowe identyfikatory użytkownika. Przy pomocy wywołania systemowego kill (lub polecenia kill) można wysłać sygnały do wszystkich procesów związanych z danym terminalem. Listę sygnałów obsługiwanych przez system oraz korzystanie z nich na poziomie shella sh opisano w rozdziale 5.16.

Śledzenie procesów

Mechanizm śledzenia procesów umożliwia kontrolowanie przez dany proces swoich procesów potomnych. Odpowiednio przygotowany proces macierzysty może czytać oraz pisać w obszarze danych swego potomka. Mechanizm ten jest na tyle skomplikowany i przy nieumiejętnym wykorzystaniu niebezpieczny, że wykorzystywany jest praktycznie jedynie przez specjalne programy (debuggery) służące do śledzenia wykonywania innych procesów.

Łącza komunikacyjne (potoki)

Opisywany tutaj mechanizm należy do najczęściej stosowanych i służy do potokowego przetwarzania danych przez kilka procesów. Na poziomie shella pozwala utworzyć potok poleceń (ang. pipline). System pozwala przekazywać informację wyprowadzaną przez jeden

proces na standardowe wejście kolejnego procesu (informacje te są przekazywane przez pamięć podręczną). Zarówno wyprowadzanie, jak i wprowadzanie przekazywanej informacji wykonywane jest przy pomocy standardowych operacji na plikach, jednakże łącze nie jest rodzajem pliku, pomimo że na czas istnienia posiada swój i-wezeł.

Opisywany mechanizm rozwiązuje problem synchronizacji dostępu do plików standardowego wejścia/wyjścia procesów, uczestniczących w potoku. Jeśli proces czytający wyprzedzi proces piszący, to będzie wprowadzony w stan oczekiwania na pojawienie się następnych danych. Jeśli natomiast proces piszący wyprzedzi proces czytający, to zostanie uśpiony do chwili, kiedy proces czytający będzie mógł odebrać kolejne dane.

Nawiązanie łączności przez łącze komunikacyjne jest oparte na dziedziczeniu praw do plików, otwartych przez przodka współpracujących procesów. Współpracujące procesy powinny więc być procesami pokrewnymi, co stanowi podstawową wadę tego mechanizmu.

Potoki są znane większości użytkowników Unixa jako mechanizm shellowy (rozdział 5.9). Chcąc wyświetlić posortowaną listę nazw wszystkich aktualnie pracujących użytkowników. należy wprowadzić następujące polecenie potokowe

who | sort | more

Określa ono trzy procesy połączone za pomocą dwóch łączy komunikacyjnych. W przykładzie tym dane przepływają tylko w jednym kierunku: od procesu who do sort i dalej do more.

Pomimo że snęli nie dostarcza mechanizmów tego typu, przy pomocy funkcji systemowych można tworzyć również potoki dwukierunkowe (z przepływem danych od procesu A do B i z powrotem od B do A) oraz potoki pierścienie.

Łącza nazwane (kolejki proste FIFO)

Łącza nazwane pozwalają przesyłać pomiędzy procesami porcje danych obsługiwane na zasadzie kolejki FIFO (ang. first-in-first-out—pierwszy-przyszedł-pierwszy-obsłużony). Raz odczytane z nich dane nie mogą być ponownie pobrane. Łączą cechy komunikacji poprzez pliki i łącza komunikacyjne, jednakże nad tymi drugimi mają dwie przewagi:

• komunikujące się procesy nie muszą być procesami pokrewnymi;

• dozwolone jest ich użycie również w przypadku wielu procesów, jednocześnie piszących i czytających dane z tej samej kolejki.

Łącze nazwane jest plikiem specjalnym, który może zostać otwarty do czytania lub pisania przez każdy proces mający odpowiednie uprawnienia.

Semafory

Semafor to mechanizm (dostarczany przez jądro systemu) pozwalający zapobiegać . sytuacjom, w których co najmniej dwa procesy konkurują o dostęp do dzielonych zasobów. Wyróżnia się semafory dwustanowe oraz wielostanowe. Semafor dwustanowy jest wskaźnikiem (przechowywanym w pamięci operacyjnej), który uniemożliwia dwóm lub większej liczbie procesów dostęp do tego samego zasobu w tym samym czasie. Mechanizm ten można by symulować z wykorzystaniem plików, ale byłoby to rozwiązanie znacznie powolniejsze.

Komunikaty

Komunikat to niewielka porcja danych, którą można przesłać do kolejki komunikatów — są w niej przechowywane w kolejności nadsyłania. Komunikaty mogą być różnych typów. Każdy proces, mający odpowiednie uprawnienia, może pobierać komunikaty z kolejki. Pobieranie może być realizowane w różny sposób, na przykład: można pobrać komunikat pierwszy z kolejki, pierwszy danego typu itp.

Pamięć dzielona

Idea pamięci dzielonej polega na tym, że ten sam obszar pamięci wchodzi w skład przestrzeni adresowej kilku procesów. Dane są dostępne dla pozostałych procesów, natychmiast po wpisaniu do pamięci dzielonej przez którykolwiek z procesów. Jest to najszybszy sposób komunikacji pomiędzy procesami.

4.2.3.9. Typowe polecenia do obsługi procesów

Do obsługi procesów mogą być wykorzystywane między innymi następujące polecenia:

at — wykonanie listy poleceń w podanym czasie;

batch — wykonanie listy poleceń w czasie dogodnym dla systemu;

crontab — obsługa listy cyklicznie wykonywanych poleceń;

nice — wykonanie polecenia z obniżeniem jego priorytetu;

kill — wystanie sygnału unicestwienia do podanego procesu;

nohup — uruchomienie procesu w trybie ignorowania sygnałów;

ps — wyprowadzenie informacji o procesach rezydujących w pamięci;

sleep — uśpienie procesu na podany odcinek czasu.

Wymienione polecenia są opisane w rozdziałach 5 i 6.

W dalszych częściach podręcznika szczególną uwagę należy zwrócić na informacje poświęcone poleceniom ps i kill, których brak w systemie DOS powoduje, że w przypadku zawieszenia się dosowego programu jedynym wyjściem z sytuacji jest zwykle przełączenie zasilania komputera (reset). W Unixie zawieszenie się programu użytkownika powoduje jedynie chwilowe zawieszenie pracy na danym terminalu, który może być odblokowany z innego terminala właśnie dzięki wspomnianym poleceniom.

4.2.4. Data i czas

W wielodostępnym i wielozadaniowym systemie Unix jest wiele sytuacji takich, w których stan wielu procesów jest zależny od aktualnego czasu. Dotyczy to zarówno przydzielania poszczególnym procesom kwantów czasu dostępu do procesora, jak i możliwości wykonywania procesów w określonym czasie. Ponadto data i czas są często wykorzystywane do odnotowywania różnych zdarzeń, takich jak czas powstania pliku, czas powstania pewnych zdarzeń opisywanych w bazach danych itp.

W systemie Unix używane są dwa rodzaje czasów: czas wykonania procesu i czas kalendarzowy.

4.2.4.1. Czas wykonania procesu

Czas wykonania procesu służy do mierzenia przedziałów czasu, synchronizacji procesów i sprawozdawczości (o ile jest włączony tryb zbierania danych statystycznych). Jądro systemu automatycznie rejestruje czas wykonania każdego procesu. Jest on wyrażany w systemowych taktach, przy czym, w zależności od systemu, jest ich 60 lub 100 na sekundę. Czas wykonania procesu można uzyskać funkcją systemową i poleceniem o nazwie times.

4.2.4.2. Czas kalendarzowy

Czas kalendarzowy jest zapisywany w postaci liczby sekund, które upłynęły od pewnej umownej daty (zwykle l stycznia 1970 roku) czasu GMT (ang. Greenwich Mean Time — czas zachodnioeuropejski; w HP-UX określany jako UCT — ang. Universal Coordinated Time).

Czas kalendarzowy służy do określania czasów utworzenia, dostępu, modyfikacji oraz zmiany statusu plików, rejestracji czasu rozpoczęcia pracy przez poszczególnych użytkowników systemu itp. W programach użytkowych czas ten można stosować również do zapamiętywania różnych dat w zbiorach danych, aczkolwiek w tym celu częściej stosowany jest czas lokalny, związany z ustawioną w systemie strefą czasową.

Jądro systemu obsługuje wewnętrznie wyłącznie strefę GMT. Dla użytkowników wygodniejszy jest jednak czas lokalny, uwzględniający ich strefę czasową oraz czas letni i zimowy. Problem ten rozwiązują programy użytkowe i niektóre polecenia (na przykład wyświetlające zawartość katalogu) posługując się pakietem procedur ctime.

Systemowa nazwa strefy czasowej, w której przebywa i pracuje użytkownik, jest przechowywana w zmiennej środowiskowej TZ (ang. Time Żonę). Jej wartość jest zwykle określana w trakcie instalacji systemu. Jest to ciąg znaków, w którym można wyodrębnić następujące trzy grupy:

• trzy znaki, określające nazwę strefy czasowej (na przykład "MET");

• różnica czasowa w stosunku do czasu GMT (na przykład "-1 MET");

• trzy znaki, określające nazwę czasu letniego (o ile jest używany, na przykład "DST").

Różnica czasowa jest dodatnia dla stref położonych na zachód od Greenwich (dzielnicy Londynu) i ujemna dla stref wschodnich, przykładowo — gdy w Londynie jest godzina 12°°, w Warszawie jest godzina 13°°, a w Moskwie 15°°.

Z powyższych względów, dla obszaru Polski, w Unixie stosowana jest zwykle następująca wartość zmiennej TZ:

TZ=MET-1METDST

4.2.4.3. Wykonywanie procesów w podanym czasie

Przydzielanie procesom czasu dostępu do procesora jest realizowane przez jądro, natomiast wykonywanie procesów zależnych od czasu jest synchronizowane za pomocą programu cron, który w terminologii unixowej jest znany jako demon zegarowy. Wykonanie tego Programu powoduje utworzenie rezydentnego procesu, kontrolującego co pewien czas stan

innych procesów zależnych od zegara. Sprawdza on odpowiednią tablicę systemową (przechowywaną w pliku o określonej nazwie), poszukując w niej opisu zdarzeń wymagających jego udziału. Każdy taki opis określa proces, który ma być powołany do jego obsługi.

W większości systemów unixowych cron wywołuje cyklicznie funkcję sync, przenoszącą co pewien czas (zwykle 30 sekund) zawartość wszystkich buforów systemowych na dysk (w niektórych odmianach systemu zadanie to jest wykonywane przez proces o nazwie syncdaenion). Administratorzy systemu bardzo często korzystają z usług programu cron, aby uruchomić pewne programy o określonych godzinach (przykładowo: w nocy — programy rozliczeniowe, gdyż komputer jest wówczas mniej obciążony). Także zwykli użytkownicy mogą polecać mu obsługę swoich procesów.

Istniej ą polecenia umożliwiające wykonanie wskazanych procesów w określonym czasie, np. polecenia at i batch (opisywane dokładnie w rozdziale poświęconym poleceniom):

at — wykonanie poleceń w określonym czasie;

batch — wykonanie poleceń w czasie dogodnym dla systemu.

4.2.4.4. Wewnętrzny budzik procesu

Każdy proces ma swój programowy budzik, przechowywany we własnym segmencie danych. Kiedy nadchodzi czas pobudki, wtedy jest wysyłany sygnał SIGALRM. Czas pobudki jest ustawiany funkcją systemową alarm. Proces potomny dziedziczy po swoim przodku ustawienie czasu pobudki, jednakże sam budzik nie jest dziedziczony.

Istnieje szereg funkcji systemowych przeznaczonych do obsługi czasu. Przykładowo, funkcja stime podaje czas użytkowy i systemowy wykorzystania procesora przez procesy, funkcja alarm służy do ustawienia czasu pobudki, która ma nastąpić po podanej liczbie sekund. natomiast funkcja (i polecenie) sleep służy do usypiania (zawieszania pracy) procesu na podany czas.

4.3. Wielodostęp i ochrona zasobów

Unix jest systemem wielodostępnym; stąd bierze się konieczność zagwarantowania wzajemnej niezależności i bezpieczeństwa zasobów poszczególnych użytkowników. Jest to realizowane między innymi poprzez system haseł, kontrolę uprawnień w dostępie do zasobów systemu oraz poprzez przydzielanie poszczególnym użytkownikom prywatnych katalogów (ang. home directory), umieszczonych w pewnym miejscu systemu plików, gdzie danemu użytkownikowi wolno wykonywać wszelkie operacje plikowe; w szczególności zakładać własne podkatalogi. Ponadto realizowana jest ochrona dostępu do plików poprzez rozbudowany mechanizm kontroli trybów ich wykorzystania. Kontrola ta dotyczy wszystkich plików; obejmuje więc zarówno pliki z danymi, jak i z programami. Wiele komercyjnych systemów unixowych stosuje dodatkowe metody ochrony, np. tzw. poziomy bezpieczeństwa, listy uprawnień, okrojone wersje shella itp.

4.3 1. Identyfikatory użytkowników i grup

Użytkownicy, którzy otrzymali zezwolenie na korzystanie z systemu, są opisani w pliku ewidencyjnym użytkowników (/etc/passwd). Ponadto są oni łączeni w grupy opisane w pliku ewidencyjnym grup (/etc/group). Z każdą nazwą użytkownika i grupy użytkowników, opisanych w wymienionych plikach, jest związana unikatowa dodatnia liczba całkowita, pełniąca odpowiednio rolę identyfikatora użytkownika i grupy użytkowników.

Wykonywane przez użytkownika polecenia systemowe i uruchamiane przez niego programy użytkowe powodują uaktywnienie pewnych procesów. Z każdym procesem związane są dwa identyfikatory numeryczne:

identyfikator użytkownika (UID), nazywany również „rzeczywistym identyfikatorem użytkownika", bowiem określa rzeczywistego użytkownika, czyli osobę która otworzyła sesję pracy na terminalu;

identyfikator grupy użytkowników (GID), nazywany również „rzeczywistym identyfikatorem grupy".

Proces shella powołany w celu interpretacji poleceń użytkownika, wprowadzanych z konkretnego terminala, otrzymuje identyfikatory użytkownika, który na tym terminalu otworzył sesję swojej pracy. Proces, utworzony przez shella w celu wykonania wprowadzonego polecenia użytkownika, otrzymuje znowu te identyfikatory od procesu shella. Wywołany proces może wywoływać inne procesy, przekazując im swoje identyfikatory. Utworzony proces otrzymuje swoje identyfikatory rzeczywiste zawsze w spadku od procesu tworzącego.

Z każdym plikiem związane są także dwa identyfikatory: właściciela i grupy. Właściciel jest tutaj rozumiany jako twórca i użytkownik pliku, zaś grupa traktowana jest jako pewna liczba współużytkowników pliku. Plik otrzymuje je od procesu, który go utworzył (mogą to być również identyfikatory nadane przez użytkownika uprzywilejowanego). Identyfikatory użytkownika i grupy, związane z procesem, określają prawa procesu przy dostępie do plików. W stosunku do konkretnego pliku wszystkie procesy dzielą się na trzy kategorie:

procesy właściciela pliku — procesy posiadające identyfikator użytkownika zgodny z identyfikatorem właściciela pliku;

procesy członków grupy właściciela pliku — procesy posiadające identyfikator grupy zgodny z identyfikatorem grupy, do której należy właściciel pliku;

inne procesy nie należące do pierwszych dwóch kategorii. Procesy mogą wykonywać następujące operacje dotyczące pliku:

• czytanie z pliku;

• zapis w pliku;

• wykonanie zapisanego w pliku programu.

W czasie wykonania tych żądań system sprawdza, czy dany proces może uzyskać żądany dostęp do podanego pliku.

4.3.2. Tryb wykorzystania pliku

Przy tworzeniu pliku system przyporządkowuje mu tryb wykorzystania pliku, określający sposób i prawa dostępu do tego pliku. Tryb ten jest zapisany w jednym ze stów i-wezła — podstawowej struktury opisującej plik (rozdziały 4.1.3.6, 4.1.5.1).

Bity słowa trybu wykorzystania pliku są podzielone na dwie części, spełniające odrębne role:

bity 15-9 — kod typu pliku;

bity 8-0 — kod praw dostępu do pliku.

4.3.2.1. Kod praw dostępu do pliku

Kod praw dostępu do pliku zajmuje 9 młodszych bitów słowa „trybu wykorzystania pliku", przy czym bity ustawione na l zezwalają na określony sposób dostępu do pliku. Poniżej przedstawiono odpowiedniość masek cyfrowych ustawiających określone bity i odpowiadających im praw dostępu do pliku dla procesów uruchamianych przez poszczególne kategorie użytkowników (maski podane są ósemkowo):

0x01 graphic

Dla każdej z trzech kategorii użytkowników poszczególne wartości mogą być sumowane, tak więc 000050 oznacza, że członkowie grupy — do której należy właściciel pliku —będą posiadali prawo czytania pliku oraz wykonywania zawartego w nim programu (000040 + 000010).

Jeśli proces wymaga dostępu do pliku, określona zostaje kategoria procesu w stosunku do tego pliku (proces właściciela pliku, grupy lub innych użytkowników). Następnie z kodu praw dostępu do pliku wybierane są te trzy bity, które odpowiadają danej kategorii i sprawdzane jest, czy dla danego procesu wymagany dostęp jest dozwolony. Jeśli dostęp nie jest dozwolony, wywołanie systemowe — przy pomocy którego proces wydał żądanie dostępu — jest odrzucane przez system.

Kod praw dostępu do pliku (jak i kod typu pliku) może być zmieniany poleceniem chmod (opisanym dokładnie w dalszej części podręcznika). Polecenie to dopuszcza zarówno liczbowe, jak i literowe (rwx) oznaczenia trybów dostępu, przy czym znaczenie znaków rwx jest następujące:

r — prawo czytania;

w — prawo pisania;

x — prawo wykonania.

Przykładowo, każde z poniższych dwu wywołań polecenia chmod nadaje wszystkie (rwx), prawa dostępu do pliku dane1 procesom właściciela pliku (u — ang. user) i procesom członków grupy (g — ang. group), a pozostałym użytkownikom systemu (o — ang. other) tylko prawo do czytania tego pliku:

chmod 774 dane1 chmod u+rwx g+rwx o+r dane1

Zera wiodące w trybie wykorzystania pliku wskazanym w poleceniu chmod mogą być pominięte.

4.3.2.2. Kod typu pliku

Kod typu pliku zajmuje 7 starszych bitów słowa „trybu wykorzystania pliku", przy czym odpowiednie bity ustawione na l określają odpowiedni typ pliku. Poniżej przedstawiono odpowiedniość masek cyfrowych ustawiających określone bity i odpowiadających im typów pliku (maski podane są ósemkowo):

0x01 graphic

Bity 8-0 opisują omówione w poprzednim podrozdziale prawa dostępu do pliku. Poszczególnym bitom przyporządkowane są pewne nazwy symboliczne, zdefiniowane w pliku /usr/include/sys/stat.h.

Czterech starszych bitów (15 - 12) mogą używać wyłącznie funkcje systemowe: Stat, fstat oraz mknod, natomiast bity 11-0 mogą być używane przez wszystkie funkcje korzystające z kodu praw dostępu: create, open, chmod, stat, fstat oraz mknod.

Bit 9 (zachowanie segmentu kodu usuniętego procesu) odnosi się do „programów z dzielonym segmentem kodu" (rozdział 4.2.3.6). Jeżeli bit ten jest ustawiony na jeden, to po zakończeniu wykonania takiego programu jego segment kodu jest zachowywany w obszarze wymiatania, co w przypadku często używanych programów redukuje czasy ich ładowania przy ponownych wywołaniach.

4.3.3. Użytkownik uprzywilejowany

Użytkownik uprzywilejowany (ang. superuser) to użytkownik, który ma rzeczywiste i efektywne identyfikatory użytkownika równe 0 (zero). Według konwencji unixowej ma on nazwę root i pełni zwykle rolę administratora systemu.

Niezależnie od kodu ochrony pliku, proces aktywowany przez użytkownika uprzywilejowanego ma prawo dostępu do każdego pliku do czytania i zapisu. Jeśli w kodzie ochrony chociażby jednej kategorii użytkowników pliku było dozwolone wykonanie pliku, to proces uprzywilejowany ma także prawo wykonać program zapisany w tym pliku. W celu uniknięcia możliwych nadużyć uprawnień systemowych i wynikających z tego konsekwencji, użytkownik uprzywilejowany powinien być chroniony hasłem dostępu. W tym przypadku w celu otwarcia sesji pracy z nazwą root należy podać hasło. Hasło to jest określane zazwyczaj w czasie instalacji systemu lub bezpośrednio po niej.

4.3.4. Identyfikatory rzeczywiste i efektywne

Każdy proces (program) aktywowany przez użytkownika systemu Unix posiada swojego właściciela, którym jest użytkownik uruchamiający program lub, w zależności od prawa wykonania programu, właściciel programu. Z tego też względu w Unixie rozróżniany jest właściciel rzeczywisty (użytkownik uruchamiający program) i właściciel efektywny (właściciel programu), zwany niekiedy również właścicielem obowiązującym.

Przy aktywowaniu każdego procesu wywołaniami systemowymi z grupy fork i exec, proces standardowo dziedziczy środowisko procesu wywołującego (za wyjątkiem jego zmiennych lokalnych); zachowuje więc związane z nim identyfikatory użytkownika i grupy. Użytkownik ten jest traktowany jako rzeczywisty właściciel procesu. Rozwiązanie to jest dobre w większości przypadków, jednakże czasami występują problemy dotyczące praw dostępu do plików, używanych przez wywołane procesy. Przykładowo, niektóre programy (polecenie passwd — zmiana hasła użytkownika) są ogólnodostępne dla wszystkich użytkowników systemu, jednakże po ich wywołaniu muszą korzystać z plików o ograniczonych prawach dostępu. Wspomniany program passwd może uruchomić każdy (i zmienić swoje hasło), ale używany przez niego plik ewidencyjny /etc/passwd może być modyfikowany bezpośrednio jedynie przez jego właściciela: użytkownika uprzywilejowanego o nazwie root. Podobny konflikt może zdarzyć się również w wielu innych sytuacjach.

Przedstawiony typ konfliktów system rozwiązuje w ten sposób, że na czas wykonania procesu zmienia efektywny identyfikator użytkownika na identyfikator właściciela programu odpowiadającego procesowi. Jest to zwykle również identyfikator właściciela pliku, z którego proces chce korzystać. Podobna operacja może dotyczyć identyfikatorów grup użytkowników. Taka zmiana jest możliwa, o ile proces jest do tego upoważniony. Decydują o tym bity SUID i SGID (ustawienia identyfikatora użytkownika/grupy; (ang. set user/ group identity) w słowie „trybu wykorzystania pliku" i-wezła pliku przechowującego program odpowiadający procesowi.

Jeśli plik z programem ma ustawiony bit SUID, to podczas wyprowadzania informacji o plikach w formacie pełnym jest on oznaczony nie jako zwykły plik wykonywalny, lecz

jako plik z programem posiadającym prawo do zmiany swojego identyfikatora efektywnego, co sygnalizuje litera s użyta zamiast litery x. W wyprowadzanych przez polecenie Is -l informacjach opis takich plików może wyglądać następująco:

-r-sr-xr-x l root bin 24576 passwd

-r-xr-s--x l bin sys 9675 ps

Aby efektywny identyfikator użytkownika procesu mógł zostać zmieniony na identyfikator właściciela programu, właściciel lub użytkownik uprzywilejowany musi wyrazić na to zgodę, ustawiając w prawach dostępu do pliku tego programu w/w bity. Może on to zrealizować używając w poleceniu chmod symbolu s zamiast x, na przykład:

chmod u+s progi

chmod g+s prog2

Pierwotne (początkowe) identyfikatory związane z procesem są nazywane rzeczywistymi, a otrzymane przez niego po wykonaniu wywołania systemowego exec — efektywnymi (obowiązującymi lub skutecznymi). Tuż po załadowaniu procesu do pamięci jego identyfikatory rzeczywiste i efektywne są zgodne, jednakże po aktywowaniu procesu jego efektywny identyfikator użytkownika (lub grupy), jeżeli zezwalają na to wyżej wymienione bity SUID/SGID, zostaje zamieniony na identyfikator właściciela/grupy programu (tym samym pliku, z którego proces realizujący program chce korzystać).

Identyfikator efektywny służy do określenia praw dostępu, natomiast identyfikator rzeczywisty do rozliczeń oraz w komunikacji z użytkownikami. Inaczej mówiąc pierwszy określa prawa użytkowników, a drugi — identyfikuje użytkownika. Identyfikator efektywny należy do środowiska procesu potomnego, a nie wywołującego. Jego ważność kończy się więc wraz z zakończeniem wykonywania procesu potomnego.

Prawa dostępu procesu sprawdza się według jego identyfikatorów efektywnych. Proces poprzez funkcje systemowe getuid/geteuid oraz getgid/getegid może pobrać związane z nim odpowiednie identyfikatory rzeczywiste/efektywne użytkownika/grupy. Proces może również dynamicznie zmieniać swoje identyfikatory funkcjami setuid i setgid. Ponieważ oznacza to zmianę praw procesu, zmiana identyfikatorów jest dopuszczalna tylko dla procesu uprzywilejowanego i takiego, którego rzeczywisty identyfikator jest zgodny z ustawionym. Zauważmy, że przy zmianie swoich identyfikatorów proces czyni je identycznymi.

Możliwość zmiany efektywnych identyfikatorów procesu jest wygodna dla organizacji niekonwencjonalnego dostępu do danych. Wykorzystując ten mechanizm można tworzyć pliki, na których zezwala się wykonywać tylko określoną grupę operacji, ustaloną przez programy przeznaczone do przetwarzania danych zawartych w tych plikach. W szczególności, przy pomocy takich programów zwykły użytkownik może wykonywać pewne operacje na plikach, które normalnie są dozwolone tylko dla użytkownika uprzywilejowanego lub właściciela tych plików.

4.3.5. Chwilowa zmiana identyfikatora użytkownika

Do chwilowej zmiany swojego identyfikatora na identyfikator innego użytkownika, użytkownik może wykorzystać polecenie SU. Właścicielem pliku programu odpowiadającego poleceniu SU jest użytkownik uprzywilejowany o nazwie root. Jeżeli polecenie to zostanie wywołane bez argumentów, użytkownik może powołać nowy proces shella jako proces uprzywilejowany, jednakże dla zagwarantowania bezpieczeństwa systemu program su prosi

użytkownika o podanie hasła użytkownika uprzywilejowanego. Wywołanie tego polecenia z nazwą innego użytkownika również wymaga podania jego hasła, przy czym wymóg ten nie dotyczy użytkownika uprzywilejowanego.

Poniżej podano przykładowy ciąg poleceń pokazujący działanie polecenia su.

$ who am i Enter adam

$ pwd Enter

/users/adam

$ id Enter

uid=20(adam) gid=102(users)

$ su Enter password: —> należy podać hasło roota i nacisnąć klawisz Enter

# who am i Enter root

# id Enter

uid=0(root) gid=3(sys)

# pwd Enter

/users/adam

# exit Enter -> powrót do swojego środowiska

$ who am i Enter

adam

4.3.6. Kontrola dostępu do plików

Opisywane wcześniej mechanizmy dotyczące identyfikatorów użytkowników i procesów oraz stosowane kody ochrony plików tworzą pewien spójny system uprawnień, określający prawa użytkownika w dostępie do zasobów systemu. System uprawnień określa, czy dany użytkownik może uruchomić dany program, jak również czy ma prawo wykonania odpowiedniej operacji dotyczącej konkretnego pliku. Korzystając, w uproszczeniu, z podanych niżej regół, system ten określa, czy dane prawo jest przyznane użytkownikowi:

• Jeżeli efektywny identyfikator użytkownika jest równy O (identyfikuje użytkownika uprzywilejowanego), to dane prawo jest natychmiast przyznawane;

• Jeżeli efektywny identyfikator użytkownika wykonywanego procesu jest równy identyfikatorowi właściciela danego pliku, to zbiór uprawnień właściciela określa czy można wykonać daną operację;

• Jeżeli efektywny identyfikator grupy procesów jest równy identyfikatorowi grupy współużytkowników pliku, to sprawdzany jest zbiór uprawnień grupy, w przeciwnym przypadku sprawdzany jest zbiór uprawnień pozostałych użytkowników;

• Jeżeli w i-weźle pliku programu jest ustawiony bit „ustanowienia identyfikatora użytkownika", to wywołanie tego programu spowoduje zmianę efektywnego identyfikatora użytkownika na identyfikator właściciela pliku zawierającego ten program. Ponieważ efektywny, a nie rzeczywisty, identyfikator użytkownika określa prawa dostępu, więc możliwe jest w ten sposób przejmowanie uprawnień

4.4. Wymienne shelle

Większość użytkowników systemu Unix kontaktuje się z systemem za pośrednictwem shella, odpowiedzialnego za interpretację poleceń. O tym, który shell zostanie załadowany po otwarciu na terminalu sesji pracy, decyduje administrator przy rejestrowaniu użytkownika w systemie, przy czym użytkownik ma możliwość zmiany shella w czasie pracy.

4.4.1. Przegląd dostępnych shelli

System Unix umożliwia różnym użytkownikom pracę pod kontrolą różnych shelli. Większość wersji Unixa udostępnia ich trzy podstawowe odmiany:

sh, rsh — Shell Bourne'a (sh — standardowy i rsh — okrojony; ang. restricted shell), napisany przez Stephana R. Bourne'a z Beli Labs. Shell rsh jest identyczny jak sh, ale ogranicza pewne prawa użytkownika; w niektórych realizacjach Unixa nazwa rsh identyfikuje shella zdalnego (ang. remote shell);

cshC-shell, w zakresie składni rozszerza możliwości shella Bourne'a o pewne cechy języka C, skąd bierze się jego nazwa. Napisany został przez Billa Joy'a (z Uniwersytetu Kalifornijskiego w Berkeley) i jest obecnie adoptowany dla wszystkich odmian Unixa;

ksh, rksh — Shell Kom'a (ksh — standardowy i rksh — okrojony) — rozszerzona wersja sh, napisana w 1983 r. przez Davida Koma (z Laboratoriów Bella) i udostępniona w 1986 r. Obecnie jest dostępny w większości odmian Unixa.

Wszystkie shelle wywodzą się z jego dwu podstawowych odmian: sh (rsh, ksh, rksh, bash, bdsh, zsh) i csh (tcsh).

W porównaniu z shellem sh, shelle csh i ksh udostępniają dodatkowe możliwości, takie jak: zapamiętywanie poleceń i ich przywoływanie, edycję oraz sterowanie procesami. Shell ksh udostępnia ponadto szereg dodatkowych zmiennych środowiskowych, na przykład pamiętających nazwę bieżącego katalogu, bieżący czas itp.

W poszczególnych odmianach Unixa dostępne są również inne shelle, przykładowo:

bash — ponowiony shell Boume'a (ang. Bourne Again Shell), opracowany przez fundację FSF, udostępniony w 1989 r.; zbliżony do ksh;

scosh — dostępny w SCO UNIX R.3.2v.4.x. który obok możliwości standardowego shella, udostępnia wiele dodatkowych funkcji, wykonywanych z wykorzystaniem mechanizmów okien i menu (w trybie tekstowym);

keysh — dostępny w HP-UX, który obok funkcjonalności zbliżonej do shella ksh udostępnia użytkownikowi w trybie interakcyjnym wyod­rębniony zestaw poleceń związanych z klawiszami funkcyjnymi, wspomagany przez menu wyświetlane w dolnej linii ekranu oraz przez klawisz podpowiedzi. Jest wygodny nie tylko dla początku­jących użytkowników systemu.

Shelle sh, csh i ksh są używane zarówno do pracy interakcyjnej, jak i do interpretacji większej liczby poleceń umieszczonych w plikach wsadowych (ang. batch files), nazywanych również skryptami sheUowymi (ang. shell scripts) lub krótko — skryptami. Wykazują one duże wzajemne podobieństwa, przy czym shell Sh oferuje w zasadzie podzbiór możliwości pozostałych. Shelle sh i csh towarzyszą Unixowi począwszy od momentu jego powstania, natomiast ksh powstał w okresie późniejszym i zdobywa sobie coraz większą, w pełni zasłużoną, popularność.

Domyślnym shellem (sugerowanym przez system w chwili rejestrowania użytkownika) jest shell sh. Przedmiotem pełnego dalszego opisu będzie jedynie ten shell, bowiem prawie wszystkie jego cechy dostępne są we wszystkich pozostałych shellach.

Dla użytkowników przyzwyczajonych do mikrokomputerów IBM PC — wygodnymi (aczkolwiek niestandardowymi) uzupełnieniami systemu mogą być:

dshell — pseudo-shell, przyjmujący pewną liczbę poleceń w standardzie systemu DOS, „przezroczysty" jednocześnie dla poleceń unixowego shella, z poziomu którego został wywołany. Dystrybuowany jest wraz z pakietem oprogramowania ICE.TEN.PLUS; dostępnym dla wielu odmian Unixa;

Mix Commander — odpowiednik popularnego w środowisku DOSa Norton Commandera, zrealizowany w Polsce przez katowicką firmę KaNet; dostępny dla wielu odmian Unixa (podobnym produktem jest pakiet oprogramowania o nazwie Alegro, rozprowadzany przez krakowską firmę ABA).

4.4.2. Systemowe środowisko pracy

Systemowe środowisko pracy użytkownika jest ustalane na podstawie danych pobieranych z plików ewidencyjnych (/etc/passwd i /etc/group) oraz na podstawie wykonania pewnych czynności inicjacyjnych, opisanych we właściwych dla danego shella plikach startowych, umieszczonych w katalogu macierzystym danego użytkownika. Tymi plikami są:

.profile — dla shelli: sh, rsh, ksh, rksh;

.login i .cshrc — dla shella csh.

Jest to środowisko startowe, które może być modyfikowane przez użytkownika po otwarciu na terminalu sesji pracy.

Systemowe środowisko pracy użytkownika jest (za wyjątkiem zmiennych lokalnych) dziedziczone przez procesy; które są aktywowane przez tego użytkownika. Jest ono więc

standardowym środowiskiem tych procesów. Dla każdego użytkownika środowisko systemowe może być inne. W systemie Unix istnieją dwa rodzaje użytkowników:

• zwykli użytkownicy;

• użytkownik uprzywilejowany (administrator systemu o nazwie root).

Każdy nowy użytkownik musi być zarejestrowany w systemie przez administratora. Informacje o zarejestrowanych użytkownikach są umieszczane w dwóch plikach ewidencyjnych:

/etc/passwd — pliku haseł i innych danych inicjacyjnych;

/etc/group — pliku opisu grup użytkowników.

W niektórych odmianach Unixa — szczególnie tych, które stosują dodatkowe środki bezpieczeństwa — mogą być używane dodatkowe pliki ewidencyjne.

W pliku haseł, w chwili rejestrowania użytkownika w systemie, wpisywane są następujące informacje:

• nazwa użytkownika;

• hasło użytkownika, uprawniające go do pracy w systemie, zapamiętywane w postaci szyfrowanej;

• numeryczny identyfikator użytkownika UID (ang. UserIdenfification);

numeryczny identyfikator wiodącej grupy użytkowników, do której on należy GID (ang. Group Identification);

opcjonalnie tekst opisujący użytkownika (nazwisko i imię, numer telefonu kontaktowego);

• nazwa katalogu macierzystego przydzielonego użytkownikowi;

• nazwa programu startowego, uruchamianego w roli shella po otwarciu sesji pracy użytkownika (Login Shell; domyślnie jest nim /bin/sh ale może to być dowolny inny program).

Nazwa i hasło użytkownika są podawane przez użytkownika w chwili otwierania przez niego na terminalu sesji swojej pracy. Nazwa jest zawsze wymagana, natomiast hasło jest wymagane tylko wtedy, jeżeli zostało określone w chwili rejestrowania użytkownika (otwierania jego konta). Użytkownik uprzywilejowany ma nazwę root. Zwykły użytkownik nie może zmienić swojej nazwy, natomiast może zmieniać swoje hasło, przy czym w większości odmian Unixa zmieniane przez niego hasło musi zawierać minimum 6 znaków (krótsze hasła może określać tylko użytkownik uprzywilejowany). Zwykle kontrolowanych jest tylko pierwszych 8 znaków hasła. Hasło może zawierać dowolne 7-bitowe znaki ASCII (o kodach nie większych od 127), przy czym dla zagwarantowania większej trudności w odgadnięciu hasła przez innych użytkowników, w wielu odmianach Unixa powinno ono zawierać minimum 2 litery oraz być różne od nazwy: jakiegokolwiek użytkownika, grupy lub nazwy systemu. Nazwa użytkownika pozwala zidentyfikować w pliku ewidencyjnym opis użytkownika i ustalić jego numer (UID), wykorzystywany zarówno przez shella, jak i niektóre polecenia do kontroli uprawnień użytkownika.

Dany użytkownik może należeć do kilku grup użytkowników, przy czym ta wskazana w pliku /etc/passwd nazywana jest grupa, wiodącą, (ang. primary group}. W pliku opisu grup (/etc/group) dla każdej grupy wpisywane są następujące informacje:

• nazwa grupy (maksymalnie 8 znaków);

• hasło grupy (tylko w niektórych odmianach Unixa);

• numer grupy:

• nazwy użytkowników należących do grupy.

Numeryczny identyfikator użytkownika (UID) służy do identyfikacji użytkownika w systemie, w tym do identyfikacji jego plików i uruchomionych przez niego procesów. Na jego podstawie ustalane są między innymi: nazwa użytkownika i jego uprawnienia, przy czym ma on prawo przerywania wszystkich procesów posiadających ten sam co on identyfikator użytkownika oraz prawo do modyfikacji atrybutów i treści wszystkich plików związanych z tym identyfikatorem. Użytkownik o nazwie root ma identyfikator UID równy O (zero) i posiada najwyższe prawa, w tym prawo modyfikacji treści i atrybutów dowolnych plików, niezależnie od ich praw dostępu, oraz prawo przerywania wykonania dowolnych procesów. Ten sam użytkownik powinien mieć ten sam numer na wszystkich komputerach, jeżeli są one połączone siecią i udostępniają swoje zasoby.

Numeryczny identyfikator grupy (GID) identyfikuje grupę użytkowników. Łączenie użytkowników w grupy pozwala nadawać wszystkim członkom grupy podobne prawa dostępu do wszystkich plików, związanych z tym samym identyfikatorem grupy.

Identyfikatory UID i GID związane z użytkownikiem, który otworzył sesję na danym terminalu, można wyświetlić poleceniem id. System rozróżnia ponadto identyfikator rzeczywisty i identyfikator efektywny (rozdział 4.3.4).

Katalog macierzysty. Po otwarciu przez użytkownika sesji pracy (login:) jego pierwotnym bieżącym katalogiem do pracy staje się wymieniony w pliku /etc/passwd katalog startowy (ang. login directory). który w dalszej części pracy nazywany jest również katalogiem macierzystym (ang. home directory). W katalogu tym są umieszczone odpowiednie dla używanego shella pliki startowe, inicjujące pewne czynności wstępne po otwarciu na terminalu sesji pracy użytkownika. Katalogi macierzyste są standardowo umieszczane w większości odmian Unixa w katalogu /usr (w systemie HP-UX w katalogu /users) i otrzymują domyślnie taką samą nazwę jak nazwa użytkownika, przy czym zarówno w chwili rejestrowania użytkownika, jak i w okresie późniejszym ich nazwy mogą być zmienione na inne. Oto przykładowe nazwy katalogów macierzystych:

/users/basia /users/ewa

Ponieważ w katalogu /usr umieszczonych jest szereg katalogów systemowych, dla zachowania pewnego porządku na dysku wygodnie jest umieszczać katalogi macierzyste użytkowników w wyodrębnionym do tego celu katalogu, na przykład w katalogu /users tak jak w HP-UX.

Nazwa programu startowego (ang. Login-Shell), wpisywana do pliku /etc/passwd, może być nazwą zarówno jednego z dostępnych w systemie shelli (domyślnie /bin/sh), jak i nazwą dowolnego programu użytkowego. Korzystając z tej drugiej możliwości można całkowicie odciążyć użytkownika od potrzeby znajomości systemu operacyjnego, gdyż przydzielając mu w roli shella program użytkowy, program ten zgłosi się bezpośrednio po otwarciu sesji na terminalu (zaś po wyjściu z programu pojawi się znowu login:).

Systemowe środowisko pracy procesów użytkownika (ang. environment) dla wszystkich shelli tworzone jest w podobny sposób. Definiują go przede wszystkim: informacje wpisane w pliku ewidencyjnym /etc/passwd, deskryptory otwartych plików oraz wartości zmiennych predefiniowanych w shellu lub ustalonych przez użytkownika. Liczba i nazwy zmiennych predefiniowanych w poszczególnych shellach mogą być różne.

Do zmiennych, które występują w większości z nich i mają to samo znaczenie, należą między innymi:

HOME — pełna ścieżkowa nazwa katalogu macierzystego;

LOGNAME — nazwa użytkownika;

PATH — lista nazw katalogów, w których będą poszukiwane wywoływane przez użytkownika polecenia i programy;

SHELL — nazwa shella startowego (/bin/sh, /bin/ksh, /bin/csh,...);

TERM — nazwa trybu pracy terminala;

TZ — symbol strefy czasowej;

PS l — tekst „symbolu gotowości" do przyjmowania poleceń:

sh,ksh

csh

Rodzaj użytkownika

#

#

użytkownik uprzywilejowany

$

%

użytkownik zwykły

Większość tych zmiennych jest zdefiniowana w pliku startowym danego użytkownika. Pełny opis wszystkich zmiennych środowiskowych dla shella sh zamieszczono w kolejnym rozdziale.

Inicjowanie systemowego środowiska pracy użytkownika jest realizowane w drodze odczytu i analizy informacji zawartych w plikach ewidencyjnych, w pliku /etę/profile oraz w drodze wykonania pewnych czynności opisanych w plikach startowych danego użytkownika (umieszczonych w jego katalogu macierzystym):

.profile — dla shelli sh, ksh;

.login i .cshrc — dla shella csh.

Pliki startowe zawierają pewne polecenia inicjujące otwarcie sesji pracy oraz definicje zmiennych globalnych (środowiskowych), dostępnych zarówno na poziomie interakcji z systemem, jak i dla wywoływanych poleceń i programów. Do zmiennych tych należą między innymi wcześniej wymienione: SHELL, PATH, TERM.

Zmienne definiowane w czasie dalszej interakcji z systemem lub w skryptach shellowych są standardowo traktowane jako zmienne lokalne, jednakże przy pomocy polecenia export (setenv — dla shella csh) mogą być uczynione zmiennymi globalnymi, dostępnymi dla wykonywanych poleceń i ich procesów potomnych.

Wspomniane pliki startowe spełniają podobną rolę co pliki autoexec.bat i config.sys w systemie DOS, jednakże z tą różnicą, że dla każdego użytkownika może on być inny. Użytkownicy (o ile nie pracują pod kontrolą shelli okrojonych) mają możliwość modyfikowania treści własnych plików startowych, a tym samym kształtowania swojego środowiska pracy.

Do standardowego systemowego środowiska pracy należą również deskryptory trzech już otwartych plików standardowego wejścia/wyjścia (rozdział 4.2.3.7).

Shelle sh, ksh i csh pozwalają wiązać standardowe wejście/wyjście z plikami (za pomocą symboli: <, <<, >,...), co dokładnie zostało opisane w rozdziale poświęconym shellowi sh (rozdział 5.8).

Opisane powyżej środowisko jest również systemowym środowiskiem pracy procesów aktywowanych przez użytkownika.

Z punktu widzenia użytkownika systemu środowisko pracy to również dostępny dla niego zestaw poleceń wbudowanych w shella jak i wstaw poleceń zewnętrznych funkcjonujących na zasadzie samodzielnych programów. Zestaw dostępnych poleceń zewnętrznych jest ustalany zarówno przez listę przeszukiwanych katalogów (wymienioną w wartości zmiennej PATH), jak i przez prawa dostępu do tych poleceń, przysługujących danemu użytkownikowi. Największe ograniczenia stwarzają pod tym względem shelle okrojone (rsh i rksh), co jest opisane w jednym z następnych podrozdziałów.

Możliwości zmian środowiska systemowego są dla użytkowników dość duże, o ile nie pracują oni pod kontrolą shella okrojonego. Pozostałe shelle pozwalają wykonywać następujące modyfikacje:

• modyfikować plik startowy, określający czynności inicjacyjne;

• zmieniać aktualny katalog roboczy (poleceniem cd);

• zmieniać hasło dostępu do systemu (poleceniem passwd):

• wybierać shella (przez polecenia: sh, ksh, csh,...);

• zmieniać wartości zmiennych globalnych (PATH, PSI, LANG.... — ich wartości można wyświetlić poleceniem printenv lub env);

• zmieniać dla wskazanych swoich plików:

• prawa dostępu (poleceniem chmod);

• nazwę właściciela (poleceniem chown);

• nazwę grupy (poleceniem chgrp):

• ustalać domyślne prawa dostępu do wszystkich swoich nowo utworzonych plików (poleceniem umask):

• zmieniać czasowo swój identyfikator użytkownika (poleceniem su) lub identyfikator grupy (poleceniem newgrp);

• zabraniać lub zezwalać innym użytkownikom na przesyłanie komunikatów na własny terminal (poleceniem: mesg);

• modyfikować dostępny zestaw poleceń poprzez:

• tworzenie własnych synonimów ich nazw (polecenie: In, a w shellach csh i ksh również polecenie: alias);

• tworzenie ich kopii ze zmienionymi nazwami (polecenia: cp, mv);

• pisanie własnych skryptów shellowych;

• ustawiać parametry pracy terminala (SetUp) i wskazywać go poprzez wartość zmiennej TERM;

• ustalać sposób obsługi terminala (poleceniami: stty, setcolor, clear, telinit. tic, tput, untic/infocmp, w SCO UNIX ponadto: mapchan i mapkey).

4.4.3. Shelle okrojone rsh i rksh

Dla standardowych shelli sh i ksh w wielu odmianach Unixa (HP-UX i SCO UNIX) istnieją ich wersje okrojone (ang. restricted shells) o nazwach rsh i rksh. Mają one takie same możliwości jak ich pełne wersje, za wyjątkiem tego, że ustalają dla użytkownika poniżej opisane ograniczenia:

• plik startowy użytkownika ($HOME/.profile) nie może być przez niego modyfikowany;

użytkownik nie może modyfikować wartości zmiennej PATH;

• użytkownik ma dostęp tylko do programów (i poleceń) umieszczonych w swoim katalogu macierzystym oraz do umieszczonych w katalogach wskazanych w wartości zmiennej PATH.

Typową praktyką administratorów systemu jest tworzenie katalogu /usr/rbin i umieszczanie w nim tylko pewnej liczby poleceń, niezbędnych użytkownikom pracującym pod kontrolą shella okrojonego. Nie muszą to być fizyczne kopie plików poleceń, lecz dowiązane do nich nazwy.

• roboczym katalogiem aktualnym użytkownika może być tylko jego katalog macierzysty.

• niedozwolone jest tworzenie lub zapisywanie plików w wyniku przeniesienia standardowego wyjścia za pomocą symboli: >, >>.

Ograniczenia te oraz możliwość odpowiedniego ustawienia praw dostępu dają administratorowi systemu możliwość określania kolejnych ograniczeń. Opisywane okrojone shelle są więc często przypisywane początkującym użytkownikom (w celu ochrony systemu przed skutkami braku ich doświadczenia) oraz tym użytkownikom, którzy nie powinni mieć prawa korzystania z pewnych zasobów systemu.

Nazwy shelli okrojonych (rsh i rksh) są w rzeczywistości nazwami dowiązanymi do ich wersji pełnych (odpowiednio plików /bin/sh i /bin/ksh), a o tym, czy shell pracuje w wersji pełnej czy z restrykcjami, decyduje nazwa podana w jego wywołaniu.

4.4.4. Porównanie shelli

Dostępne shelle wykazują duże podobieństwa. Różnią się głównie zakresem oferowanych możliwości i udogodnień. W tabelach 4.4 i 4.5 wymieniono ich podstawowe cechy.

W niniejszym podręczniku opisywany jest shell sh ponieważ:

• jest najprostszym shellem spośród wymienianych;

• występuje we wszystkich odmianach systemu Unix;

• pod względem składni swoich poleceń wewnętrznych jest prawie w 100%, a pod względem ogólnych możliwości w około 90% — podzbiorem wszystkich pozostałych shelli, z których rozszerzeniami w razie potrzeby będzie łatwo użytkownikowi się zapoznać;

• istotne różnice występują tylko w stosunku do shella csh, w którym symbolem gotowości dla roota jest % (zamiast #) oraz definiowanie zmiennych środowiskowych poleceniem setenv (a nie export).

Tabela 4.4. Ogólne cechy wybranych shelli

Cecha shella

Opis

sh

ksh keysh

csh

Historia poleceń

Możliwość przeglądania wcześniej wprowadzonych poleceń, ich modyfikacji i powtórnego wprowadzania

nie

tak

tak

Pełna edycja wiersza poleceń

Możliwość edycji bieżącego lub poprzednio używanego polecenia

ograniczona

tak

tak

Synonimy poleceń (alias)

Możliwość definiowania własnych synonimów poleceń i posługiwania się nimi

nie

tak

tak

Wersja okrojona

Możliwość przypisania użytkownikowi okrojonej wersji shella, ograniczającej jego uprawnienia

tak

tak

nie

Sterowanie procesami

Zarządzanie procesami (Job control)

nie

tak

tak

Wybrane dodatkowe możliwości shella csh, w porównaniu z sh

Mechanizm historii poleceń umożliwia wyświetlanie poleceń wcześniej wprowadzonych i powtórne wykonanie wskazanego polecenia (z ograniczoną możliwością jego edycji). Mechanizm ten bazuje na poleceniu history, które wyświetla listę kilkunastu ostatnio wprowadzonych i ponumerowanych poleceń. Powtórne wykonanie polecenia z tej listy odbywa się poprzez zapis

!nr_polecenia

• Dodatkowe polecenie alias pozwala definiować dodatkowe polecenia, na przykład

alias Im 'Is -l | more'

(w sh może on być zastąpiony przez odpowiednie wykorzystanie funkcji shella, zdefiniowanej przez użytkownika).

• Wyrażenia mogą być obliczane przez zapis @y=wyrażenie.

• Drobna różnica występuje w zakresie definiowania zmiennych. Zmienne lokalne są ustawiane w csh poleceniem set natomiast zmienne globalne (środowiskowe) — poleceniem setenv.

Wybrane dodatkowe możliwości shella ksh, w porównaniu z. sh

Istnieje szereg użytecznych dodatkowych zmiennych środowiskowych, na przyk­ład: EDITOR, ERRNO, LINENO, PPID, PWD, SECONDS, TMOUT, VISUAL.

• Istnieje możliwość łatwego definiowania dynamicznego symbolu gotowości, na przykład pokazującego bieżący katalog:

PS1='$PWD> '

• Istnieje możliwość uzyskania obsługi historii poleceń i pełnej edycji wybranego ' polecenia edytorem emacs lub edytorem vi, po wprowadzeniu jednego z poleceń:

EDITOR=vi VlSUAL=vi

Po ustawieniu jednej z tych zmiennych, w jednowierszowym (niewidocznym) oknie, dostępnym w wierszu w którym wyświetlany jest symbol gotowości, można przeglądać plik z historią wcześniej wprowadzonych poleceń ($HOME/.sh_history). O ile wskazano edytor vi (i nie znajdujemy się w trybie wstawiania znaków) klawisze k i j powodują wyświetlenie polecenia poprzedniego i następnego względem aktualnie wyświetlanego polecenia, natomiast klawisze h i l pozwalają przemieszczać kursor po tekście wyświetlanego polecenia. Dostępne są wszystkie klawisze edycyjne wskazanego edytora. Po korekcie polecenia i naciśnięciu klawisza Return polecenie jest oddawane do wykonania. Jeżeli chcemy wykonać edycję aktualnie wprowadzanego polecenia - należy wejść w tryb komend edytora vi (nacisnąć klawisz Esc).

• Istnieje możliwość automatycznego zamykania nieaktywnych sesji pracy. W tym celu poprzez wartość zmiennej środowiskowej TMOUT, umieszczonej w pliku $HOME/.profile, należy wskazać czas w sekundach, po którym nastąpi zamknięcie sesji pracy („wylogowanie") o ile w podanym czasie — po wyświetleniu ostatniego symbolu gotowości — nie zostało wprowadzone żadne polecenie. Przykładowo pokazane poniżej wiersze, wstawione do pliku .profile, spowodują zamkniecie sesji pracy po 15 minutach nieaktywności użytkownika (o ile nie uruchomił jakiegoś programu):

MIN=15; SEK='expr $MIN * 60'

echo "Ustawiono zamknięcie sesji po $MIN minutach nieaktywności" TMOUT=$SEK; export TMOUT

• Istnieją dodatkowe konstrukcje sterujące i polecenia wewnętrzne, na przykład:

alias, fc, function, jobs, let, select, print. Oto przykład wykorzystania polecenia alias, definiującego nowe polecenia:

alias type='cat' alias dir='ls -la' alias del='rm -i'

Pliki konfiguracyjne shelli

Wszystkie pliki konfiguracyjne danego shella powinny być umieszczone w katalogu macierzystym użytkownika.

Tabela 4.5. Pliki konfiguracyjne poszczególnych shelli

Nazwa shella

Program

Systemowy plik startowy

Lokalny plik startowy

Lokalny plik kończący sesję

Shell Bouerne'a

/bin/sh

/etc/profile

$HOME/.profile

Shell Korn'a

/bin/ksh

/etc/pronie

$HOME/.profile

$ENV

keysh

/usr/bin/keysh

/etc/profile

$HOME/.profile

$ENV

csh

/bin/csh

/etc/csh.login /etc/.cshrc

$HOME/.login $HOME/.cshrc

$HOME/.logout

Uwagi:

Shell keysh jest dostępny tylko w systemie HP-UX.

• Nazwy bazowe okrojonych wersji shelli są poprzedzone literą r:

/bin/rsh

/bin/rksh

/usr/bin/rkeysh

• W niektórych systemach (np. HP-UX) wzorcowe pliki startowe i kończące sesje pracy dla noworejestrowanych użytkowników są zlokalizowane w katalogu /etc i mają nazwy:

/etc/d.profile

/etc/d.login

/etc/d.cshrc

/etc/d.logout

• Katalogiem $HOME dla użytkownika o nazwie root jest katalog główny.



Wyszukiwarka

Podobne podstrony:
Rozdział 1, Moja twórczość, Wybrana
Rozdział 1, Moja twórczość, Wybrana
Rozdział 3, Moja twórczość, Wybrana
Rozdział 1, Moja twórczość, Wybrana
Rozdział 4, Moja twórczość, Wybrana
Prolog + Rozdział 1, Moja twórczość, Wybrana
Rozdział 3, Moja twórczość, Wybrana
Dwa nurty polskiego baroku na przykladzie tworczosci wybranych autorow, WYPRACOWANIA, ZADANIA
Wybranka Ognia - rozdział 5, &Krąg&, 01. Wybranka Ognia, WERSJA DOC
Wybranka Ognia - rozdziały 0-3, &Krąg&, 01. Wybranka Ognia, WERSJA DOC
Wybranka Ognia - rozdział 4, &Krąg&, 01. Wybranka Ognia, WERSJA DOC
Mrok. 1-14, Moja twórczosc, Mrok
Eternity begins now. 1-5, Moja twórczosc, Eternity begins now
Rozdział I moja ksiazka bez tytułu
Scharakteryzuj, odwołując się do twórczości wybranych przedstawicieli jedną z grup poetyckich dw2
Charakterystyka z odwołaniem do twórczości wybranych przedstawicieli, jednej z grup poetyckich dw
ROZDZIAŁ X.1 WYBRANE OBIEKTY POŁUDNIOWEGO SKUPISKA O NIEUSTALONEJ CHRONOLOGII, MAGAZYN DO 2015, Nowe
ROZDZIAŁ X.2 WYBRANE OBIEKTY O NIEUSTALONEJ CHRONOLOGII, MAGAZYN DO 2015, Nowe Grocholice - wersje m
Przemiany don Juana wybrane rozdziały

więcej podobnych podstron