ask4, Edukacja, studia, Semestr IV, Architektura Systemów Komputerowych, Wyklad


POLITECHNIKA GDAŃSKA

WYDZIAŁ ELEKTRONIKI, TELEKOMUNIKACJI i INFORMATYKI

KATEDRA ARCHITEKTURY SYSTEMÓW KOMPUTEROWYCH

0x01 graphic

Architektury systemów komputerowych

Materiały pomocnicze do wykładu — cz. III

Opracował dr inż. Andrzej Jędruch

Gdańsk 2004

Wprowadzenie do systemów operacyjnych

system operacyjny jest programem, który nadzoruje i koordynuje posługiwanie się sprzętem przez różne programy użytkowe — te zaś pracują na zlecenie różnych użytkowników;

do programów użytkowych (nazywanych też aplikacjami) zalicza się kompilatory, systemy baz danych, programy handlowe, gry komputerowe i wiele innych;

użytkownikami mogą być ludzie, jak też inne komputery czy maszyny;

rozwiązanie postawionego zadania w systemie komputerowym wymaga użycia różnych elementów sprzętu i oprogramowania, które określa się terminem zasoby; do zasobów zalicza się czas procesora, obszar w pamięci operacyjnej, obszar w pamięci plików, urządzenia wejścia-wyjścia itd.;

system operacyjny zarządza zasobami i przydziela je poszczególnym programom i użytkownikom wówczas, gdy są one nieodzowne do wykonywania zadań; niektóre zasoby (np. pamięć operacyjna) są często zbyt małe w stosunku do potrzeb, więc system operacyjny musi decydować o przydziale zasobów poszczególnym zamawiającym, mając na względzie wydajne i harmonijne działanie całego systemu komputerowego;

można przyjąć, że w ścisłym znaczeniu system operacyjny jest programem, który działa w komputerze nieustannie; taki program nazywany jest także jądrem systemu operacyjnego; niekiedy do systemów operacyjnych zalicza się edytory, kompilatory, debuggery, itd., chociaż częściej traktowane są one jako programy użytkowe.

Rozwój systemów operacyjnych

pierwsze programy były całkowicie samodzielne, w trakcie realizacji nie korzystały z pomocy innych programów, w szczególności samodzielnie obsługiwały urządzenia wejścia-wyjścia;

wkrótce okazało się, że prawie wszystkie programy realizują podobne lub identyczne czynności w zakresie obsługi urządzeń wejścia-wyjścia; jednocześnie urządzenia te wymagały starannego oprogramowania, co wymagało znajomości wielu szczegółów technicznych; często realizacja prostej czynności przez urządzenie wymagała wykonania złożonego ciągu operacji;

w tej sytuacji zaczęto tworzyć odrębne podprogramy obsługi dla każdego urządzenia, udostępniane innym programom — podprogramy te wykonywały (często w złożony sposób) proste zlecenia otrzymywane od zwykłych programów;

bardzo wysokie koszty instalacji i eksploatacji systemów komputerowych w latach pięćdziesiątych i sześćdziesiątych spowodowały konieczność wprowadzenia oprogramowania nadzorującego i koordynującego pracę całego systemu w celu uzyskania jego maksymalnej wydajności — oprogramowanie to przyjęto nazywać systemem operacyjnym;

w różnych systemach komputerowych zdefiniowano szereg usług, które mogły być zlecane przez zwykłe programy do podprogramów systemowych; w rezultacie powstało środowisko, w którym obok elementarnych operacji wykonywanych przez procesor, programista tworzący programy użytkowe miał do dyspozycji szereg operacji mających charakter usług realizowanych przez system operacyjny;

w ten sposób ukształtował się pewien interfejs obejmujący zasady korzystania z usług systemu operacyjnego przez programy, znany jako interfejs programowania aplikacji (ang. API — Application Programming Interface); usługi API udostępniono także w wielu językach wysokiego poziomu w postaci funkcji lub procedur;

systemy operacyjne i architektura komputerów wywarły na siebie wzajemnie znaczny wpływ — aby ułatwić posługiwanie się sprzętem, zaczęto rozwijać systemy operacyjne; z kolei zauważono, że wprowadzenie zmian w sprzęcie pozwala na uproszczenie systemów operacyjnych.

Systemy wielozadaniowe

we współczesnych systemach operacyjnych dla komputerów PC najważniejszym celem działania systemu jest wygoda użytkownika, a efektywne działanie systemu jest celem drugorzędnym; wygoda i wydajność są niekiedy sprzeczne; w przeszłości szczególną uwagę zwracano na wydajność; w komputerach osobistych dominuje wygoda użytkowania;

Systemy czasu rzeczywistego

systemy czasu rzeczywistego (ang. real-time) stanowią specjalizowany rodzaj systemów operacyjnych, w których występują surowe wymagania na czas wykonania operacji lub przepływu danych;

systemy czasu rzeczywistego stosowane są do komputerowego sterowania procesami przemysłowymi, ruchem pojazdów, w urządzeniach medycznych, wojskowych i wielu innych;

w systemach czasu rzeczywistego przetwarzanie danych musi (!) zakończyć się przed upływem określonego czasu, w przeciwnym razie system nie spełnia wymagań;

istnieją dwie odmiany systemów czasu rzeczywistego

1. rygorystyczny system czasu rzeczywistego (ang. hard real time system) gwarantuje terminowe wypełnianie krytycznych zadań;

2. łagodny system czasu rzeczywistego (ang. soft real-time system) jest mniej wymagający: krytyczne zadanie do obsługi w czasie rzeczywistym otrzymuje pierwszeństwo przed innymi zadaniami, przy czym pierwszeństwo zostaje zachowane aż do chwili wykonania zadania;

systemy rygorystyczne, ze względu na ostre wymagania czasowe i konieczność ograniczenia wszystkich opóźnień w systemie, nie mają większości cech nowoczesnych systemów operacyjnych, a ponadto cechują je liczne ograniczenia, np. pamięć pomocnicza jest bardzo mała lub nie występuje wcale, nie używa się także pamięci wirtualnej;

żaden z istniejących, uniwersalnych systemów operacyjnych nie umożliwia działania w czasie rzeczywistym w sensie rygorystycznym, jedynie system Windows NT/2000 w pewnym stopniu może być brany pod uwagę;

systemy łagodne znajdują zastosowanie w mniej odpowiedzialnych rolach, np. w technice multimedialnej; większość uniwersalnych systemów operacyjnych spełnia wymagania systemów łagodnych.

wprawdzie wczesne wersje systemu Windows nigdy nie pretendowały do klasy systemów czasu rzeczywistego, ale wskutek ich powolnego działania nie zostały zaakceptowane przez użytkowników; późniejsze wersje Windows korzystały już ze znacznie szybszych procesorów i większej pamięci, ale niektóre przyjęte rozwiązania miały charakter uproszczony, dostosowany do aktualnych możliwości sprzętu.


Warstwowa struktura systemu operacyjnego

0x01 graphic

najniższą warstwę SO stanowią programy niskiego poziomu obsługi sprzętu, często wbudowane do pamięci stałej (BIOS);

kolejną warstwę stanowi jądro systemu (ang. kernel), składające się ze zbioru funkcji i tablic systemowych; głównym zadaniem jądra jest dostarczenie narzędzi do zarządzania procesami, zarządzania pamięcią i zarządzania systemem plików;

kolejna warstwa dostarcza programistom narzędzi do odwoływania się do systemu operacyjnego z wnętrza programów, a więc tzw. interfejs programów użytkowych (API ang. Application Program Interface); korzystają z niego również programy systemowe, jak np. programy powłok;

pomiędzy warstwą API i użytkownikiem ulokowano w modelu warstwowym ww. programy powłok, programy poleceń systemowych i programy użytkowe jako narzędzia bezpośrednio wykorzystywane przez użytkownika; oprogramowanie użytkowe (narzędziowe) to różnego rodzaju edytory tekstowe, translatory (kompilatory) języków programowania, programy łączące (konsolidatory, linkery), programy uruchomieniowe, programy wspomagające lub wręcz tworzące interfejs użytkownika itp.; na ogół zakłada się, że każda warstwa może komunikować się tylko z warstwami sąsiednimi (tzn. korzystać z usług warstwy niższej i udostępniać usługi warstwie wyższej); jednak to założenie jest bardzo trudne w praktycznej realizacji i często stosuje się jedynie podejście przybliżone;

z modelem warstwowym związana jest koncepcja hierarchii maszyn wirtualnych, tworzących abstrakcyjny ciąg modeli systemów komputerowych; każda kolejna warstwa wzbogaca sprzęt o nowe własności, dając ciekawe implikacje w przypadku systemów wielozadaniowych (różne maszyny dla różnych zadań);

System interpretacji poleceń

interpreter poleceń stanowi interfejs między użytkownikiem a systemem operacyjnym; jest to jeden z najważniejszych programów w systemie operacyjnym;

w wielu systemach operacyjnych (np. DOS, Unix) interpreter poleceń jest specjalnym programem, wykonywanym przy rozpoczynaniu zadania lub wtedy, gdy użytkownik rejestruje się w systemie; inne systemy operacyjne zawierają interpreter poleceń w swoim jądrze;

interpreter poleceń znany jest pod nazwą powłoki (ang. shell);

interfejs graficzny (np. MS Windows, Macintosh) jest zwykle bardziej przyjazny dla użytkownika;

za pomocą poleceń przekazywanych do interpretera można:

tworzyć procesy i zarządzać nimi;

obsługiwać wejście-wyjście;

administrować pamięcią pomocniczą i operacyjną;

realizować operacje na plikach i katalogach;

realizować pracę sieciową;

użytkownik porozumiewa się z systemem za pomocą interpretera zleceń, analogicznie programy porozumiewają się z systemem operacyjnym za pomocą funkcji systemowych.

Procesy

realizacja wielozadaniowości wymaga dzielenia czasu procesora między zadania poszczególnych użytkowników: system operacyjny udostępnia procesor wybranemu zadaniu na krótki odcinek czasu (np. 200 ms), powodując wykonywanie zadania przez ten czas; po upływie tego czasu wykonywane zadanie zostaje zatrzymane, a system operacyjny przekazuje procesor innemu zadaniu, również na ustalony, krótki odcinek czasu;

sam program jest obiektem pasywnym i nie jest procesem, natomiast proces jest obiektem aktywnym, który ma przydzielone pewne zasoby i dla którego można wskazać następny rozkaz do wykonania poprzez określenie zawartości wskaźnika instrukcji (licznika rozkazów) procesora; wykonywany proces może tworzyć wiele nowych procesów;

głównym obowiązkiem systemu operacyjnego jest wykonywanie programów użytkowych; system operacyjny musi jednak realizować także różne zadania systemowe (które wygodniej jest pozostawić poza jądrem systemu); można więc przyjąć, że system składa się zbioru procesów: procesy systemu operacyjnego wykonują kod systemowy, a procesy użytkowe działają wg kodu należącego do użytkowników;

ponieważ istnieje możliwość podziału mocy obliczeniowej procesora (lub procesorów), więc wszystkie te procesy mogą być wykonywane współbieżnie;

proces może znajdować się w jednym z następujących stanów:

nowy — proces został utworzony,

aktywny — są wykonywane instrukcje,

oczekiwanie — proces czeka na wystąpienie jakiegoś zdarzenia (np. na zakończenie operacji wejścia-wyjścia),

gotowy — proces czeka na przydział procesora,

zakończony — proces zakończył działanie.

wymienione stany oznaczane są czasami trochę innymi terminami;

w każdej chwili w dowolnym procesorze tylko jeden proces może być aktywny, przy czym może być wiele procesów gotowych lub czekających;

0x01 graphic

1) wielozadaniowość z wywłaszczaniem (ang. preemptive multitasking) — w klasycznych wielozadaniowych systemach operacyjnych, przewidzianych dla wielu użytkowników, każdy proces wykonywany jest przez ustalony odcinek czasu (np. 20 ms) — po upływie tego czasu, co jest sygnalizowane przerwaniem zegarowym, realizowany proces zostaje zatrzymany (zawieszony), po czym wznawiany jest inny proces; niekiedy procesowi może być przydzielony odcinek czasu będący wielokrotnością jednostki podstawowej;

2) wielozadaniowość bez wywłaszczania (ang. non-preemptive multitasking, cooperative multitasking) — przełączanie realizowane jest za wiedzą programu, za pomocą funkcji systemowej wywołanej przez ten program — zatem proces jest realizowany aż do chwili, gdy dobrowolnie przekaże sterowanie do systemu, który z kolei wznowi inny proces.

w starszych wersjach systemu Windows dominowała metoda wielozadaniowości bez wywłaszczania, aczkolwiek w odniesieniu do zadań DOSowych stosowana była zawsze wielozadaniowość z wywłaszczaniem — zadania DOSowe są bowiem realizowane wg koncepcji proceduralnych i nie są przygotowane do tymczasowego oddawania sterowania do systemu;

w systemie sterowanym zdarzeniami bardziej naturalna jest wielozadaniowość bez wywłaszczania; jednak ten rodzaj wielozadaniowości może być akceptowany pod warunkiem, że czas obsługi komunikatów jest krótki (np. 100 ms); w przypadku stosowania wielozadaniowości bez wywłaszczania pojedynczy program może "zamrozić" cały system, jeśli przetwarzanie komunikatu trwa nadmiernie długo;

Wskaźnik

Stan procesu

Nr procesu

Wskaźnik instrukcji

Rejestry

Ograniczenia pamięci

Wykaz otwartych plików

.

.

.

Struktura pamięci dla procesu

obszar pamięci przeznaczonej dla procesu jest zazwyczaj zorganizowany w sposób pokazany na rysunku;

kod programu umieszczony jest w obszarze pamięci nazywanym często tekstem (procesu) — w wielu systemach operacyjnych część ta jest zastrzeżona tylko do czytania (wykonywany program nie może modyfikować swoich rozkazów);

dane programu mogą składać się z trzech części:

0x01 graphic

sterta jest to część pamięci programu używana do dynamicznego przydzielania dodatkowego obszaru pamięci na dane podczas wykonywania programu;

stos jest to część pamięci programu używana dynamicznie podczas wykonywania programu do przechowywania zmiennych automatycznych i ramek stosu (zawierających ślad i parametry wywołania funkcji);

wiele systemów operacyjnych pozostawia wolne miejsce między stertą i stosem, aby ich wielkości mogły się zmieniać dynamicznie podczas wykonywania programu.

Programy wielomodułowe

0x01 graphic

cl zbadaj.c

powoduje bezpośrednio po kompilacji automatyczne wywołanie konsolidatora link; i wytworzenie pliku wykonywalnego .EXE; jeśli zamierzamy przeprowadzić wyłącznie kompilację, to trzeba zastosować dodatkową opcję -c

cl -c zbadaj.c

Uwaga: w przypadku używania zewnętrznego kompilatora i linkera Microsoft Visual Studio, trzeba wcześniej z poziomu bieżącego katalogu uruchomić plik wsadowy VCVARS32.BAT.

Pliki linkowalne i wykonywalne

Ładowanie programów

0x01 graphic

Interfejs programowania aplikacji (API)

interfejs programowania aplikacji, (używany jest także termin: interfejs programu użytkownika), nazywany w skrócie API (ang. application program interface), obejmuje zbiór struktur danych i funkcji, a niekiedy stanowi listę komunikatów; API stanowi ustaloną konwencję wywoływania, za pomocą której program użytkowy (aplikacja) może uzyskać dostęp do usług systemu operacyjnego lub usług udostępnianych przez inne moduły oprogramowania, które zwykle implementowane są jako biblioteki;

niekiedy skrót API jest tłumaczony jako "interfejs programowania aplikacji";

API definiowane jest na poziomie kodu źródłowego i stanowi pewien poziom abstrakcji między aplikacją a jądrem systemu operacyjnego (lub innego programu usługowego), co z kolei tworzy potencjalne możliwości przenośności kodu;

rozmaite pakiety API używane są w prawie każdym systemie komputerowym; do najbardziej popularnych API dla komputerów osobistych należy opracowany przez firmę Microsoft pakiet Win32 API, zawierający opisy funkcji używanych w systemie Windows; do połowy lat dziewięćdziesiątych w oprogramowaniu komputerów osobistych szeroko stosowano zestaw funkcji DOS API, który definiowany jest na poziomie asemblera;

zestaw funkcji wykonujących operacje na plikach w Unixie (i w DOSie): open, close, lseek, read , . . . stanowi API dla operacji plikowych UNIXa — dla każdej z tych funkcji podawany jest opis funkcjonalny wraz ze szczegółowymi informacjami odnośnie przekazywania parametrów do funkcji i interpretacji wartości zwracanych przez te funkcje;

opisy zawarte w API nie zawierają na ogół jakichkolwiek danych o sposobie wykonywania funkcji przez system operacyjny, czyli ukrywają wewnętrzne działania systemu — jest to określane jako pewien poziom abstrakcji, rozumianej jako ukrycie szczegółów implementacyjnych;

pominięcie szczegółów implementacyjnych zazwyczaj ułatwia zrozumienie funkcji, co stanowi jeden z czynników przyśpieszających budowę systemu oprogramowania; koncepcja ta koresponduje z zasadami hermetyzacji w językach obiektowych;

API nie precyzuje sposobu realizacji funkcji, więc ta sama funkcja może być zrealizowana w różnych środowiskach za pomocą różnych technik; co więcej, jeśli zostanie opracowana ulepszona wersja jakiejś usługi systemowej, to producent systemu udostępnia tę wersję zachowując dotychczasowe API, co oznacza, że nie są potrzebne jakiekolwiek zmiany w programach użytkownika;

jeśli poszczególne realizacje są zgodne ze specyfikacją, to nic nie stoi na przeszkodzie by program korzystający z pewnego zestawu funkcji API mógł być wykonywany w innych środowiskach — przenośność kodu jest od dawna postulowana w technice programowania;

stosowanie rozwiązań niestandardowych (np. wstawek asemblerowych) ogranicza przenośność kodu;

interfejs API musi być poprawny, spójny, podatny na dalsze rozszerzenia i dobrze udokumentowany; API powinien być przyjazny dla użytkownika, zwłaszcza gdy przewidywane jest jego stosowanie przez programistów nie znających dokładnie danej problematyki; API powinien być w miarę możliwości niezależny od platformy;

wywoływanie niektórych funkcji API wymaga podawania znacznej liczby parametrów, z których większość nie koresponduje z rozwiązywanym problemem i powtarza się przy kolejnych wywołaniach; w takim przypadku oferowane są programy wspomagające określane terminem wrapper (= obwoluta, opaska); w szerszym znaczeniu terminem tym określa się programy, podprogramy lub makra, które ułatwiają wywoływanie innych programów lub funkcji API;

przykładowo, gdy wywoływany program wymaga podania w linii zlecenia dużej liczby parametrów (często o standardowych wartościach), wrapper udostępnia ten program żądając podania tylko kilku istotnych parametrów, a pozostałym parametrom przypisuje wartości standardowe.

Struktura warstwowa API

często kolejne warstwy API budowane na podstawie API już istniejących — poniższy rysunek ilustruje to dla systemu Windows;

warstwa HAL (ang. hardware abstraction layer) w systemie Windows stanowi ogniwo pośrednie między sprzętem a jądrem systemu operacyjnego; HAL jest w istocie sterownikiem urządzenia dla płyty głównej; każda platforma sprzętowa ma swoją własną wersję HAL, np. wersja HAL dla płyty dwuprocesorowej różni od wersji dla płyty jednoprocesorowej.

0x01 graphic

w wielu przypadkach programy mogą posługiwać się zarówno funkcjami udostępnianymi przez przez system operacyjny, jak też odpowiednimi (zwykle wygodniejszymi) funkcjami bibliotecznymi — tego rodzaju funkcje biblioteczne odwołują się również do funkcji systemowych.

0x01 graphic

API a maszyny wirtualne

maszyna widziana przez programistę jest rezultatem nałożenia warstw oprogramowania na maszynę rzeczywistą, złożoną tylko ze sprzętu; przykładowo w programie asemblerowym programista posługuje się nie tylko instrukcjami procesora, ale ma do dyspozycji także funkcje systemowe udostępniane przez system operacyjny (np. wywoływane poprzez INT 21H w środowisku DOSu);

maszyna widziana przez programistę, będąca rezultatem nałożenia na maszynę rzeczywistą szeregu warstw oprogramowania, określana jest jako maszyna wirtualna;

w takim ujęciu maszyna rzeczywista, znajdująca się na najniższym poziomie jest "nadbudowana" przez warstwy oprogramowania, co powoduje że programista ma do dyspozycji pewną liczbę maszyn, z których może tworzyć nowe, bardziej abstrakcyjne maszyny;

ponieważ opis warstwy oprogramowania sformułowany jest jako API, więc można przyjąć, że API stanowi definicję maszyny wirtualnej;

programista wyobraża sobie maszynę wirtualną jako istniejącą maszynę fizyczną, nawet gdy naprawdę nie istnieje, tzn. gdy jest wirtualna; nawet jeśli programista jest świadomy istnienia maszyny wirtualnej, to nie musi zajmować szczegółami jej implementacji;

Implementacja funkcji API za pomocą bibliotek dynamicznych

funkcje Win32 API implementowane są za pomocą kodu zawartego w bibliotekach dynamicznych:

user32.dll (obsługa interfejsu użytkownika)

kernel32.dll (operacje plikowe, zarządzanie pamięcią)

gdi32.dll (operacje graficzne)

istnieje też szereg innych bibliotek DLL, np. WinInet, która obsługuje operacje w Internecie, Winmm, która obsługuje operacje multimedialne i wiele innych;

biblioteki dynamiczne pozwalają na współdzielenie kodu przez kilka procesów — kod pewnej funkcji może być (pseudo) jednocześnie wykonywany przez kilka procesów;

w systemie Windows każdy proces, który używa DLL otrzymuje swoją własną kopię sekcji (segmentu) danych — w szczególności oznacza to, że ewentualne wykorzystanie sekcji danych DLL do komunikacji między procesami wymaga wyraźnego zaznaczenia, że dane mają być współdzielone;

kod zawarty w bibliotekach dynamicznych wykonywany jest na poziomie uprzywilejowania 3, co oznacza, że nie komunikuje się on bezpośrednio z portami urządzeń wejścia/wyjścia; rolę tę pełnią sterowniki urządzeń.

Sterowanie procesami na poziomie języka C

exit - funkcja przekazuje sterowanie do procesu macierzystego, podając jednocześnie kod powrotu (zazwyczaj 0 przyjmowane jest jako kod poprawnego wykonania programu); przykładowe wywołanie może mieć postać:

exit (kod_zakonczenia);

Funkcja exit może być wywoływana wewnątrz dowolnej funkcji w programie. Natomiast wewnątrz funkcji main zamiast exit można umieścić instrukcję return, np.:

int main( )

{

- - - - - - - - - - - - - - - -

- - - - - - - - - - - - - - - -

return (kod_zakonczenia);

}

Należy odróżniać:

1. funkcję systemową exit zdefiniowaną w Unixie (Linuxie) — powoduje zakończenie procesu;

2. funkcję exit w języku C — powoduje najpierw wypisanie danych ze wszystkich częściowo zapełnionych buforów, wykonanie procedur zakończenia (zgłoszonych wcześniej przez funkcje atexit), a następnie wywołuje funkcję systemową exit;

3. funkcję _exit w języku C — od razu wywołuje funkcję systemową exit.

system - funkcja wywołuje interpreter zleceń (ang. shell) z podanym argumentem, którym może być standardowe polecenie systemu operacyjnego lub nazwa wywoływanego programu, np.:

int main ( )

{

int stan;

odp = system("date");

- - - - - - - - - - - - -

}

funkcja system zwraca 1 w przypadku błędu; jeśli argumentem funkcji jest łańcuch pusty, to wywołany interpreter podaje znak zachęty (ang. prompt) i dalsze polecenia podaje się z klawiatury — zakończenie funkcji system następuje po napisaniu exit;

exec... - zakończenie wykonywania bieżącego procesu i rozpoczęcie realizacji procesu utworzonego na podstawie pliku podanego jako argument funkcji — zatem funkcja systemowa exec... zastępuje dotychczas wykonywany proces przez inny; innymi słowy: funkcja exec... dokonuje ponownego zainicjowania procesu na podstawie wskazanego programu — kod i dane dotychczas wykonywane procesu są usuwane z pamięci, a na ich miejsce zostaje wprowadzony kod i dane pobrane z pliku; natomiast proces pozostaje ten sam (identyfikator procesu nie ulega zmianie);

funkcja exec... ma sześć odmian różniących się formatem parametrów (execl, execp, execv, ...);

proces, który wywołał funkcję exec... nazywany jest procesem wywołującym, a program który ma być wykonany nazywany jest nowym programem (w tej operacji Unix nie tworzy nowego procesu);

w systemie Unix wywołanie funkcji systemowej exec... jest jedynym sposobem wykonania innego programu; przykładowe wywołanie w środowisku Unix może mieć postać:

execl ("/bin/konw2.out",

"konw2.out", "/plany", "/wydr",NULL);

pierwszy parametr funkcji execl określa nazwę pliku zawierającego nowy program; następne parametry są tymi samymi, które byłyby przekazane do programu przez system, gdyby program został wywołany z linii zlecenia;

zazwyczaj pierwszy i drugi i parametr funkcji execl są podobne lub identyczne — pierwszy parametr zawiera bowiem nazwę programu, który zastąpi dotychczasowy, drugi parametr i następne przekazywane są do uruchamianego programu jako parametry linii zlecenia; przekazane parametry będą dostępne w uruchomionym programie, napisanym w języku C, jako argv[0] (drugi parametr), argv[1] (trzeci parametr), itd.; ponieważ standardowo parametr argv[0] zawiera nazwę wykonywanego programu, więc pierwszy i drugi parametr funkcji execl wskazują na ten sam plik (chociaż drugi może nie zawierać ścieżki dostępu);

wyjątkowo, po wywołaniu funkcji exec... proces wywołujący jest kontynuowany, jeśli nie udało się uruchomić nowego programu, np. ze względu na brak pamięci;

spawn... - funkcja powoduje zatrzymanie wykonywania procesu macierzystego, uruchomienie procesu potomnego, a jego zakończeniu wznowienie procesu macierzystego;

funkcja spawn... jest dostępna w systemie Windows, nie jest natomiast dostępna w systemie Unix;

przykładowe wywołanie może mieć postać:

spawnl(P_WAIT, "e:\\zadania\\konw2.exe",

"e:\\zadania\\konw2.exe", "/plany", "/wydr",NULL);

funkcja spawn... ma kilka odmian różniących się formatem parametrów (spawnl, spawnle, . . .).

w systemie Windows dopuszczalne są poniższe wartości pierwszego parametru (w systemie DOS mogą być używane tylko parametry P_WAIT i P_OVERLAY):

P_WAIT — powoduje zatrzymanie wykonywania procesu macierzystego aż do zakończenia procesu potomnego;

P_NOWAIT — powoduje wykonywanie procesu macierzystego i potomnego; funkcja zwraca identyfikator procesu potomnego, tak że proces macierzysty może oczekiwać na zakończenie procesu potomnego używając funkcji wait;

P_NOWAITO — podobnie jak P_NOWAIT, ale nie może być używana funkcja wait;

P_DETACH — podobnie do P_NOWAITO, ale proces potomny wykonywany jest w tle bez dostępu do klawiatury i ekranu;

P_OVERLAY — działa identycznie jak funkcja exec...;

CreateProcess stanowi podstawową funkcję używaną do tworzenia nowych procesów w systemie Windows; jednak ze względu na dużą liczbę parametrów i ich złożoność nie będzie tu omawiana; szczegóły można znaleźć w opisie Win32 API.

fork  - (ang. rozwidlenie) funkcja systemowa, dostępna w systemie Unix, tworzy kopię wykonywanego procesu i rozpoczyna wykonywanie dwóch niezależnych procesów realizujących ten sam kod; (innymi słowy: funkcja fork tworzy kopię aktualnie wykonywanego programu i rozpoczyna wykonywanie obu programów);

początkowe wartości zmiennych w obu procesach są te same co przed wykonaniem funkcji fork, ale proces może zmieniać wartości zmiennych tylko w swoim segmencie danych;

prototyp funkcji ma postać

int fork ( );

funkcja fork wywołana raz przez proces macierzysty przekazuje wartość dwukrotnie:

procesowi macierzystemu — numer identyfikacyjny nowo utworzonego procesu potomnego,

procesowi potomnemu — wartość 0;

zatem w wyniku przykładowego wywołania

proc_id = fork ( );

wartość zmiennej proc_id w procesie potomnym będzie wynosiła 0, a w procesie macierzystym będzie różna od 0; są to jedyne różnice w obu egzemplarzach programu; jeśli funkcji fork nie uda się wykonać pomyślnie, to zwracana jest wartość 1;

po wykonaniu funkcji fork kopia segmentu danych procesu potomnego jest kopią segmentu danych procesu macierzystego (ale to nie jest kopia odpowiedniego pliku dyskowego); często segment instrukcji (segment kodu) nie ulega żadnym zmianom w trakcie realizacji i może być współdzielony przez oba procesy;

w systemie Unix wykonanie funkcji fork stanowi jedyny sposób utworzenia nowego procesu;

proces potomny może wraz z procesem macierzystym wykonywać działania na wspólnych plikach (proces macierzysty otwiera te pliki przed fork i przekazuje procesowi potomnemu); proces potomny ma własne kopie uchwytów (deskryptorów) plików procesu macierzystego.

1. proces tworzy swoją kopię, aby jedna z nich mogła wykonać jakąś operację, podczas gdy druga kopia zajmowałaby się innym zadaniem;

2. proces zamierza wykonać inny program — ponieważ jedynym sposobem wykonania nowego procesu jest fork, więc proces musi najpierw utworzyć swoją kopię, a następnie jeden z procesów (zwykle proces potomny) wywołuje funkcję systemową exec w celu wykonania nowego programu; wg takiego schematu działają m.in. interpretery zleceń (ang. shell).

wait — funkcja systemowa Unixa (dostępna także w Win32), której wywołanie powoduje, że proces czeka aby jeden z utworzonych przez niego procesów potomnych zakończył działanie; prototyp funkcji ma postać

int wait (int * status);

wartość zwracana przez funkcję wait jest równa identyfikatorowi procesu potomnego, który został zakończony (jeśli nie było żadnego procesu potomnego, to funkcja zwraca 1);

jeśli proces wykonujący funkcję wait ma procesy potomne, które jeszcze nie zostały zakończone, to funkcja wait zawiesza proces wywołujący, dopóki nie zakończy działania jeden z jego potomków; po zakończeniu procesu potomnego wartość przekazana przez funkcję exit (kończąca działanie procesu potomnego) będzie zapamiętana w zmiennej status; zmienna status zawiera: w młodszym bajcie kod zakończenia podawany przez system operacyjny, w starszym bajcie kod powrotu podawany przez funkcję exit.

Analiza działania funkcji wait wymaga rozpatrzenia dwóch przypadków:

1. proces macierzysty wywołał funkcję wait zanim proces potomny zakończył działanie — w tym przypadku po wykonaniu funkcji exit zaczyna być dalej wykonywany proces macierzysty; argument funkcji exit będzie przekazany poprzez zmienną status;

2. proces potomny został zakończony zanim jeszcze proces macierzysty wykonał funkcję wait — w tym przypadku proces, który został zakończony poprzez wykonanie funkcji exit staje się procesem-duchem (ang. zombie process), nazywany także procesem w stanie zombie.

Proces-duch jest procesem zakończonym, ale na jego zakończenie nie czekał proces macierzysty; jądro systemu operacyjnego zwalnia wszystkie zasoby używane przez proces-duch (np. obszar pamięci), ale pozostawia rekord zawierający kod wyjścia i pewne statystyki; kod wyjścia zostanie udostępniony procesowi macierzystemu, gdy ten wywoła funkcję wait.

Należy także brać pod uwagę możliwość, że proces macierzysty zakończy się przed swoimi procesami potomnymi. W tej sytuacji identyfikatory procesu macierzystego dla rozpatrywanych procesów potomnych tracą ważność. Osieroconym procesom (aktywnym i duchom) Unix przydziela identyfikator procesu "init" (= 1).

funkcja spawn... nie jest dostępna w systemie Unix — można ją zastąpić przez funkcje fork i exec..., np.

int st1;

- - - - - - - - - - - -

proc_id = fork ( );

if (proc_id == 0)

{

/* ten fragment zostanie wykonany w proces. potomnym */

execl ("inny.exe", .....);

/* jedna z kopii (uzyskana jako wynik fork) zostanie

zastąpiona przez program "inny.exe", który

rozpocznie się wykonywać */

}

/* ten fragment zostanie wykonany w procesie macierzystym */

wait(&st1); /* oczekiwanie na zakończenie proc. potomnego */

Uwaga: zakładamy, że został uruchomiony dokładnie jeden proces potomny, jak również że w podanym fragmencie na pewno nie wystąpią błędy.

Programy rezydentne w systemie DOS

system operacyjny DOS dominował w komputerach PC do połowy lat dziewięćdziesiątych; system DOS jest stosunkowo prosty, jednozadaniowy, o umiarkowanych wymaganiach sprzętowych — te cechy odpowiadały możliwościom stosowanego wówczas sprzętu;

aczkolwiek system DOS z założenia był systemem jednozadaniowym, to jednak przez wiele lat czynione były próby prowizorycznego dostosowania systemu DOS do pracy wielozadaniowej; w wyniku podjętych prac rozwinięto technikę programów rezydentnych (ang. TSR — terminate and stay resident), stanowiących namiastkę wielozadaniowości;

obecnie system DOS wychodzi z użycia, ale programy "DOSowe" są nadal akceptowane przez różne wersje systemu Windows; system Windows musi być więc także przygotowany do wykonywania programów TSR;

program TSR różni się tylko tym od zwykłego programu DOSowego, że po wykonaniu programu jego część kodu i danych pozostaje w pamięci operacyjnej tworząc właściwy program rezydentny; program rezydentny może być później wielokrotnie uruchamiany (w całości lub tylko poprzez wywołanie wybranego podprogramu);

obecność programu rezydentnego ogranicza możliwość wykonywania innych programów jedynie w tym zakresie, że program rezydentny zajmuje część pamięci operacyjnej (o rozmiarze 640 KB); w rezultacie w pamięci operacyjnej komputera mogą się jednocześnie znajdować dwa programy, wykonywane na żądanie;

0x01 graphic

struktura typowych programów rezydentnych pokazana jest na rysunku;

0x01 graphic

ze względu na sposób, w jaki przekazywane jest sterowanie do programu rezydentnego można podzielić je na:

aktywne — wywoływane wskutek wystąpienia pewnych zdarzeń zewnętrznych w stosunku do aktualnie wykonywanego programu (zwykle naciśnięcie charakterystycznej kombinacji klawiszy);

pasywne — sterowanie przekazywane jest wyłącznie poprzez jawne odwołania do niego skierowane z innego programu (zwykle jako wywołanie podprogramu za pomocą instrukcji INT);

program rezydentny aktywny jest całkowicie odrębnym programem uruchamianym wskutek wystąpienia jakiegoś zdarzenia — uruchomienie takiego programu powoduje zawieszenie dotychczas wykonywanego programu na czas wykonywania programu rezydentnego;

system Windows zastępuje funkcje programów rezydentnych aktywnych i pasywnych:

aktywnych, poprzez naturalne wywołanie innego programu w trakcie wykonywania bieżącego;

pasywnych, poprzez możliwość instalacji bibliotek linkowanych dynamicznie (DLL), które mogą być wywoływane przez programy.

Zarządzanie pamięcią

pamięć operacyjna jest tablicą, której rozmiar może sięgać kilkuset milionów bajtów; każdy bajt ma własny adres; pamięć operacyjna traci zawartość po wyłączeniu zasilania (jest pamięcią ulotną);

pamięć operacyjna jest za mała aby pomieścić wszystkie dane i programy, a zawarte w niej dane giną po odcięciu zasilania;

konieczne jest więc stosowanie pamięci pomocniczej, stanowiącej zaplecze dla pamięci operacyjnej; zadaniem pamięci pomocniczych jest trwałe przechowywanie wielkiej ilości danych; typowym urządzeniem pamięci pomocniczej jest dysk magnetyczny, jako środek magazynowania danych i programów; stosowanych jest wiele innych typów pamięci pomocniczych, obecnie szeroko stosowane napędy CD ROM, wykorzystujące optyczne metody odczytu;

większość programów (w tym kompilatory, edytory, debuggery) do czasu załadowania do pamięci operacyjnej jest przechowywana na dysku; z kolei w trakcie wykonywania używa dysków jako źródeł i miejsc przeznaczenia przetwarzanych przez nie danych;

przetwarzanie danych zapisanych w pamięci pomocniczej wymaga uprzedniego przepisania ich do pamięci operacyjnej (za pomocą operacji wejścia-wyjścia);

warunkiem efektywnego wykorzystania systemu komputerowego, a także skrócenia czasu udzielania odpowiedzi użytkownikom jest stworzenie możliwości umieszczenia w pamięci operacyjnej kilku programów; można powiedzieć, że pamięć musi być dzielona między kilka procesów; wymaga to wprowadzenia odpowiednich metod zarządzania pamięcią.

pamięć operacyjna stanowi jeden z zasobów systemu komputerowego i racjonalne gospodarowanie pamięcią jest warunkiem efektywności systemu; udostępnianiem (przydzielaniem) bloków pamięci poszczególnym programom zajmuje się system operacyjny;

w wielu przypadkach w trakcie tworzenia programu nie można przewidzieć rozmiarów danych, na których program będzie wykonywał działania — w takich przypadkach pamięć potrzebna na dane powinna być przydzielana dynamicznie przez system operacyjny w miarę żądań programu;

system operacyjny powinien ewidencjonować aktualnie zajęte części pamięci wraz z informacją w czyim są władaniu;

system operacyjny powinien decydować, które procesy mają być załadowane do zwolnionych obszarów pamięci;

system operacyjny powinien przydzielać i zwalniać różne obszary pamięci stosownie do potrzeb; aczkolwiek podstawową rolę pełni pamięć operacyjna, to jednak pamięć pomocnicza jest często używana i musi działać wydajnie; system operacyjny musi zajmować się planowaniem przydziału obszarów pamięci dyskowej;

zarządzanie pamięcią jest szczególnie ważne w systemach wielozadaniowych — system operacyjny powinien odpowiednio dzielić dostępną pamięć między wykonywane programy, w dążeniu do jak najbardziej efektywnej pracy systemu komputerowego; opracowano i zbadano wiele różnych algorytmów zarządzania pamięcią.

Ochrona pamięci

w dobrze skonstruowanym systemie komputerowym powinny istnieć mechanizmy zapewniające ochronę systemu operacyjnego przed wpływami programów użytkowników, a ponadto wzajemną ochronę programów użytkowników;

prosty sposób realizacji takiej ochrony polega na użyciu rejestru bazowego i granicznego; mechanizm ten stosowany jest nadal we współczesnych procesorach, ale w zmodyfikowanej postaci;

0x01 graphic

rejestr bazowy przechowuje najmniejszy dopuszczalny adres fizyczny pamięci dla danego zadania;

rejestr graniczny zawiera rozmiar obszaru pamięci;

każdy adres wygenerowany w programie jest sprawdzany z zawartością ww. rejestrów; próba wyjścia poza obszar traktowana jest jako błąd programu;

zawartości rejestrów bazowego i granicznego ustawia system operacyjny za pomocą specjalnych rozkazów, które nie mogą być wykonywane przez program użytkownika;

Fragmentacja zewnętrzna i wewnętrzna

system operacyjny przechowuje tablicę z informacjami o tym, które części pamięci są dostępne, a które zajęte;

po uruchomieniu systemu cała pamięć jest dostępna dla procesów użytkowych i jest traktowana jako jeden wielki blok pamięci — dziura; gdy program lub system zgłasza zapotrzebowanie na pamięć, to poszukiwana jest dziura, która nadaje się najlepiej do przydziału;

najbardziej znane są następujące strategie poszukiwania:

1. pierwsze dopasowanie,

2. najlepsze dopasowanie,

3. najgorsze dopasowanie;

w wyniku wykonywania wielu operacji przydzielania i zwalniania obszarów pamięci występuje fragmentacja, objawiająca się istnieniem dużej liczby małych niezajętych obszarów pamięci — przydzielanie większych obszarów pamięci napotyka na trudności;

fragmentacja zewnętrzna polega na tym, że rozmiar znalezionej dziury jest większy od rozmiaru żądanego — po przydzieleniu pozostaje mały niezajęty obszar pamięci, który jest nieprzydatny dla innych programów;

czasami jeśli rozmiar dziury jest minimalnie większy od rozmiaru żądanego, to przydziela się całą dziurę, aby uniknąć kłopotów związanych z administrowaniem obszarami zawierającymi kilkanaście bajtów; w rezultacie program otrzymuje większy obszar pamięć niż zamówiony, co oznacza że część pamięci pozostanie niewykorzystana — omawiane sytuacja nazywana jest fragmentacją wewnętrzną;

system operacyjny musi więc być zdolny do przesuwania bloków pamięci, tak by obszar niezajęty tworzył zwarty blok; mechanizm stronicowania pozwala na tworzenie ciągłych obszarów pamięci bez konieczności przepisywania zawartości pamięci.

Technika nakładkowania a pamięć wirtualna

tworzone obecnie programy są często bardzo rozbudowane, tak że nie mieszczą się w całości w pamięci operacyjnej;

technika nakładkowania polega na przechowywaniu w pamięci operacyjnej tylko tych rozkazów i danych, które są stale potrzebne — inne rozkazy i dane ładowane są w miarę potrzeby; koncepcję tę realizuje się poprzez podział programu na fragmenty, nazywane nakładkami (ang. overlay) — w praktyce mają one postać procedur;

organizacja wymiany nakładek między pamięcią dyskową i operacyjną należy do obowiązków wykonywanego programu; zatem używanie nakładek nie wymaga jakichkolwiek specjalnych mechanizmów udostępnianych przez system operacyjny, ale wymiana nakładek musi być starannie zaprojektowana i zakodowana przez programistę;

we współczesnych systemach operacyjnych problem wykonywania dużych programów rozwiązuje się przede wszystkim poprzez udostępnienie pamięci wirtualnej; w rezultacie powstaje abstrakcja pamięci głównej w postaci wielkiej, jednolitej tablicy (uwalnia programistów od zajmowania się ograniczeniami pamięciowymi);

Stronicowanie

0x01 graphic

Przykład — z punktu widzenia programu zmienne a i b zajmują sąsiednie lokacje pamięci, chociaż w rzeczywistości (w pamięci fizycznej) zajmują odległe lokacje.

0x01 graphic

Dwupoziomowe tablice stron

ilustruje to poniższy rysunek:

0x01 graphic

Realizacja pamięci wirtualnej za pomocą stronicowania

Tablica TLB

0x01 graphic

Organizacja pamięci w komputerach PC

procesory Pentium (i ich poprzedniki) mogą pracować w trybie rzeczywistym i w trybie chronionym; dodatkowo w trybie chronionym zdefiniowany jest podtryb V86, w którym procesor, pracując dalej w trybie chronionym, zachowuje się prawie dokładnie tak samo jak w trybie rzeczywistym; podtryb V86 używany jest do wykonywania programów przewidzianych do wykonywania w systemie DOS;

w trybie rzeczywistym i w trybie V86 możliwe jest adresowanie obszaru 1 MB (pamięć operacyjna RAM zajmuje początkowe 640 KB tego obszaru); w trybie chronionym adresy mogą dochodzić do 4 GB;

0x01 graphic

dostęp do pamięci o adresach powyżej 1 MB, nazywanej pamięcią rozszerzoną, wymaga wprowadzenia procesora w tryb chroniony, co jest kłopotliwe dla programów wykonywanych w trybie rzeczywistym.

Standaryzacja metod dostępu do pamięci o adresach powyżej 1 MB

przez wiele lat, mimo rozpowszechniania się procesorów rodziny x86 posiadających zdolność pracy w trybie chronionym, brak było odpowiedniego oprogramowania pozwalającego na wykonywanie programów w trybie chronionym — system Windows rozpowszechnił się dopiero na początku lat dziewięćdziesiątych;

w istniejącej sytuacji, przed opracowaniem systemów operacyjnych umożliwiających wykonywanie programów w trybie chronionym, postanowiono doraźnie zdefiniować techniki i standardy wspomagające dostęp do pamięci rozszerzonej dla programów wykonywanych w trybie rzeczywistym i w trybie V86;

czołowe firmy komputerowe przyjęły dokumenty (specyfikacje) określające zasady dostępu do pamięci rozszerzonej — specyfikacje te stanowiły podstawę do budowy pakietów oprogramowania wspomagających dostęp do pamięci rozszerzonej, i jednocześnie określały interfejs, poprzez który zwykłe programy mogły się porozumiewać z oprogramowaniem wspomagającym;

najczęściej używane są specyfikacje:

XMS — (ang. extended memory specification) specyfikacja zasad dostępu do pamięci rozszerzonej; program HIMEM.SYS jest zgodny ze specyfikacją XMS;

EMS — (ang. expanded memory specification) specyfikacja zasad dostępu do pamięci oparta na innej koncepcji:

w latach osiemdziesiątych specyfikacja EMS implementowana była za pomocą kart rozszerzeniowych zawierających dodatkową pamięć RAM, umieszczoną poza przestrzenią adresową procesora;

w późniejszych rozwiązaniach zastosowano symulację za pomocą odpowiedniego oprogramowania (np. sterownik EMM.386 w systemie DOS);

DPMI — (ang. DOS protected mode interface) specyfikacja opracowana m.in. przez firmy Microsoft, Borland, oferuje możliwość wykonywania quasi-konwencjonalnych programów DOSowych w trybie chronionym;

wymienione specyfikacje są udostępniane także w systemie Windows 95/98 i NT; maksymalny rozmiar przydzielanego obszaru pamięci (zarządzanego przez jedną z ww. specyfikacji) jest ograniczony jedynie możliwościami systemu, co odpowiada ustawieniu "Automatycznie" w oknie Właściwości/Pamięć wykonywanego programu; w szczególności, ze względu na stosowanie pamięci wirtualnej możliwe jest przydzielenie obszaru pamięci większego niż cała zainstalowana pamięć RAM;

0x01 graphic

w przypadku źle napisanych programów, które próbują przydzielić całą dostępną pamięć (co znacznie pogarsza pracę innych programów) może być celowe ograniczenie rozmiaru pamięci XMS lub EMS, np. do 4 MB.

Przestrzeń adresowa w systemie Windows

0x01 graphic

każdy proces (zadanie) w systemie Windows widzi pamięć jako 32-bitową liniową przestrzeń adresową o adresach o 0 do 4 GB; obszar 2 4 GB jest zarezerwowany dla systemu; każdy proces ma dyspozycji obszar 4 MB 2 GB; pamięć o takiej wielkości nie jest (na razie?) dostępna w komputerach PC — jest implementowana jako pamięć wirtualna za pomocą stronicowania.

Wprowadzenie do operacji plikowych

Struktura informacji zapisywanej na dysku

Rozmieszczenie plików na dysku

Metoda przydziału listowego

0x01 graphic

Tablica rozmieszczenia plików FAT

Katalogi plików w systemie Windows

Przechowywanie plików na dysku w systemie Linux

0x01 graphic

12 bloków bezpośrednich po 4 KB = 48 KB

1 blok pośredni i 1024 bloki bezpośrednie = 4 MB

1 blok podwójnie pośredni i 1024 pośrednie = 4 GB

1 blok potrójnie pośredni i 1024 bloki podwójnie pośrednie = 4 TB

Operacje na plikach na poziomie API

0 — standardowe urządzenie wejściowe,

1 — standardowe urządzenie wyjściowe,

2 — standardowe urządzenie do sygnalizacji błędu;

zazwyczaj uchwyty 0, 1, 2 są w pewnym stopniu ze sobą związane.

Tworzenie, otwieranie i zamykanie plików

fd = open (nazwa, dostęp);

nazwa pliku podawana jest jawnie albo jako wskaźnik do łańcucha znaków

dostęp określa jakie operacje będą wykonywane na pliku:

O_RDONLY — tylko odczyt,

O_WRONLY — tylko zapis,

O_RDWR — odczyt lub zapis,

O_CREAT — tworzenie pliku (jeśli nie istnieje),

O_BINARY — otwarcie pliku w trybie binarnym,

O_TEXT — otwarcie pliku w trybie tekstowym;

można składać podane opcje za pomocą operatora |, np.

O_RDONLY | O_BINARY

co oznacza operację odczytu pliku w trybie binarnym;

EACCES odmowa dostępu,

EINVACC błędny kod dostępu,

EMFILE zbyt dużo otwartych plików,

ENOENT plik lub katalog nie istnieje;

fd = creat (nazwa, rodzaj ochrony);

gdzie parametr rodzaj ochrony może przyjmować wartości S_IWRITE lub S_IREAD;

close (fd)

Bieżąca pozycja pliku

Z pliku zawierającego 2472 bajtów odczytano 7 bajtów.

0x01 graphic

p = lseek (fd, offset, skąd);

gdzie:

p — bieżąca pozycja pliku po przesunięciu,

fd — uchwyt pliku,

offset — wielkość przesunięcia (long),

skąd — punkt startowy: może przyjmować wartości:

SEEK_SET — początek pliku,

SEEK_CUR — bieżące położenie,

SEEK_END — koniec pliku

p = tell (fd);

która zwraca bieżącą pozycję pliku (long);

Odczyt i zapis plików

read (fd, buf, n);

write (fd, buf, n);

gdzie:

fd — uchwyt pliku,

buf — bufor w programie,

n — liczba przesyłanych bajtów.

EACCES odmowa dostępu,

EBADF błędny uchwyt pliku;

Przeglądanie zawartości katalogów

za pomocą funkcji opendir, readdir i closedir

opendir otwarcie strumienia danych katalogowych,

readdir odczytanie ze strumienia danych katalogowych pojedynczej pozycji katalogu (kolejne wykonanie funkcji readdir spowoduje odczytanie kolejnej pozycji katalogu),

rewinddir przesunięcie strumienia na pierwszą pozycję katalogu,

closedir zamknięcie strumienia danych katalogowych;

#include <stdio.h>

#include <sys/types.h>

#include <dirent.h>

int main ( )

{

DIR * dirp;

struct dirent * dp;

printf("\n\n");

dirp = opendir(".");

/* odczytywanie bieżącego katalogu */

while ((dp = readdir(dirp)) != NULL)

{

printf("\nPlik = %s", dp > d_name);

}

/* od nowa */

rewinddir(dirp);

while ((dp = readdir(dirp)) != NULL)

{

printf("\nPlik = %s", dp > d_name);

}

closedir (dirp);

printf("\nTest programu\n");

return 0;

}

char d_name[ ];

pole to zawiera nazwę (np. pliku), zakończoną bajtem zerowym; przekazywane nazwy mogą stanowić: nazwy plików (także plików ukrytych i systemowych), nazwy katalogów (w tym katalogi . i ..), etykiety dysku oraz nazwy urządzeń; funkcja readdir zwraca wskaźnik NULL w przypadku natrafienia na koniec katalogu, a także w przypadku błędu;

#include <sys\stat.h>

struct stat statbuf;

/* odczytywanie bieżącego katalogu */

while ((dp = readdir(dirp)) != NULL)

{

printf("\nPlik = %s", dp > d_name);

stat (dp -> d_name, &statbuf);

if (statbuf.st_mode & S_IFDIR) printf(" - jest to katalog!");

else printf (" - rozmiar pliku = %ld", statbuf.st_size);

Inne operacje na plikach

rename (stara nazwa, nowa nazwa);

w przypadku poprawnej realizacji funkcja zwraca wartość 0, w przeciwnym razie -1; przykład:

char nazwa_pliku [80];

- - - - - - - - - - - -

strcpy (nazwa_pliku, "nowy_sch.rys");

rename ("schemat.rys", nazwa_pliku);

unlink (nazwa pliku);

getcurdir (napęd, bufor);

gdzie:

napęd — napęd bieżący - 0, napęd A - 1, napęd B - 2, itd.,

tablica — tablica, do której zostanie wpisana ścieżka do katalogu (łańcuch pusty w przypadku katalogu głównego).

chdir (nazwa katalogu);

mkdir (nazwa katalogu);

rmdir (nazwa katalogu);

getdisk( );

setdisk ( );



Wyszukiwarka

Podobne podstrony:
ask1, Edukacja, studia, Semestr IV, Architektura Systemów Komputerowych, Wyklad
ask3, Edukacja, studia, Semestr IV, Architektura Systemów Komputerowych, Wyklad
ask2, Edukacja, studia, Semestr IV, Architektura Systemów Komputerowych, Wyklad
ASK-koło pierwsze pytania z mojej grupy, Edukacja, studia, Semestr IV, Architektura Systemów Kompute
opracowane pytania na ASK@, Edukacja, studia, Semestr IV, Architektura Systemów Komputerowych, Oprac
Projekt 3, Edukacja, studia, Semestr IV, Architektura Systemów Komputerowych, Projekt, Projekt 3
Teoria 2003, Edukacja, studia, Semestr IV, Architektura Systemów Komputerowych, Opracowania pytań
assembler 1, Edukacja, studia, Semestr IV, Architektura Systemów Komputerowych, Projekt, Projekt 1
Pytania przykl ASK1, Edukacja, studia, Semestr IV, Architektura Systemów Komputerowych, Opracowania
Pytania przykl ASK2, Edukacja, studia, Semestr IV, Architektura Systemów Komputerowych, Opracowania
TECHNIKA MIKROPROCESOROWA (1), Edukacja, studia, Semestr IV, Technika Mikroprocesorowa
liniowkaWKLEPANE PYTANIA, Edukacja, studia, Semestr IV, Układy Elektroniczne
pytania na smoki, Edukacja, studia, Semestr IV, Technika Mikroprocesorowa
arch02, UŁ Sieci komputerowe i przetwarzanie danych, Semestr II, Architektura systemów komputerowych
Układy Elektroniczne zagadnienia, Edukacja, studia, Semestr IV, Układy Elektroniczne
arch05, UŁ Sieci komputerowe i przetwarzanie danych, Semestr II, Architektura systemów komputerowych
arch07, UŁ Sieci komputerowe i przetwarzanie danych, Semestr II, Architektura systemów komputerowych
Optoelektronika kolo 1, Edukacja, studia, Semestr IV, Optoelektronika, Pytania na koła, zestaw 8
JavaScript- podstawy, Edukacja, studia, Semestr IV, Języki Programowania Wysokiego Poziomu, Java skr

więcej podobnych podstron