• Nie Znaleziono Wyników

Wyjaśnienie. Algorytm Mutual Exclusion dostępu do sekcji krytycznej obsługi danego zasobu, z którego równocześnie korzystać może tylko jeden proces nosi nazwę Algorithm of

1.4. S YSTEM OPERACYJNY

1.4.7.30. Wyjaśnienie. Algorytm Mutual Exclusion dostępu do sekcji krytycznej obsługi danego zasobu, z którego równocześnie korzystać może tylko jeden proces nosi nazwę Algorithm of

Mutual Exclusion. Jedną z pierwszych wersji algorytmu był opracowany w 1962 roku przez holenderskiego matematyka T.J. Dekkera. Był to poprawny algorytm, stosowany do dzisiaj. Jest to algorytm typu producent-konsument. Ciekawostką jest, że uważano ten algorytm za zbyt złożony i wielokrotnie próbowano go bezskutecznie uprościć. Między innymi jeden z poważnych producentów oprogramowania wprowadził w nowoopracowanym SO - algorytm zawierający poważny błąd. Na błąd ten zwrócili uwagę w 1980 roku Doran i Thomas. Uproszczony poprawny algorytm Mutual Exclusion opublikował w 1981 roku G.I. Peterson.

Piśmiennictwo: Shaw A. S.4.1., Silberschatz A. S.6.1., Stencel K. S.12.1.

1.4.8.ZARZĄDZANIE WEJŚCIEM/WYJŚCIEM

Jak wiemy, komputer składa się z: procesora, urządzeń zewnętrznych, sterowników urządzeń zewnętrznych, pamięci operacyjnej, pamięci podręcznej, sterownika pamięci i magistrali systemowej. Za obsługę tych części składowych konfiguracji komputera odpowiada system operacyjny. Sekcja systemu operacyjnego - obsługi każdego urządzenia wejścia/wyjścia jest sekcją krytyczną systemu operacyjnego (patrz podrozdział 1.4.7).

1.4.8.10. Wyjaśnienie. Urządzenia zewnętrzne komunikują się z systemem komputerowym poprzez przesyłanie sygnałów. Łączem między urządzeniem a komputerem jest tzw. port. Porty są podłączane do magistrali, tzn. mediów, którymi przesyłane są sygnały (takim medium może być wiązka przewodów; przesyła się przez nią sygnały w postaci impulsów elektrycznych). Do jednej magistrali może być podłączonych wiele portów. Do każdego portu może być podłączony jeden lub więcej sterowników urządzań, do których z kolei są podłączne urządzenia wejścia/wyjścia. W komputerze mogą również występować układy szeregowe, tzn. że urządzenie A jest podłączone do urządzenia B, które z kolei jest połączone z urządzeniem C, które jest już bezpośrednio połączone do portu komputera.

1.4.8.20. Wyjaśnienie. Sterownik jest układem elektronicznym, który nadzoruje pracę portu, szyny lub urządzenia albo większej ich grupy. Sterownik pośredniczy w przesyłaniu informacji z i do urządzeń. Sterownik ma własną pamięć, w której może buforować dane przesyłane do i z urządzeń. Pozwala to na szybkie przesłanie informacji magistralą systemową, niezależnie od prędkości urządzenia. Do magistrali systemowej mogą też być podłączone sterowniki, które łączą główną magistralę z magistralami niższego poziomu (np. PCI, ISA), do których też mogą być podłączone sterowniki itd. Często też urządzenia zewnętrzne są podłączone do sterownika za pomocą odpowiedniej specjalizowanej magistrali (np. SCSI).

1.4.8.30. Wyjaśnienie. Procesor przekazuje sterownikom polecenia i dane niezbędne do wykonania tych poleceń. Sterownik ma specjalny rejestr, który służy do przekazywania komunikatów z i do procesora. Procesor porozumiewa się ze sterownikiem pisząc i czytając bity w tym rejestrze. Przesłanie tych bitów do rejestru urządzenia może odbywać się poprzez specjalne instrukcje wejścia/wyjścia będące zleceniami przesłania bitów na adres portu wejścia/wyjścia. Rozkazy te powodują przekazanie bitów z i do rejestrów sterujących urządzenia poprzez magistralę.

1.4.8.40. Wyjaśnienie. Urządzenia wejścia/wyjścia dzielimy na dwie podstawowe klasy:

1. Urządzenia znakowe, takie jak np. klawiatura, które transmitują dane asynchronicznie znak po znaku, a odczyt/zapis każdego znaku jest obsługiwany bezpośrednio przez procesor.

2. Urządzenia blokowe, takie jak np. dysk magnetyczny, które transmitują dane blokami synchronicznie, a odczyt/zapis każdego bloku danych jest jedynie inicjowany przez procesor, następnie zaś wykonywany pod nadzorem układu DMA.

1.4.8.50. Wyjaśnienie. Wyróżnimy ponadto dwa szczególne rodzaje urządzeń, a mianowicie:

1. Urządzenia sieciowe. Urządzenia sieciowe stanowią dość szczególną grupę ze względu na wydajność i adresowanie. Zwykle mają więc szczególny interfejs, inny niż czytaj/pisz/szukaj charakterystyczny dla urządzeń blokowych. Ważnym aspektem urządzeń sieciowych jest nawiązywanie połączenia, nasłuchiwanie z jednego połączenia oraz nasłuchiwanie z wielu połączeń równocześnie i obsługa jednego połączenia, którym akurat przysłano dane.

2. Zegary i czasomierze. Ich zadaniem jest podawanie bieżącego czasu, czasu trwania czegoś i uruchamianie operacji w ściśle określonej chwili. Szczególność tych urządzeń wynika z tego, że nie chodzi tu o transfer danych, ale raczej o synchronizację transferów. Podczas gdy w przypadku np. dysku przerwanie oznacza zakończenie operacji, to w przypadku zegara przerwanie zegarowe jest jedynym oczekiwanym wynikiem jego działania!

Wiemy już, czym jest urządzenie wejścia/wyjścia oraz w jaki sposób korzysta z niego komputer.

Kontakt procesora z urządzeniem może być zaimplementowany za pomocą odpytywania, przerwań i DMA. Żądanie procesu użytkownika trafia do jądra systemu operacyjnego, w którym odpowiedni fragment oprogramowania tzw. podsystem wejścia/wyjścia zleca odpowiednie operacje modułowi sterującemu. Moduł sterujący to fragment oprogramowania komunikujący się bezpośrednio ze sprzętowym sterownikiem urządzenia. Moduł sterujący obudowuje charakterystykę urządzenia prezentując jądru systemu operacyjnego standardowy interfejs.

Wydajność wejścia/wyjścia ma istotny udział w wydajność całego systemu.

Piśmiennictwo: Shaw A. S.4.1., Silberschatz A. S.6.1., Stencel K. S.12.1.

1.5. Z

ARYS PROGRAMOWANIA W JĘZYKU

C

1.5.0.KILKA SŁÓW O POWSTANIU I DONIOSŁOŚCI JĘZYKA C

Jak podaje Wikipedia: „C – imperatywny, strukturalny język programowania wysokiego poziomu stworzony na początku lat siedemdziesiątych XX w. przez Dennisa Ritchiego do programowania systemów operacyjnych i innych zadań niskiego poziomu”. Poprzednikiem języka C był interpretowany język B, który Ritchie rozwinął w język C. Pierwszy okres rozwoju języka to lata 1969 - 1973. W roku 1973 w języku C udało się zaimplementować jądro systemu operacyjnego Unix. W 1978 roku Brian Kernighan i Dennis Ritchie opublikowali dokumentację języka pt. C Programming Language (wydanie polskie: Język ANSI C). Język C stał się popularny poza Laboratoriami Bella (gdzie powstał) po 1980 roku i stał się dominującym językiem do programowania systemów operacyjnych i aplikacji. Na bazie języka C w latach osiemdziesiątych Bjarne Stroustrup stworzył język C++, który ułatwia znacząco programowanie obiektowe. W 1983 roku ANSI powołało komitet X3J11 w celu ustanowienia standardu języka C. Standard został zatwierdzony w 1989 roku jako ANSI X3.159-1989 "Programming Language C". Ta wersja języka jest określana nieformalnie jako ANSI C, standardowe C lub C89. W 1990 roku standard

ANSI C został zaadoptowany przez ISO jako norma ISO/IEC 9899:1990. Ta wersja jest potocznie nazywana C90. Ponieważ normy wydane przez oba ciała standaryzacyjne są identyczne, wobec tego potoczne określenia C89 oraz C90 dotyczą tej samej wersji języka C. W 1999 roku ISO opublikowało normę ISO/IEC 9899:1999, język zgodny z tą normą jest nieformalnie nazywany C99. Ostatnia norma została opublikowana w 2011 roku pod nazwą ISO/IEC 9899:2011. Ta wersja języka jest potocznie nazywana C11 (C1X przed opublikowaniem normy).”

Pojawienie się języka C w 1972 roku, stanowiło przełom w dotychczasowym podejściu do budowy oprogramowania dla komputera o nowoopracowanej architekturze. Złożyło się na to kilka istotnych przyczyn. A mianowicie:

1. Opracowany został język wysokiego poziomu abstrakcji (o notacji zbliżonej do takiego języka jak FORTRAN, czy ALGOL 60), a jednocześnie język bardzo bliski rozwiązań sprzętowych architektury komputera.

2. Napisano kompilator języka C, w tymże języku C. Wystarczyło, więc zdefiniować w notacji BNF poszczególne instrukcje języka C w krótkich sekwencjach instrukcji wewnętrznego kodu danego komputera, a następnie z pomocą prostego programu wczytać kompilator kodu języka C oraz sekwencje instrukcji kodu wewnętrznego realizujące instrukcje języka C; po czym dokonać kompilacji - kompilatora języka C, w ten sposób otrzymujemy instalację języka C – np. na nowo zaprojektowanym komputerze.

3. Kolejnym krokiem, jest możliwość poprawienie sprawności zainstalowanego języka C, poprzez użycie narzędzia optymalizacji kodu wynikowego kompilacji.

4. Dysponując biblioteką podstawowego oprogramowania łącznie z systemem operacyjnym (np. z rodziny systemów LINUX) – napisanym w języku C, możemy po zainstalowaniu na danym komputerze z kompilatorem języka C, zainstalować system operacyjny i bibliotekę oprogramowania, w tym kompilatory języków wysokiego rzędu oraz systemy aplikacyjne.

Tym samym, język C stał się podstawowym narzędziem przenaszalności oprogramowania.

Pierwszym chyba, zastosowaniem tej nowej technologii – było opracowanie oprogramowania dla komputera firmy DEC PDP-11, wykorzystując do tego celu komputer PDP-8. Dziś język C, oraz jego rozszerzenie język C++, są powszechnie używanymi językami programowania, przez profesjonalnych programistów systemowych. W wielu przypadkach, język C - zastąpił wcześniej powszechnie stosowane języki symboliczne typu assembler.

Język C jest jednak krytycznie oceniany przez część programistów. Jak podaje Wikipedia:

„…pozwala na wykonywanie niskopoziomowych operacji, przez co wiele prostych błędów programistycznych nie jest wykrywanych przez kompilator, a przy wykonywaniu programu ujawniają się dopiero po jakimś czasie i w przypadkowych miejscach. Twórcy języka chcieli uniknąć sprawdzeń w czasie kompilacji i wykonywania programu, bo były one zbyt kosztowne czasowo, gdy C był implementowany po raz pierwszy. Z czasem powstały zewnętrzne narzędzia do wykonywania części z tych sprawdzeń. Nic nie przeszkadza implementacji języka w dostarczaniu takich sprawdzeń, ale też nie są one wymagane przez oficjalne standaryzacje.

Używanie języka C wymaga od programisty dokładnego zrozumienia pisanego kodu źródłowego, łącznie z mechanizmami kompilacyjnymi, dodatkowo komplikowanymi nieprzenośnością między platformami i kompilatorami, jak również rygorystycznego przestrzegania dobrych praktyk, szczególnie w odniesieniu do funkcji obsługujących wszelkiego rodzaju buforowania. Podobnie brak standaryzacji bibliotek wyższego poziomu jest powodem do uznania C za język niezalecany dla początkujących. Jednakże wiele z tych niedogodności można zniwelować tworząc własne elastyczniejsze rozwiązania. Pod względem zastosowań

praktycznych C nie ustępuje innym językom, traci jednak w stosunku do nich, gdy wziąć pod uwagę czas i inne środki niezbędne do implementacji porównywalnych systemów.”

Piśmiennictwo: Wikipedia W.2.6.

1.5.1.TWORZENIE PROGRAMU W JĘZYKU C

1.5.1.10. Wyjaśnienie. Komentarz blokowy umieszcza się między sekwencją znaków "/*" a

"*/", a komentarz liniowy rozpoczyna się sekwencją "//" a kończy znakiem końca linii.

1.5.1.11. Wyjaśnienie. Lista słów kluczowych języka C określona jest przez normy ISO/IEC 9899-1999 (C99) – patrz tab. 1.5.1.12. Uwaga: Istnieją zależne od implementacji rozszerzenia języka o inne słowa kluczowe jak np. asm – dla oznaczenia wstawki kodu asemblera.

Przykład programu:

#include <stdio.h>

int main(void) {

printf("hello, world\n");

return 0;

}

W powyższym kodzie:

Dyrektywa #include włącza do pliku zawartość odpowiednich plików nagłówkowych – w tym przypadku pliku stdio.h, zawierającego m.in. prototyp funkcji printf.

Główna funkcja nazywa się zawsze main. Zwraca ona wartość typu całkowitoliczbowego – int, w tym przypadku 0.

Za wyprowadzenie wyniku na standardowe wyjście (zwykle na ekran) odpowiedzialna jest funkcja printf.

Łańcuch tekstowy zamyka się w cudzysłowach: "łańcuch".

Znak nowej linii zapisuje się jako "\n".

Tabela 1.5.1.12. Słowa kluczowe języka C

auto default float long sizeof unsigned _Imaginary

break do for register static void

case double goto restrict struct volatile

char else if return switch while

const enum inline short typedef _Bool

continue extern int signed union _Complex

Piśmiennictwo: Tłuczek M. T.5.1.

1.5.2.ZMIENNE, STAŁE I WYRAŻENIA W JĘZYKU C

1.5.2.10. Wyjaśnienie. Każda zmienna odpowiada przypisanemu miejscu w pamięci operacyjnej komputera, któremu to miejscu pamięci można przypisać różne wartości – danej zmiennej.

Przed użyciem zmiennej w programie należy ją zadeklarować.

1.5.2.11. Wyjaśnienie. Zmienne deklaruje się za pomocą prostej konstrukcji: typ nazwa; (patrz tab. 1.5.2.12. Należy pamiętać, że podane powyżej rozmiary zmiennych są jedynie orientacyjne i mogą się różnić w zależności od środowiska (w systemach 64-bitowych zmienna long posiada zazwyczaj 64-bity). Inną ważną sprawą jest szerokość bajtu. Język C wymaga tylko, by bajt składał się z co najmniej 8 bitów. Jest to zwykle najmniejsza porcja danych, która może być

adresowana. Wielu programistów nie zdaje sobie sprawy z powyższych problemów, co może być (i jest) przyczyną wielu błędów oprogramowania, a w rezultacie powstają różne luki w bezpieczeństwie oprogramowania.

Tabela 1.5.2.12 Lista typów zmiennych w języku C Typ zmiennej Symbole

char %c 1 bajt Pojedynczy znak ASCII oraz liczby <-128, +127>

unsigned char %c 1 bajt Pojedynczy znak ASCII oraz liczby <0, 255>

signed char %c 1 bajt Pojedynczy znak ASCII oraz liczby <-128, +127>

int %d 4 bajty Liczba stałoprzecinkowa z zakresu

<-2 147 483 648, +2 147 483 647>

unsigned int %u 4 bajty Liczba stałoprzecinkowa z zakresu

<0, +4 294 967 295>

long int %ld 4 bajtów Liczba stałoprzecinkowa z zakresu

<-2 147 483 648, +2 147 483 647>

unsigned long

int %lu 4 bajtów Liczba stałoprzecinkowa z zakresu

<0, +4 294 967 295>

float %f 4 bajty Liczba zmiennoprzecinkowa z zakresu

<-1.2E-38, 3.4E38>, pamiętanych 7 cyfr mantysy double %f 8 bajtów Liczba zmiennoprzecinkowa z zakresu

<-2.22E-308, 1.8E308>, pamiętanych 16 cyfr mantysy

1.5.2.13. Wyjaśnienie. Typy pochodne danych, to:

• Wyliczeniowe typy danych: enumnazwa { jeden, dwa };

• Struktury: struct nazwa { typ1 nazwa1; typ2 nazwa2; };

• Unie: union nazwa { typ1 nazwa1; typ2 nazwa2; };

• Pola bitowe: typ [identyfikator] : długość;

• Tablice: typ nazwa[liczba];

Wskaźniki: typ *nazwa; typ **nazwa; typ_zwracany (*nazwa_wsk_do_funkcji)(typ parametru1,typ parametru2,...);

1.5.2.14. Wyjaśnienie. Stałe deklarujemy, na jeden z dwu sposobów:

1. Stała symboliczna deklarowana na początku kodu w następujący sposób:

#define NaszaLiczba 13, (NaszaLiczba to symbol stałej, a 13 – przypisana jej wartość).

2. Innym sposobem deklaracji stałej jest użycie słowa kluczowego const:

np. const NaszaLiczba 13. Piśmiennictwo: Tłuczek M. T.5.1.

1.5.3.INSTRUKCJE ARYTMETYCZNE, LOGICZNE, WYJŚCIA I WEJŚCIA, ORAZ SKOKU I WARUNKOWE 1.5.3.01. Wyjaśnienie. Symbol „=” jest używany w języku C, jako symbol operacji podstawienia (podobnie jak w pierwszym języku programowania wysoko - poziomowego FORTRAN), natomiast jako znak równości jest wykorzystywana para symboli „==”, zaś jako znak nierówności para symboli „!=”.

1.5.3.02. Wyjaśnienie. Instrukcje arytmetyczne (dodawania, odejmowania, mnożenia i dzielenie) mają postać: x = y + 10; x = y – 6; z = x *y / 2;

1.5.3.03. Wyjaśnienie. Skrócony zapis operacji dodawania jedynki i odejmowania jedynki (przedrostkowo i przyrostkowo): x++; ++x; x--; --x;

1.5.3.11. Wyjaśnienie. Instrukcje logiczne wykonywane na parach odpowiadających pozycji

Outline

Powiązane dokumenty