Wstęp do informatyki (Podręcznik.

Wersja RC1)1
Piotr Fulmański2 Ścibór Sobieski3

4 stycznia 2004

RC1 — Release Condidate 1, oznacza, że nie będzie żadnych istotnych modyfikacji, raczej będą usuwane wyłącznie błędy. 2 E-mail: fulmanp@math.uni.lodz.pl 3 E-mail: scibor@math.uni.lodz.pl
1

2

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

Spis treści
Spis treści Spis rysunków 1 Wstęp 1.1 Czym jest informatyka? . . . . . . 1.2 Historia informatyki . . . . . . . . 1.2.1 Bardzo dawno temu ... . . . 1.2.2 Ostatnie tysiąclecie . . . . . 1.2.3 Wiek XX . . . . . . . . . . 1.3 Zastosowanie i przyszłość . . . . . 1.4 Kierunki współczesnej informatyki 1.4.1 Algorytmika . . . . . . . . . 1.4.2 Bazy danych . . . . . . . . 1.4.3 Grafika komputerowa . . . 1.4.4 Kryptografia . . . . . . . . 1.4.5 Programowanie . . . . . . . 1.4.6 Sieci komputerowe . . . . . 1.4.7 Systemy operacyjne . . . . 1.4.8 Sztuczna inteligencja . . . . 1.4.9 Teoria informacji . . . . . . 1.5 Zadania . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 9 15 15 17 17 19 22 27 29 29 30 30 31 31 32 33 35 36 37 39 39 39 44 45 47

2 Podstawowe pojęcia i definicje 2.1 Algebra Boole’a . . . . . . . . . . . . . . 2.1.1 Definicja ogólna . . . . . . . . . 2.1.2 Dwuelementowa algebra Boole’a 2.2 Pozycyjne systemy liczbowe . . . . . . . 2.2.1 System dwójkowy . . . . . . . .

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

4

SPIS TREŚCI

2.3 2.4

2.2.2 Zapis liczby rzeczywistej w systemie dwójkowym 2.2.3 Kod szesnastkowy . . . . . . . . . . . . . . . . . 2.2.4 Inne pozycyjne systemy liczbowe . . . . . . . . . BCD . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Zadania . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

52 56 57 62 67 69 69 70 75 78 83 84 86 89 90 96 100 100 103 103 105 109 113 117 117 118 120 121 122 125 128 129 129 132 132 133 134

3 Architektura i działanie komputera 3.1 Maszyna Turinga . . . . . . . . . . . . . . 3.1.1 Definicja Maszyny Turinga . . . . 3.2 Bramki logiczne . . . . . . . . . . . . . . . 3.3 Architektura współczesnego komputera . . 3.4 Procesor — serce komputera . . . . . . . 3.4.1 Cykl pracy procesora . . . . . . . . 3.4.2 Rejestry procesora Intel 8086 . . . 3.4.3 Budowa rozkazu . . . . . . . . . . 3.4.4 Adresowanie . . . . . . . . . . . . 3.4.5 Asembler . . . . . . . . . . . . . . 3.5 Reprezentacja informacji . . . . . . . . . . 3.5.1 Znaki alfanumeryczne . . . . . . . 3.5.2 Liczby naturalne . . . . . . . . . . 3.5.3 Liczby całkowite . . . . . . . . . . 3.5.4 Reprezentacja uzupełnień do dwu . 3.5.5 Liczby rzeczywiste . . . . . . . . . 3.6 Zadania . . . . . . . . . . . . . . . . . . . 4 Teoria informacji 4.1 Informacja vs. wiadomość . . . . . . 4.2 Geneza i zakres teorii informacji . . 4.3 Metody kontroli poprawności danych 4.3.1 Bit parzystości . . . . . . . . 4.3.2 Suma kontrolna . . . . . . . . 4.3.3 Kod CRC . . . . . . . . . . . 4.4 Zadania . . . . . . . . . . . . . . . . 5 Algorytmy i struktury danych 5.1 Pojęcie algorytmu . . . . . . 5.2 Struktury danych . . . . . . . 5.2.1 Typ danych . . . . . . 5.2.2 Tablica . . . . . . . . 5.2.3 Rekord . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

SPIS TREŚCI

5

5.3

5.4

5.5 5.6 5.7

5.2.4 Zbiór . . . . . . . . . . . 5.2.5 Plik . . . . . . . . . . . . Metody opisu algorytmów . . . . 5.3.1 Schemat blokowy . . . . . 5.3.2 Schemat zapisu algorytmu Podstawowe algorytmy . . . . . . 5.4.1 Algorytmy obliczeniowe . 5.4.2 Algorytmy sortowania . . 5.4.3 Algorytmy wyszukujące . Rekurencja vs. iteracja . . . . . . Analiza złożoności . . . . . . . . Zadania . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . za pomocą pseudo-języka . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . .

136 136 137 137 139 143 143 144 146 148 153 155 157 157 159 159 173 173 176 177 183 183 184 185

6 Języki programowania 6.1 Czym jest język programowania? . . . . . . . . . . 6.1.1 Składnia i semantyka . . . . . . . . . . . . . 6.2 Ewolucja języków programowania . . . . . . . . . . 6.3 Klasyfikacja języków programowania . . . . . . . . 6.3.1 Podział według metodologii programowania 6.3.2 Generacje języków programowania . . . . . 6.4 Kompilacja vs. interpretacja . . . . . . . . . . . . . 6.5 Elementy teorii języków formalnych . . . . . . . . . 6.5.1 Gramatyka . . . . . . . . . . . . . . . . . . 6.5.2 Notacja BNF . . . . . . . . . . . . . . . . . 6.5.3 Notacja przy użyciu diagramów składni . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

7 System operacyjny 7.1 Zadania realizowane przez system operacyjny . . . . . . 7.2 System operacyjny a architektura komputera . . . . . . 7.3 Klasyfikacja systemów operacyjnych . . . . . . . . . . . 7.4 Realizacja zadań . . . . . . . . . . . . . . . . . . . . . . 7.5 W kierunku systemów wielozadaniowych . . . . . . . . . 7.6 Procesy, wątki, . . . . . . . . . . . . . . . . . . . . . . . . 7.7 Zarządzanie pamięcią . . . . . . . . . . . . . . . . . . . . 7.8 System plików . . . . . . . . . . . . . . . . . . . . . . . . 7.9 Czy każdy komputer musi posiadać system operacyjny? 7.10 Przykładowe systemy operacyjne . . . . . . . . . . . . . 7.10.1 Amoeba . . . . . . . . . . . . . . . . . . . . . . . 7.10.2 Mac OS . . . . . . . . . . . . . . . . . . . . . . .

187 . 188 . 189 . 190 . 193 . 196 . 197 . 198 . 198 . 199 . 201 . 201 . 202

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

6

SPIS TREŚCI

7.10.3 MS-DOS i Windows 7.10.4 NetWare . . . . . . . 7.10.5 OS/390 . . . . . . . 7.10.6 OS/400 . . . . . . . 7.10.7 Unix i rodzina . . . 7.11 Zadania . . . . . . . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . .

204 207 208 210 211 213 215 215 216 216 217 218 222 223 224 224 225 227 228 229 229 230 233 235 237 241 243

8 Sieci komputerowe 8.1 Po co mi ona? . . . . . . . . . . . 8.2 Struktura fizyczna sieci . . . . . 8.2.1 Zasięg sieci . . . . . . . . 8.2.2 Topologia sieci . . . . . . 8.3 Architektura sieci . . . . . . . . . 8.3.1 Architektura równorzędna 8.3.2 Architektura klient-serwer 8.4 Kilka przydatnych pojęć . . . . . 8.4.1 Pakiety i ramki . . . . . . 8.4.2 Protokół . . . . . . . . . . 8.4.3 TCP/IP . . . . . . . . . . 8.4.4 Usługa DNS . . . . . . . . 8.5 Urządzenia sieci komputerowych 8.5.1 Urządzenia transmisji . . 8.5.2 Urządzenia dostępowe . . 8.6 Zadania . . . . . . . . . . . . . .

A Podstawowe jednostki i pojęcia w informatyce B Kody ASCII C Kodowanie polskich znaków D Organizacje standaryzujące E Rozwiązania zadań E.1 Rozwiązania do rozdziału E.2 Rozwiązania do rozdziału E.3 Rozwiązania do rozdziału E.4 Rozwiązania do rozdziału Bibliografia 2 3 4 5 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

245 245 248 251 252 257

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

SPIS TREŚCI

7

Indeks

260

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

8

SPIS TREŚCI

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

Spis rysunków
1.1 1.2 3.1 3.2 3.3 3.4 3.5 3.6 3.7 3.8 3.9 3.10 3.11 3.12 3.13 3.14 3.15 3.16 5.1 5.2 5.3 Ilustracja idei metody różnicowej . . . . . . . . . . . . . . . . 20 Schemat maszyny analitycznej . . . . . . . . . . . . . . . . . 21 Ilustracja procesu czytania przez człowieka. . . . . . . . . . . Kartka przekształcona w taśmę . . . . . . . . . . . . . . . . . Symbole reprezentujące bramki AND, OR, NOT, NAND, NOR i XOR. . . . . . . . . . . . . . . . . . . . . . . . . . . . Realizacja bramek AND, OR , NOT za pomocą bramek NAND lub NOR. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ilustracja architektury współczesnego komputera. . . . . . . . Ilustracja zależności kosztów pamięci, ich pojemności i czasu dostępu. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Architektura procesora Intel 8086. . . . . . . . . . . . . . . . Rozmieszczenie flag w rejestrze flagowym. . . . . . . . . . . . Uproszczony schemat rozkazu procesora Intel 8086. . . . . . . Adresowanie natychmiastowe. . . . . . . . . . . . . . . . . . . Adresowanie bezpośrednie z rejestru. . . . . . . . . . . . . . . Adresowanie bezpośrednie z pamięci. . . . . . . . . . . . . . . Adresowanie pośrednie przez rejestr bazowy. . . . . . . . . . . Adresowanie pośrednie przez rejestr bazowy i offset. . . . . . Adresowanie przez rejestr bazowy i indeksowy. . . . . . . . . Adresowanie przez rejestr bazowy, indeksowy i offset. . . . . . 69 70 76 77 79 81 84 87 89 93 93 93 94 94 95 95

Przykładowe symbole stosowane na schematach blokowych. . 138 Schemat blokowy — fragment algorytmu znajdowania pierwiastków trójmianu. . . . . . . . . . . . . . . . . . . . . . . . 139 Instrukcja warunkowa if zapisana w postaci schematu blokowego. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

10

SPIS RYSUNKÓW

5.4 5.5 5.6 5.7 5.8 5.9 6.1 6.2 6.3 6.4 7.1 7.2 7.3 8.1 8.2 8.3 8.4 8.5 8.6 8.7 8.8 8.9 8.10

Pętla while i do--while zapisana w postaci schematu blokowego. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Pętla for zapisana w postaci schematu blokowego. . . . . . Ilustracja procesu sortowania przez wstawianie. . . . . . . . Ilustracja procesu przeszukiwania połówkowego. . . . . . . . Drzewo wywołań dla rekurencyjnego obliczania 5-ego wyrazu ciągu Fibonacciego. . . . . . . . . . . . . . . . . . . . . . . . Schemat blokowy pewnego algorytmu. . . . . . . . . . . . . Ilustracja procesu kompilacji. . . . . . . . . . . . . . . . . . Ilustracja procesu interpretacji. . . . . . . . . . . . . . . . . Ilustracja procesu prekompilacji i wykonania na maszynie wirtualnej. . . . . . . . . . . . . . . . . . . . . . . . . . . . . Fragment składni Pascala zapisany przy pomocy diagramów składni. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . .

141 142 145 147

. 152 . 155 . 179 . 181 . 182 . 186

Umiejscowienie Systemu Operacyjnego w systemie komputerowym. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 191 Stany procesu i przejścia pomiędzy nimi. . . . . . . . . . . . . 194 Wykorzystanie procesora przez dwa procesy. . . . . . . . . . . 195 Topologia magistrali. . . . . . . . . . Topologia pierścienia. . . . . . . . . Topologia gwiazdy. . . . . . . . . . . Topologia oczek pełnych. . . . . . . Porównanie transmisji pakietowej. . Ogólny schemat ramki. . . . . . . . . Kabel koncentryczny. . . . . . . . . . Skrętka. . . . . . . . . . . . . . . . . Światłowód. . . . . . . . . . . . . . . Ogólny schemat komputerów w sieci. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 218 219 219 220 225 225 230 230 231 233

E.1 Schamat blokowy algorytmu obliczającego potęgę. . . . . . . 253

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

Przedmowa
Pisanie jest jedyną sztuką, którą musimy się nauczyć pisząc. Anonim

Autorzy niniejszego podręcznika od roku 2000 prowadzą wykłady i ćwiczenia z przedmiotu „Wstęp do informatyki”. Przedmiot ten występuje na pierwszym roku studiów informatycznych na Wydziale Matematyki Uniwersytetu Łódzkiego. Jego celem jest zapoznanie studentów z historią informatyki, jej podstawowymi pojęciami i definicjami, oraz przybliżenie tego co dzieje się w obecnej informatyce i jakie dziedziny wchodzą jej skład. W czasie prowadzenia zajęć z przedmiotu Autorzy zauważyli lukę w literaturze informatycznej. Owszem istnieje wiele książek, czy publikacji skryptowych, które omawiają wszystkie poruszane problemy, jednak informacje i wiedza są zwyczajnie rozproszone w bardzo wielu pozycjach, wśród których trudno jednoznacznie wskazać najbardziej pomocną czy użyteczną 1 . Kiedy studenci często pytali skąd można uzyskać informację, Autorzy byli zmuszeni wskazywać dość pokaźną bibliografię. Działało to w pewien sposób odstraszająco, nawet nie z przyczyny ilości pozycji, co często niemożności ich otrzymania, a często również stopnia trudności w jaki sposób były one napisane 2 . Większość specjalistycznych pozycji, omawiających jakiś dział jest zbyt trudna dla osób dopiero poznających informatykę, lub wprowadza za dużo szczegółów, niepotrzebnie zaciemniających idee danego zagadnienia. Stąd też w 2001 roku zrodziła się potrzeba stworzenia podręcznika, który bardziej pasowałby do charakteru przedmiotu wprowadzającego w informatykę. Niniejsze opracowanie jest próbą przybliżenia obecnego zakresu dziedziny naukowej nazywanej Informatyka. Autorzy starają się wskazać czytelnikowi na charakter tej dyscypliny wiedzy, jej podstawowe pojęcia oraz główne gałęzie. I tak w kolejnych rozdziałach czytelnik zapozna się z różnymi fragmentami wiedzy informatycznej. Niektóre działy są potraktowane
1 2

Dowodem mnogości źródeł może być spis cytowanej bibliografii. Są to najczęsciej specjalistyczne ksiązki z danego działu.

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

12

SPIS RYSUNKÓW

bardzo dokładnie (dla przykładu 2), gdyż omawiają podstawowe pojęcia, które stanowią niejako alfabet informatyka. Inne zaś, dla przykładu sieci czy systemy operacyjne, są potraktowane bardziej powierzchownie, głównie w celu wprowadzenie pojęć i zasygnalizowania problematyki, kierunki te stanowią tematykę specjalizowanych wykładów na późniejszych latach studiów. Na samym wstępie umieszczono rozdział omawiający istotę i historię informatyki, oraz pokazano, iż rozwój informatyki jest w chwili obecnej ściśle związany z rozwojem komputerów. Opisano też główne dziedziny obecnej informatyki i wskazano, gdzie według autorów odbywa się obecnie najbardziej dynamiczny rozwój. W rozdziale 2 Czytelnik zapozna się z niezbędnymi definicjami i informacjami dotyczącymi systemów liczbowych, kodów i pojęć stosowanych w komputerach. Rozdział ten jest w części związanej z algebrą Boole’a teoretyczny i zawiera definicje oraz podstawowe twierdzenia tych algebr. Starano się jednak opisać to wszystko w sposób maksymalnie uproszczony i przejrzysty, zdając sobie sprawę, że nie każdy czytelnik ma odpowiednie przygotowanie matematyczne. Należy jednak podkreślić, że algebra Boole’a jest bardzo ważnym elementem, bynajmniej nie tylko teoretyczny, szczególnie przydaje się w późniejszej praktyce upraszczanie wyrażań algebraicznych. W rozdziale 3 została omówiona budowa dzisiejszego komputera i zasada jego działania. Przy czym szczególny nacisk położono na ideę konstrukcji od strony koncepcji nie zaś technologii. Stąd w pierwszej kolejności omówiono Maszynę Turinga, która stanowiła jeden z elementów, który posłużył von Neumannowi do stworzenia idei maszyny von neumannowskiej. Poza tym sama Maszyna Turinga stanowi ważną konstrukcję myślową, która służy później do badań w teorii języków formalnych i automatów skończonych. W dalszym ciągu w rozdziale tym omówiono elementarną budowę mikroprocesora na przykładzie procesora INTEL 8086, wraz z sygnalizacją takich pojęć jak rejestry, kod maszynowy czy asembler. Wyjaśniono również w jaki sposób jest reprezentowana informacja w komputerze, zatem została omówiona reprezentacja w kodzie uzupełnień do dwu, reprezentacja zmiennoprzecinkowa, kod ASCII czy też UNICODE. Rozdział 4 stanowi wprowadzenie w zagadnienia teorii informacji. Ponieważ sama ta teoria jest trudna i wymagająca świetnej znajomości rachunku prawdopodobieństwa, stąd starano się przedstawić tylko najważniejsze fakty, wyrabiając w czytelniku pewną intuicję, pomijając zaś niepotrzebne szczegóły techniczne, czy twierdzenia. W dalszej części pokazano jak pewne elementy związane z teorią informacji wykorzystuje się na codzień
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

SPIS RYSUNKÓW

13

w praktyce informatyka. Rozdział 5 zawiera wprowadzenie do algorytmów i struktur danych. Dział ten jest przedmiotem osobnego wykładu na studiach informatycznych. Autorzy omówili podstawowe pojęcie tego przedmiotu i opisali te elementy, co do których później zakłada się, że student je zna. Są to sposoby zapisu algorytmów, za pomocą schematu blokowego, czy też pseudo-języka. W rozdziale 6 czytelnik znajdzie informacje o historii i rozwoju języków programowania, oraz ich własnościach i próbach klasyfikacji. Języki programowania jak i samo programowanie stanowią jeden z głównych działów informatyki, bez którego nie istniały by programy, a z pewnością nie pisano by ich tak szybko i efektywnie jak to się dzieje w chwili obecnej. Stąd Autorzy starają się wprowadzić pewną wiedzą związaną z tym, czym są języki programowania, co spowodowało powstanie tak wielkiej ich ilości, oraz w jaki sposób można je pogrupować i które z nich wykorzystać w określonych sytuacjach. Rozdział 7 zawiera omówienie podstawowych własności systemu operacyjnego. Podział współczesnych systemów operacyjnych na kategorie, a na końcu krótkie omówienie współczesnych systemów operacyjnych, głownie w ujęciu historycznym. System operacyjny to zaraz po sprzęcie najważniejszy element komputera, niejednokrotnie decydujący o jego sprawności i użyteczności. Stąd Autorzy starali się omówić różnice pomiędzy systemami operacyjnymi na poziomie ich budowy, unikając odniesień, tam gdzie tylko to możliwe, do rzeczywiście istniejących systemów. Główny powód takiego postępowania, to niechęć do narzucania własnych poglądów, czy też upodobań, oraz próba uniknięcia jakiejkolwiek formy reklamy. Stąd starano się skoncentrować na wskazaniu gdzie i jakie cechy danego systemu mogą być użyteczne, a gdzie można się bez nich obejść. Ostatni rozdział 8 omawia zagadnienia związane z sieciami komputerowymi. Opisany jest podział ze względu fizyczną i logiczną strukturę sieci, wprowadzone są podstawowe pojęci i jednostki. We współczesnym świecie większość ludzi nie wyobraża sobie funkcjonowania bez komputera i to komputera podłączonego do Internetu. A Internet to nic innego jak jeden z aspektów wykorzystania sieci rozległych, wszelkie jego usługi, jak poczta czy WWW działają wyłącznie w oparciu o transmisję danych poprzez sieć. W rozdziale tym starano się przybliżyć problematykę sieci, zwrócić uwagę na wady i zalety pewnych technologii czy sposobów konfiguracji sieci. Na końcu podręcznika znajduje się zbiór dodatków, które mają być rodzajem encyklopedycznego poradnika, po który zawsze łatwo sięgnąć. Dołączono również obszerny indeks, który ma ułatwić poruszanie się po całości
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

14

SPIS RYSUNKÓW

pracy. Udostępniamy to opracowanie w nadziei, że ułatwi ono uporządkowanie wiadomości z zakresu wykładu „Wstęp do informatyki” dla tych osób, które takiego wykładu mogły wysłuchać. Pozostałe osoby, mamy nadzieję, również mogą skorzystać z tego podręcznika, szczególnie jeśli są zainteresowane omawianą problematyką i nie boją się uczyć indywidualnie. Zainteresowanych czytelników zachęcamy do poszukiwania dalszych informacji wymienionych w pozycjach bibliografii. Nawet jeśli któraś z nich nie jest ściśle związana z omawianymi zagadnieniami, to umożliwi poszerzenie własnych horyzontów myślowych, pozwalając na samodzielny rozwój. Ponieważ zdajemy sobie sprawę z własnej omylności, stąd informujemy, że wszelkie poprawki do niniejszego wydania, jak i ewentualne uaktualnienia, znajdą się na stronie internetowej: http://???/ Jeśli ktokolwiek z grona znajdzie jakikolwiek błąd, niejasności czy też będzie miał sugestie prosimy o kontakt za pomocą poczty elektronicznej na adresy: fulmanp@math.uni.lodz.pl scibor@math.uni.lodz.pl Na koniec chcielibyśmy podziękować tym wszystkim, którzy pomogli nadać podręcznikowi obecną jego formę, bądź to poprzez motywację, bądź ciepliwie czytając pierwsze wersje i nanosząc poprawki. I tak Profesorowi Andrzejowi Nowakowskimu za to, że powiedział: „Piszcie ten skrypt”, naszym koleżankom i kolegom: Oli Orpel, Jadwidze Nowak, Jankowi Pustelnikowi, Grześkowi Oleksikowi za czytanie i nanoszenie poprawek. Kilkunastu studentom, którzy zgłosili błędy zauważone we wcześniejszych wersjach. Oraz na końcu, lecz nie oznacza to, że z mniejszą wdzięcznością, Michałowi Kędzierskiemu, za cierpliwe czytanie i poprawianie błędów językowych, stylistycznych i logicznych, dzięki czemu prawdopodobnie ten podręcznik w ogóle daje się czytać. Piotr Fulmański, Ścibór Sobieski

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

Rozdział 1

Wstęp
Wykład to przenoszenie wiadomości z notatek wykładowcy do notatek ucznia — z pominięciem mózgów ich obu. Pokolenia uczniów

1.1

Czym jest informatyka?

Podobnie można zapytać o matematykę, filozofię, czy biologię, a pytanie o to, czym jest dana dyscyplina zdaje się być podstawowym w każdej dziedzinie nauki, jaką człowiek uprawia. Wydaje się jednak, że w przypadku informatyki odpowiedź nie jest tak oczywista. Z pozoru można sądzić, że informatyka to nauka o komputerach. Jednak odpowiedź ta zawiera w sobie tylko część prawdy. Owszem trudno sobie wyobrazić dzisiejszą informatykę bez komputera, ale czy to znaczy, że bez niego informatyka nie mogła by istnieć jako dyscyplina wiedzy? Nic bardziej błędnego, komputer jest tylko narzędziem, dzięki któremu informatyka nabrała tak ogromnego znaczenia i stała się tak bardzo użyteczna. Informatyka to dziedzina, która zajmuje się przetwarzaniem informacji za pomocą pewnych schematów postępowania (zwanych algorytmami). Wynikiem takiego procesu jest znów informacja. Można te rozważania w łatwy sposób zilustrować na przykładzie prostego programu słownika polsko–angielskiego. Użytkownik wpisuje słowo po polsku (informacja wejściowa), prosi o wykonanie tłumaczenia (algorytm), w efekcie otrzymuje odpowiednik lub odpowiedniki w języku angielskim (informacja wyjściowa). Jeśli w powyższym przykładzie nie wspomnimy, że chodzi o program tylko o zwykły drukowany słownik, to czy to jest informatyka? W pewien sposób tak, gdyż człowiek wykonujący czynność wyszukiwania odpowiednika słowa w języku obcym, świadomie i nieświadomie
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

16

Wstęp

wykonuje pewien proces (algorytm) poszukiwania tego słowa (świadomie — gdyż bez udziału świadomości niczego nie znajdzie, nieświadomie — gdyż nie zdaje sobie sprawy, że wykonuje algorytm). A informatyka zajmuje się między innymi tworzeniem i badaniem takich algorytmów. Czy to oznacza, że jesteśmy komputerem? Oczywiście nie. Jednak ten przykład pokazuje, że komputer jest tylko narzędziem wspomagającym informatykę i informatyków. Ale nie oznacza to, że jest on niezbędny (chodź są procesy, które bez komputera byłyby nie wykonalne), czy też jedyny. Z pewnością trudno by było mówić o informatyce w dzisiejszej formie bez komputerów. Wynika to przede wszystkim z faktu, że informatycy do przetwarzania informacji używają coraz to bardziej skomplikowanych algorytmów, które by były wykonane w rozsądnym czasie potrzebują coraz szybszych komputerów. Kolejnym ważnym aspektem informatyki jest przechowywanie ogromnych ilości informacji, zwykle w celu późniejszego jej wyszukania, czy też przeanalizowania1 . I tu znów z pomocą przychodzą komputery i elektronika, która w obecnej chwili pozwala nam na magazynowanie coraz to większej ilości danych w coraz to mniejszych nośnikach informacji (CD, DVD, etc.). Obecnie tak bardzo utożsamia się informatykę i komputer, że nawet angielskie określenie informatyki „Computer science”, informuje, że jest to „nauka o komputerach”, jednak właściwszym terminem byłaby „nauka o przetwarzaniu informacji”. Niewątpliwie najbardziej intensywny rozwój informatyki przypadł na ostatnie kilkadziesiąt powojennych lat. Spowodowane to było przede wszystkim powstaniem i bardzo szybkim rozwojem elektroniki, w szczególności elektroniki cyfrowej. Rozwój technologii i w konsekwencji miniaturyzacja układów cyfrowych umożliwił powstanie komputerów osobistych posiadających dużą moc obliczeniową. Jednak czynności związane z pobieraniem, przechowywaniem i przetwarzaniem informacji, człowiek wykonywał od bardzo dawna. Możliwości mechanizacji tych prac, a później ich automatyzacji pojawiały się stopniowo w miarę konstruowania i wytwarzania odpowiednich urządzeń, co było związane właśnie z rozwojem techniki i technologii. W tej sytuacji przestaje dziwić fakt, że informatyka jest w dużej mierze „nauką o komputerach”, gdyż bez nich nie była by ona tym, czym jest w chwili obecnej. Potwierdza to nawet datowanie początków informatyki jako dziedziny naukowej, które umieszcza się na początek lat pięćdziesiątych, kiedy to powstawały pierwsze komputery elektronowe.
Wygenerowanie raportu sprzedaży za ostatni miesiąc w programie magazynowo-sprzedażowym jest przykładem informacji, która została przetworzona (przeanalizowana).
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004 1

1.2 Historia informatyki

17

Spróbujmy zatem przybliżyć dokładniej zakres dawnej i dzisiejszej informatyki.

1.2

Historia informatyki
Historia to koszmar, z którego próbuję się obudzić. James Joyce Gdybyśmy lepiej znali historię, u źródła każdej innowacji odkrylibyśmy jakiś wielki umysł. Emile Mal´ e

Pytanie o początki informatyki jest, podobnie jak pytanie o jej istotę, pytaniem trudnym, głównie ze względu na wcześniej wspomniane trudności ze zdefiniowaniem zakresu informatyki. W tym rysie historycznym przyjęto, że początek informatyki to początek rozwoju myśli ludzkiej związanej z obliczaniem jak i próbami automatyzacji procesów obliczeniowych. Takie założenie wynika z faktu ścisłego związania informatyki z maszynami liczącymi, dzisiejszymi komputerami. Zatem początkowy rozwój informatyki będzie pokrywał się, w głównej mierze, z jednej strony z początkami pierwszych algorytmów (czyli procedur przetwarzania informacji), z drugiej zaś z rozwojem przyrządów i urządzeń wspomagających obliczenia. Rys historyczny zastał ułożony chronologicznie, konsekwencją takiego założenie jest przeplatanie się faktów z rozwoju urządzeń obliczeniowych oraz pewnych koncepcji myśli informatycznej.

1.2.1

Bardzo dawno temu ...

Około 3000 lat p.n.e.2 ludy sumeryjskie wykorzystywały do obliczeń gliniane tabliczki z wyżłobionymi rowkami do których wkładano kamyki. Około 2600 p.n.e. Chińczycy skonstruowali Abakus: drewnianą tabliczkę podzieloną na kolumny. Każda kolumna reprezentowała pozycję jednej cyfry: pierwsza — jednostki, druga — dziesiątki, itd. W każdej kolumnie do wydrążonych
Nie należy tej daty utożsamiać z narodzinami liczenia, czy inaczej nazywając zliczania. Okazuje się bowiem, że w 1937 roku w miejscowości Dolni Vestonice na Morawach znaleziono kość wilka, która zawiera 57 nacięć pogrupowanych po pięć. Można przypuszczać, że należała ona do osoby, która w ten sposób coś zliczała, nie było by w tym nic dziwnego, gdyby nie fakt, że datuje się to znalezisko na około 30 tysięcy lat p.n.e.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004 2

18

Wstęp

rowków wkładano kamyki (maksymalnie 9) lub kościane gałki oznaczone cyframi od 1 do 93 . Udoskonaleniem tego przyrządu były późniejsze liczydła. Wykonywanie działań przy pomocy Abakusa polegało na odpowiednim przesuwaniu kamyków. Chcąc na przykład do 31 dodać 23 należało: 1. w kolumnie jedności położyć 1 kamyczek, 2. w kolumnie dziesiątek położyć 3 kamyczki, 3. do kolumny jedności dołożyć 3 kamyczki, 4. do kolumny dziesiątek dołożyć 2 kamyczki, 5. policzyć ilość kamyczków w kolumnie jedności i kolumnie dziesiątek. Procedura ta wydaje się poprawna, ale zauważmy, że jeśli chcielibyśmy do 18 dodać 5 to napotkamy na problem. W kolumnie jedności mamy bowiem już 8 kamyków i należałoby dodać jeszcze 5. Tego zrobić jednak nie można, gdyż każda kolumna może zawierać maksymalnie 9 kamyków. Aby rozwiązać ten problem należało w pamięci dodać 8 do 5, w wyniku otrzymamy 13, zatem w kolumnie jedności pozostawiamy 3 kamyki a do następnej kolumny dodajemy 1. Ten sposób rozwiązania problemu nosił nazwę dziesiątkowania. Pomiędzy 400 a 300 rokiem p.n.e. wielki grecki matematyk i filozof Euklides, wymyślił pierwszy znany nam nietrywialny algorytm, czyli przepis na realizację zadania. Był to algorytm znajdowania największego wspólnego dzielnika dwóch dodatnich liczb całkowitych. W tym miejscu należy zauważyć, iż Euklides wymyślając ten sposób obliczania największego wspólnego dzielnika nie miał pojęcia, że wymyśla „algorytm”. A to dlatego, że słowo algorytm pochodzi od nazwiska matematyka arabskiego, który żył na przełomie VIII i IX wieku naszej ery. Matematyk ten nazywał się Muhammad ibn Musa al-Chorezmi (spotykana też pisownia al-Khawarizmy), zasłużył się zaś stworzeniem kilku dzieł z dziedziny matematyki, w których opisał dużą ilość reguł matematycznych (w tym dodawania, odejmowania, mnożenia i dzielenia zwykłych liczb dziesiętnych). Opis tych procedur był na tyle precyzyjny i formalny, jak na tamte czasy, że właśnie od jego nazwiska pochodzi słowo algorytm.
Należy pamiętać, że Chińczycy nie znali cyfr arabskich, którymi dzisiaj się posługujemy, gdyż zostały one wprowadzone znacznie później.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004 3

1.2 Historia informatyki

19

1.2.2

Ostatnie tysiąclecie

Na początku XVII wieku naszej ery John Neper (1550-1617) opublikował swoje dzieło o logarytmach oraz opisał przyrząd wspomagający mnożenie za pomocą logarytmów. Przyrząd ten zwany był pałeczkami Nepera. Idea działania była bardzo prosta, lecz w tamtych czasach rewolucyjna. Polegała na tym, że mnożenie sprowadzano do serii dodawań. Pomysł ten jest wykorzystywany do dzisiejszych czasów. W tym samym czasie żył Wilhelm Schickard (1592-1635), który jest uznawany za twórcę pierwszej mechanicznej maszyny liczącej. Opisał on model maszyny czterodziałaniowej wykorzystującej pałeczki Nepera wykonane w postaci walców. W tych czasach twórcy maszyn liczących ponownie natrafili na problem dziesiątkowania, który, przypomnijmy, pojawił się 38 wieków wcześniej! Tym razem problem ten został rozwiązany w sposób mechaniczny, a poradził sobie z nim francuski matematyk Blaise Pascal (1623-1662), budując w 1642 sumator arytmetyczny. Była to maszyna o kilku kołach zębatych (10 zębów z numerami od 0 do 9). Chcąc wykonać operacje ustawiało się koła na odpowiednią wartość początkową (na przykład koło dziesiątek na 1, jedności na 8), a następnie przestawiało się odpowiednie koła wymaganą ilość razy (na przykład jedności o 5). W momencie, gdy jedno z kół obracało się z pozycji 9 na następną, jaką jest 0, powodowało ono również obrót kolejnego koła o jedną pozycje. Tym oto sposobem uporano się z zagadnieniem automatycznego uwzględniania przeniesienia na następną pozycję dziesiętną. Pascal budował swoje maszyny, zwane Pascalinami, z myślą o swoim ojcu, który był poborcą podatkowym. Maszyny te, wyprodukowano ich około 50 egzemplarzy, były głównie przeznaczone do obliczeń w różnych systemach monetarnych, kilka z nich przystosowanych było do obliczeń odległości i powierzchni i były przeznaczone dla geodetów. Zarówno Schickard jak i Pascal, skonstruowali tak swoje maszyny liczące, by potrafiły automatycznie przenosić cyfry przy dodawaniu czy odejmowaniu. Niedługo po skonstruowaniu sumatora arytmetycznego urodził się kolejny wielki matematyk Gottfried Wilhelm Leibniz (1646-1716), który to ponownie odkrył system dwójkowy, ponownie, gdyż pierwsi byli Chińczycy. System dwójkowy jest obecnie podstawowym systemem do reprezentacji wszelkiej informacji (liczb, liter i innych znaków) w dzisiejszych komputerach. W początkach wieku XIX angielski matematyk Charles Babbage (17911871) skonstruował maszynę różnicową, która miała obliczać określone
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

20

Wstęp

Rysunek 1.1: Ilustracja idei metody różnicowej

równania matematyczne. Zadaniem tej maszyny miało być wyręczenie człowieka od żmudnych i powtarzających się czynności. Podstawą działania tej maszyny była metoda różnicowa. Wytłumaczymy tę metodę na przykładzie. Spójrzmy na rysunek 1.1, ilustruje on pewną własność matematyczną. Własność ta mówi o tym, że potęga kwadratowa dowolnej liczby całkowitej wyraża się jako suma potęgi kwadratowej liczby całkowitej ją poprzedzającej oraz kolejnej liczby nieparzystej. Spójrzmy na rysunek i rozważmy potęgę kwadratową liczby 3, wiemy że wynosi ona 9, ale z zacytowanej własności wynika, że jest ona sumą kwadratu liczby ją poprzedzającej, a zatem 22 = 4 oraz liczby 5. Jeśli teraz weźmiemy liczbę dowolną to wykorzystując tę własność oraz fakt, że możemy tę metodę zastosować w sposób zstępujący (rekurencyjny zob. 5.5), to zamiast wykonywać żmudne mnożenie możemy wykonywać sumowanie, które nawet w dzisiejszych komputerach jest tańsze od dodawania4 (zob. 3.4). Babbage wykorzystując podobne własności pomijał pewne niedogodności, a co za tym idzie przyśpieszał obliczenia. Istotną różnicą maszyny różnicowej w porównaniu do maszyn Pascala, było to, że po nastawieniu danych początkowych wszelkie dalsze obliczenia odbywały się automatycznie bez udziału człowieka, za wyjątkiem samego faktu napędzania maszyny. Babbage jest obecnie uważamy za najwybitniejszego twórcę maszyn liczących, żyjącego przed nadejściem maszyn elektronicznych. Sławę tę zawdzięcza głównie dzięki swojemu kolejnemu pomysłowi, jakim było stworzenie modelu i próba realizacji maszyny analitycznej. Maszyna ta miała składać się z magazynu (dzisiejszy odpowiednik pamięci), młyna (jednostka
Mówiąc „tańsze” w tym miejscu, mamy na myśli wymagające mniejszego zaangażowania komputera.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004 4

1.2 Historia informatyki

21

Młyn

"Programowanie"

Rysunek 1.2: Schemat maszyny analitycznej

licząca) i mechanizmu sterującego (jednostka sterująca). Pamięć miała służyć do przechowywania danych oraz wyników z przeprowadzanych na nich operacji. Młyn, odpowiednik dzisiejszej jednostki arytmetyczno–logicznej (zob. 3.4), wykonywać miał proste działania arytmetyczne. Działaniem całego urządzenia miał kierować mechanizm sterujący, który miał być w idei programowany. Bardzo ciekawym pomysłem była koncepcja wykorzystania do programowania mechanizmu sterującego kart perforowanych (kartoniki papieru z dziurkami). Pomysł ten został zapożyczony od Jacquarda, który kilka lat wcześniej skonstruował krosna sterowane za pomocą takich kart5 . Zwróćmy uwagę, że aby maszyna analityczna mogła być programowana musiała posiadać nie tylko jednostkę sterującą, ale również pamięć, której rolę pełnił magazyn. To właśnie w pamięci przechowywano wyniki obliczeń pośrednich, dzięki czemu można był wykonywać na tej maszynie więcej niż proste działania, można było wykonywać sekwencje działań. Niestety twórcy idei nie udało się skonstruować kompletnej i działającej maszyny analitycznej. Ale opisy były na tyle dokładne i kompletne, że gdy dostały się w ręce Ady Augusty hrabiny Lovelace, córki Byrona, zainspirowały ją do obmyślenia pierwszych programów dla tej, nie istniejącej, maszyny. Prawdą jest, że były to raczej opisy procedur obliczeniowych niż
Dokładniej wzór tkany przez te krosna zależał od „programu” zawartego na tych kartach.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004 5 

    ¡ © § 

 © § ¥ ¤ £ ¡ ¦¢¨¦¢¢¢  

© ¦( ! '%¡ #! & $ "
Sterowanie

Dane do

22

Wstęp

programy w dzisiejszym tego słowa znaczeniu, ale w niczym nie umniejsza to zasług Pani hrabiny, która stała się dzięki temu pierwszą programistką na świecie, a mówiąc ogólnie o zawodzie pierwszym programistą w ogóle. Jej zasługi zostały uhonorowane nadaniem jej imienia jednemu z najbardziej uniwersalnych i zaawansowanych obecnie języków programowania jakim jest ADA. Kolejnego kroku w dziedzinie przetwarzania informacji dokonał Herman Hollerith, który był pracownikiem amerykańskiego biura statystycznego. Impulsem do działania stały się dla niego doświadczenia z opracowywania wyników spisu powszechnego przeprowadzonego w USA w roku 1880 – operacja ta trwała 7 lat. Najbardziej czasochłonną czynnością okazało się wtedy grupowanie i liczenie jednostek charakteryzujących się określonymi cechami. Skonstruowano zatem maszynę, która automatycznie odczytywała, porządkowała i grupowała dane według określonych kryteriów. Do wprowadzania danych do maszyny użyto specjalnych kart perforowanych. Występowanie danej cechy zaznaczano otworkiem w odpowiednim miejscu karty. Wydziurkowane karty umieszczano w matrycy, pod którą znajdowały się pojemniki z rtęcią. Do każdego pojemnika doprowadzano prąd elektryczny, następnie opuszczano na kratę płytę z kołeczkami. W miejscach, gdzie były otwory, następowało zamknięcie obwodu (kołeczki dotykały rtęci) i uruchomienie licznika, których zastosowano w sumie 40. Urządzenie to pozwoliło 8-krotnie przyspieszyć prace obliczeniowe związane ze spisem powszechnym w roku 1890. Zadowolony z odniesionego sukcesu Hollerith założył w 1896 przedsiębiorstwo Tabulating Machine Company, które w 1917 przekształciło się w International Business Machine, czyli dzisiejszą firmę IBM. Po sukcesach Hollerith’a powstało jeszcze kilka firm, które wykorzystywały do sterowania maszyn obliczeniowych karty perforowane, istniejące do dziś to Bull, NCR, Bell. Popularność kart perforowanych doprowadziła do ich znormalizowania, co z kolei spowodowało, że stały się one przez wiele dziesięcioleci uniwersalnym nośnikiem informacji i to zarówno w sensie danych do obliczeń, zapisywania programów, jak i ostatecznie pamiętania wyników tych programów.

1.2.3

Wiek XX

Jedną z najbardziej znanych i zasłużonych postaci historii informatyki jest Alan Turing (1912-1954). Swoją sławę zawdzięcza opublikowaniu w 1936 roku pracy opisującej tok myślenia prowadzący od obliczeń ręcznych do
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

1.2 Historia informatyki

23

obliczeń zautomatyzowanych, wykonywanych przez bardzo prostą maszynę, zwaną maszyną Turinga. Z uwagi na wielkie znaczenie tej maszyny dla informatyki teoretycznej poświęcimy jej więcej miejsca w punkcie 3.1. Od czasu drugiej wojny światowej, włączając w to kilka lat wcześniejszych, możemy mówić już o rozwoju technologii elektronicznej i co za tym idzie rozwoju oraz miniaturyzacji komputerów. W 1936 roku w Niemczech, Konrad Zuse tworzy maszynę Z1, następnie Z2, a w końcu w 1941 roku maszynę Z3. Zadaniem tych wszystkich maszyn były obliczenia dla potrzeb militarnych. Obliczenia te były wykonywane na liczbach zapisanych w systemie dwójkowym (zob. 2.2.1) w reprezentacji dziś zwanej zmiennoprzecinkową (zob. 3.5.5). Sterowanie odbywało się za pomocą programu zapisanego na perforowanej taśmie filmowej. Budowę tych maszyn charakteryzował brak części mechanicznych, występowały natomiast przekaźniki elektromagnetyczne. Ostatni model maszyny Zusego Z4 przetrwał i pracował, aż do końca lat pięćdziesiątych. W 1937 rozpoczyna pracę zespół konstruktorów złożony z pracowników firmy IBM, kierowany przez Howarda Aikena. Wynikiem ich prac było zbudowanie w 1944 pierwszej automatycznej maszyny liczącej. Programowanie tej maszyny polegało na odpowiednim łączeniu kabelkami gniazd w specjalnej tablicy sterującej. Dane wprowadzano za pomocą kart dziurkowanych, wyniki wyprowadzano na taśmę perforowaną lub drukowano za pomocą elektrycznych maszyn do pisania. MARK I miał długość 17 metrów, wysokość 3 metry. Składał się z 760 tysięcy części w tym z 17480 lamp elektronowych. Obsługę stanowiło 10 osób. Wykonywał 3.5 dodawania na sekundę oraz 1 dzielenie na 11 sekund. Częstotliwość pracy wynosiła 100 kHz. Szacowano, iż zastępuje on pracę 100 rachmistrzów wyposażonych w arytmometr mechaniczny. W 1942 zespół specjalistów pod kierunkiem J.W. Mauchley’ego i J.P. Eckert’a projektuje i buduje maszynę ENIAC (ang. Electronic Numerical Integrator And Computer ). Jest to pierwsza maszyna w której użyto wyłącznie elementy elektroniczne (lampy elektronowych), i uznawana jest powszechnie za pierwszy kalkulator elektroniczny. Dopiero w 1976 roku okazało się, że przed nią zostały uruchomione w Anglii maszyny Colos I i Colos II. Programowanie ENIAC’a polegało na ręcznym ustawianiu przełączników oraz wymianie specjalnych tablic programowych. Długość komputera wynosiła 15 metrów, jego szerokość to 9 metrów, waga 30 ton, składał się z około 18000 lamp elektronowych. Liczby były pamiętane w systemie dziesiętnym, był on w stanie wykonać 5000 dodawań na sekundę, oraz od 50 do 360 dzieleń na sekundę.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

24

Wstęp

W 1946 roku do projektu EDVAC (ang. Electronic Discrete Variable Automatic Computer ) przyłącza się John von Neumann (1903-1957), który inspiruje uczestników projektu do budowy komputera w którym wyeliminowane byłyby wady poprzednika. Do najważniejszych z nich należy zaliczyć wysoką awaryjność, oraz dużą uciążliwość procesu programowania. Neumann zaproponował architekturę, według której są budowane komputery do dnia dzisiejszego, została ona nazwana von neumannowską. Architektura ta była wynikiem pracy Johna von Neumann’a nad problemem przechowywania w pamięci komputera zarówno danych podlegających przetwarzaniu, jak i programu, który na tych danych miał działać. Prace te umożliwiły odejście od sztywnych metod programowania sprzętowego (przełączanie kabelków, czy zworek) i zastąpienie ich programowaniem wewnętrznym, poprzez umieszczenie w pamięci maszyny programu sterującego przetwarzaniem danych. Architektura von neumannowska wyróżniała następujące elementy składowe: pamięć złożona z elementów przyjmujących stany 0 i 1, arytmometr wykonujący działania arytmetyczno–logiczne, jednostki sterującej. Sterowanie odbywało się za pomocą programu, który był umieszczany w pamięci. Stanowiło to duży skok ideowy w stosunku do wcześniejszych koncepcji, w których program był zapisywany na kartach perforowanych i bezpośrednio z nich odczytywany oraz uruchamiany. W maszynie von neumannowskiej program jak i dane znajdował się w pamięci fizycznej, sam program mógł modyfikować zawartość pamięci fizycznej, a co za tym idzie mógł sam się modyfikować. Program składał się z ciągu instrukcji, które były pobierane i rozpoznawane przez jednostkę sterującą w takt zegara taktującego pracą całego komputera. Instrukcje te musiały odpowiadać poleceniom zakodowanym przez twórców układu elektronicznego. Taka idea powodowała, że nie było już różnicy pomiędzy danymi a rozkazami, wszystkie one były kodowane za pomocą systemu binarnego. Widać bardzo duże podobieństwo do budowy maszyny Turinga (zob. 3.1), co nie powinno dziwić, gdyż Neumann znał doskonale wyniki Turinga. Maszyną skonstruowaną w oparciu o tę teorię był również EDSAC (1949, Anglia). Pierwszą maszyną zastosowaną do przetwarzania danych cywilnych był komputer UNIVAC, za pomocą którego podobno przewidziano zwycięstwo D. Eisenhowera w wyborach prezydenckich w 1952 roku. Mówiąc o rozwoju komputerów trudno nie wspomnieć o początkach komputerów osobistych. Pierwszym komputerem tej klasy był Altair wyprodukowany w 1975 roku przez firmę MITS. Wyposażony w 8-bitowy procesor Intel 8080 oraz 256 bajtów pamięci, pozbawiony klawiatury, monitora, nac 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

1.2 Historia informatyki

25

pędu taśmowego, stał się niezwykle popularny wśród osób zajmujących się do tej pory elektroniką. Młody Bill Gates napisał dla niego język BASIC (ang. Beginner’s All Purpose Symbolic Instruction Code). Mimo wielu ograniczeń Altair zyskał ogromną popularność czego konsekwencją było powstawanie coraz większej ilości firm produkujących tego rodzaju „zabawki” — tak wówczas nazywano te urządzenia i tak o nich myślano. Praktycznie wszystkie one działały pod kontrolą systemu operacyjnego nazywanego CP/M (ang. Control Program/Monitor lub Control Program for Microcomputer) i wyprodukowanego przez małą kalifornijską firmę Digital Research. Na skutek dużego zainteresowania rynku urządzeniami takiego typu, powstają produkowane przez IBM komputery PC (ang. Personal Computer), od których wzięła się nazwa jakby klasy — mówimy dziś „komputer PC” lub „komputer klasy PC”. Należy tu jednak oddać sprawiedliwość i powiedzieć, że przed komputerem firmy IBM, powstał inny o nazwie komputer Apple II, który był pierwszy z całej gamy „ jabłuszek” do dziś znanych i bardzo popularnych choć głównie na rynku amerykańskim. Co ciekawe komputery Apple a później Macintosh dużo wcześniej od komputerów PC posiadały interfejs graficzny, bo od roku 1985. Inna też była architektura: IBM PC były i do dziś są oparte o rodzinę procesorów firmy INTEL 6 , zaś Apple przez długie lata był związany z rodziną procesorów Motoroli, a obecnie także PowerPC. Na koniec tego krótkiego przeglądu wydarzeń z historii informatyki przedstawiamy zestawienie wybranych dat z historii rozwoju komputerów obecnej generacji opartych na krzemie. 1947 wynalezienie tranzystora — pierwszego i podstawowego składnika elektroniki cyfrowej i analogowej; 1958 wynalezienie układu scalonego — jest to układ, który zawiera w sobie tranzystory zamknięte w jednej obudowie i realizujące pewne konkretne funkcje; 1964 komputer IBM S/360 — pierwszy superkomputer zwany do dziś Mainframe; 1964 graficzny interfejs użytkowy i mysz; 1971 INTEL 4004 — zawierał 2.3 tys. tranzystorów, był taktowany zegarem 740 kHz, mógł zaadresować 1 kB pamięci dla danych oraz 4kB pamięci programu;
6

Lub procesorów innych firm, ale kompatybilnych z procesorami INTEL, np. AMD.

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

26

Wstęp

1972 INTEL 8008 — zawierał 3.5 tys. tranzystorów, mógł zaadresować do 16 kB RAM; 1974 INTEL 8080 — zawierał 4.8 tys. tranzystorów, mógł zaadresować do 64 kB RAM, lista poleceń składała się z 75 rozkazów; 1975 Altair 8800 – pierwszy komputer domowy oparty na procesorze INTEL 8080, posiadał 256 bajtów RAM; 1976 procesor Zilog Z80 – modyfikacja INTEL 8080, lista poleceń zawierała 176 rozkazów, prędkość zegara wynosiła 4 MHz; 1976 procesor INTEL 8086 i 8088; 1977 komputer Apple II; 1979 procesor Motorola 68000; 1981 komputer IBM PC — pierwszy komputer rozpoczynający całą rodzinę istniejących do dziś komputerów osobistych (ang. Personal Computer), był oparty na procesorze 8088, posiadał 64 kB RAM; 1982 procesor INTEL 80286 — zawierał 134 tys. tranzystorów, mógł zaadresować do 16 MB RAM, był taktowany zegarem 6 MHz; 1983 komputer PC XT — oparty na procesorze INTEL 8086; 1984 komputer PC AT — oparty na procesorze INTEL 80286; 1985 procesor INTEL 80386 — zawierał 275 tys. tranzystorów, był taktowany zegarem 16 MHz; 1989 procesor INTEL 80486 — zawierał 1.18 mln tranzystorów, był taktowany zegarem 25 MHz; 1992 procesor Power PC — zawierał 2,8 mln tranzystorów, początkowo był taktowany zegarem 66 MHz; 1993 procesor INTEL Pentium — zawierał 3.1 mln tranzystorów, początkowo był taktowany zegarem 60 MHz; 1993 procesor DEC Alpha — zawierał 1.7 mln tranzystorów, 300 MHz; 1995 procesor INTEL Pentium Pro — 5.5 mln tranzystorów, był taktowany zegarem 200 MHz;
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

1.3 Zastosowanie i przyszłość

27

1996 procesor INTEL Pentium MMX; 1997 procesor INTEL Pentium II — zawierał 7.5 mln tranzystorów, początkowo był taktowany zegarem 300 MHz; 1999 procesor INTEL Pentium III — zawierał 9.9 mln tranzystorów, początkowo był taktowany zegarem 600 MHz; Tym oto sposobem proste urządzenie wspomagające obliczenia, na przestrzeni wieków przekształciło się w urządzenie wspomagające przetwarzanie danych, a często też służące rozrywce. Z pewnością dzisiejszy komputer jest bardziej złożony technologicznie od liczydła, lecz jego rola pozostała w dużej części niezmienna: jego zadaniem jest przetwarzanie danych, nawet jeśli tymi danymi będą kolejne plansze gry.

1.3

Zastosowanie i przyszłość
A choć początki twoje były liche, ostatki świetne będą. Księga Hioba (przekład Cz. Miłosza)

Obecnie coraz trudniej wyobrazić sobie jakąkolwiek dziedzinę życia bez choćby najmniejszej obecności komputera, a w przyszłości sytuacja ta się jeszcze bardziej nasili. Wystarczy wymienić niektóre tylko dziedziny oraz zastosowanie tam informatyki: • Nauka – Nauki ścisłe - narzędzie do wspomagania obliczeń, edytor prac naukowych, umożliwienie szybszego kontaktu ze światem. – Nauki humanistyczne - inteligentne bazy danych, bazy wiedzy, edytory prac naukowych, kontakt ze światem. • Przemysł – Elektronika - projektowanie układów, wspomaganie obliczeń, analiza poprawności zaprojektowanych obwodów. – Chemia i farmacja - modelowanie procesów laboratoryjnych, wspomaganie obliczeń. – Biologia, biochemia - bazy wiedzy, modelowanie cząsteczek, analiza DNA lub RNA.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

28

Wstęp

– Aplikacje CAD/CAM - komputerowe wspomaganie projektowania i modelowania, wykorzystywane głownie przez inżynierów konstruktorów czy architektów, np. AutoCAD. • Zastosowania cywilne – Modelowanie i przewidywanie zmian pogody czy prądów oceanicznych. – Wspomaganie kierowania ruchem lotniczym, kolejowym czy morskim. – Projektowanie dróg, mostów, tuneli, kolei żelaznych. • Zastosowania militarne – Sterowanie systemem obrony. – Zdalne naprowadzanie pocisków. • Biznes – Aplikacje finansowe i ekonomiczne. – Aplikacje biurowe. – Systemy eksperckie i systemy wspomagania podejmowania decyzji. Z pewnością przyszłość informatyki będzie związana z rozwojem technologii kolejnych generacji komputerów. Co więcej same komputery, jak i technologia ich wykonania mogą się zmienić. Dość nadmienić, że już dziś prowadzone są próby konstruowania komputerów biologicznych, czyli takich, w których rolę tranzystora będzie pełniło białko. Możemy stwierdzić jedynie, że charakter procesów przetwarzanych przez te komputery nie zmieni się, lecz sposób ich wykonania może ulec modyfikacji. Od wielu lat prowadzi się badania i wdraża przetwarzanie potokowe, rozproszone oraz równoległe. Wszystkie te pojęcia, za którymi stają technologie, są związane z wysiłkami człowieka, mającymi na celu przyśpieszenie przetwarzania informacji, a w efekcie uzyskanie wyniku w krótszym czasie. Cała historia informatyki, a w szczególności jej niesłychanie dynamiczny rozwój przez ostatnie 40 lat, pozwala snuć wręcz bajeczne wizje dotyczące przyszłych zastosowań komputerów. Niemniej jednak, należy pamiętać, że za tym wszystkim stoi człowiek i to od jego mądrości i dalekowzroczności będzie zależało, czy przyszłe rozwiązania będą służyły nam efektywnie, czy też staną się dla nas balastem i niechcianą koniecznością.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

1.4 Kierunki współczesnej informatyki

29

1.4

Kierunki współczesnej informatyki
Adde parvum parvo magnus acervus erit. (Jeśli będziesz dodawał małe do małego, to zbierze się wielka sterta.) Owidiusz

Obecnie informatyka jest na tyle szeroką dyscypliną naukową, że podobnie, jak w matematyce, fizyce czy chemii, nie ma już „specjalistów od wszystkiego”. Zatem pojawiają się specjalności, które skupiają się wokół jednego bądź kilku zagadnień. Z faktu ogromnej dynamiki rozwoju informatyki wynika często, że osoby uprawiające dany dział muszą poświęcać mu tyle czasu, że nie mają już możliwości śledzić tego co dzieje się poza ich poletkiem. Poniżej zamieszczamy jedną z możliwych klasyfikacji dziedzin informatyki wymienionych w kolejności alfabetycznej.

1.4.1

Algorytmika

Algorytmika jest jedną z najstarszych dziedzin informatyki, wynika to bezpośrednio z zakresu jaki obejmuje, a jest nim tworzenie i badanie algorytmów. Poprzez badanie algorytmów rozumie się głównie zagadnienia związane z teorią obliczalności, dzięki której można stwierdzić czy dany algorytm da się w ogóle zrealizować w praktyce, a jeśli tak, to w jakim czasie 7 . Należy podkreślić, że jest to jeden z działów, który swoim zakresem przenika prawie wszystkie inne, oraz niejednokrotnie wykracza poza samą informatykę, wnikając mocno w matematykę, technikę czy ekonomię. Patrząc na historię algorytmiki okazuje się, że najbardziej fundamentalne prace z teorii algorytmów pojawiły się w w latach trzydziestych. Można się temu dziwić, gdyż jak to wynikało z rysu historii informatyki, wtedy nie istniał jeszcze żaden komputer elektronowy a już na pewno elektroniczny. Jednak nie ma w tym nic niezwykłego, algorytmika w swoim aspekcie teoretycznym nie potrzebuje w ogóle, lub tylko w minimalnym stopniu komputerów. To właśnie w latach trzydziestych Alan Turing formułuje założenia Maszyny Turinga (zob. 3.1) oraz wykorzystuje ją do formułowania teorii obliczalności [Tur36]. Stephen Kleene opisuje formalnie spójną klasę rekurencyjnie definiowalnych funkcji teorioliczbowych [Kle36]. Z kolei Alonzo Church wymyśla inny sposób opisu algorytmów, tzw. rachunek λ [Chu41]. Nieco później, bo w roku 1961, Andriej Markow proponuje jeszcze inny sposób opisu algorytmów — opis oparty na łańcuchach [Mar54].
Istnieje cała klasa algorytmów, które są poprawne i wiemy, że dają dobre rezultaty ale na przykład po 3000 lat.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004 7

30

Wstęp

Zarys algorytmiki znajduje się w rozdziale 5.

1.4.2

Bazy danych

W obecnym świecie żyjemy w zalewie informacji, którą należy przetworzyć i tym zajmują się wspomniane wcześniej algorytmy, ale również należy gdzieś tę informację magazynować. A jeśli magazynować to w taki sposób by łatwo i szybko można było do niej sięgnąć i odnaleźć potrzebne dane. Rolę takiego inteligentnego magazynu spełniają bazy danych. Historycznie bazy towarzyszyły informatykom od początku powstania komputerów, lecz różniła się ich forma i sposób przechowywania informacji. Można by nawet zaryzykować stwierdzenie, że już Hollerith opracowując nową metodę przeprowadzania spisu ludności posługiwał się bazą danych głosów w postaci kart perforowanych i na ich podstawie opracowywał raporty. Pojawienie się komputery jakie znamy obecnie dało też początek bazom danych z prawdziwego zdarzenia. Z początku były to bardzo prymitywne technologie, do których można zaliczyć bazy hierarchiczne i bazy sieciowe. Posiadały one wiele wad, jak np. nadmiarowość danych, czy brak integralności. Pod koniec lat sześćdziesiątych matematyk E. F. Codd, pracujący w firmie IBM stworzył model relacyjnej bazy danych. Idee tego modelu opisał w pracy [Coo70]. Obecnie przede wszystkim wykorzystuje się relacyjne bazy danych, lecz od ponad 15 lat bardzo dynamicznie rozwijają się obiektowe bazy danych, w których daje się, lepiej niż w relacyjnych, zamodelować pewne dane. Do najnowszych trendów można również zaliczyć bazy oparte o technologię XML. W tym opracowaniu nie ma specjalnego miejsca przeznaczonego na bazy danych, pewne informacje można znaleźć w ???.

1.4.3

Grafika komputerowa

Za datę powstania tego działu informatyki uznaje się lata pięćdziesiąte, kiedy to w Massachusetts Institute of Technology zbudowano komputer Whirlwind wyposażony w grafoskop. Przez ponad dwadzieścia lat dziedzina ta była dostępna wyłącznie nielicznym, głównie ze względu na bardzo wysokie koszty urządzeń, dość przypomnieć, że wszystkie terminale komputerowe w tamtych czasach były alfanumeryczne, zatem mogły wyświetlać wyłącznie znaki. W latach sześćdziesiątych widać już pewne zainteresowanie przemysłu grafiką komputerową. General Motors wykorzystuje system DEC-1 produkcji IBM do projektowania samochodów. W ich ślady idą producenci samolotów oraz okrętów, a następnie praktycznie cały przemysł.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

1.4 Kierunki współczesnej informatyki

31

W dzisiejszych czasach trudno sobie wyobrazić nowoczesnego projektanta bez elektronicznej deski kreślarskiej, i co ciekawe nie ma znaczenia czy będzie to architekt, czy też projektant ubrań. Grafika komputerowa oddaje ogromne usługi prawie w każdej dziedzinie współczesnego życia. Idąc do architekta, można obejrzeć swój przyszły dom wręcz po nim „spacerując”, idąc do fryzjera można zweryfikować czy fryzura nam proponowana jest dla nas odpowiednia. Oczywiście trudno zapomnieć o całym przemyśle rozrywkowym, czyli grach i wszelkiego rodzaju multimediach, które coraz chętniej sięgają po tzw. produkty interaktywne. Wszystko to wymaga wsparcia od strony komputera zarówno od strony sprzętowej jak i programowej. Widać to wyraźnie w cenach elementów wchodzących w skład komputera, bardzo dobra karta graficzna potrafi kosztować tyle co reszta komputera. Aby wszystkie te programy działały bardzo wydajnie wymagają udoskonalania algorytmów do generowania obrazu.

1.4.4

Kryptografia

Odkąd sięga pamięć ludzka, to co cenne próbowano ukryć przed wzrokiem czy dotykiem osób niepowołanych. Ponad 2000 lat temu dostrzeżono konieczność ukrywania informacji, na ten okres datuje się pierwszy znanym szyfrem — szyfr Cezara. W chwili obecnej trudno sobie wyobrazić transmisję jakichkolwiek ważniejszych danych w komputerach bez stosowania transmisji szyfrowanej przez protokół SSL. Większość programów pocztowych zawiera już wsparcie do szyfrowanego połączenia, a łącząc się z bankiem w celu sprawdzenia stanu konta, serwer banku wymusza na przeglądarce połączanie za pomocą protokołu https, który jest niczym innym jak zwykłym protokołem http „przepuszczanym” przez SSL. Dziedzina wiedzy zajmująca się badaniem, tworzeniem i łamaniem szyfrów to właśnie kryptografia. Jej gwałtowny rozwój zaczął się od roku 1975 i do dzisiejszego momentu pozostaje jedną z najdynamiczniej rozwijających się dziedzin. Należy zaznaczyć, że jest ona przede wszystkim dziedziną matematyki. Ponieważ jednak tak powszechnie jest wykorzystywana w ochronie informacji stąd też badaniem jej zajmują się również informatycy.

1.4.5

Programowanie

Programowanie jest jednym z działów informatyki, który jest właśnie z nią kojarzony, nawet przez osoby niedoświadczone. Do niedawna samo stwierdzenie „Jestem programistą”, brzmiało prawie jak „Jestem wszechmocny”.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

32

Wstęp

Faktem jest, że języki programowania, będące jednym z elementów całości zagadnienia związanego z programowaniem, powstały zaraz na początku współczesnej historii informatyki. Chodź jak wiemy, możemy dopatrywać się ich korzeni jeszcze wcześniej przy okazji maszyna analitycznej Babbage’a oraz Pani hrabiny Ady. Niemniej jednak, języki programowania w formie w jakiej je znamy obecnie, a raczej dziadkowie dzisiejszych języków programowania, pojawiły się w momencie powstania idei maszyny von neumannowskiej, która pozwalała zapamiętywać dane i program jednocześnie w pamięci. Można zatem przyjąć za początek powstania współczesnych języków programowania lata pięćdziesiąte ubiegłego wieku. Bezspornym jest, że języki bardzo się zmieniły od tamtego czasu, choć ich cel pozostał taki sam. Jest nim dostarczenie wygodnej formy przekazu naszych myśli do komputera w celu uzyskania pewnego konkretnego efektu. Oczywiście sposób zapisu, czyli wyrażania naszych myśli musi być odpowiednio precyzyjny, ale ta tematyka jest szerzej omawiana w rozdziale 6. Programowanie to jednak nie tylko języki programowania, to również wszelkiego rodzaju narzędzia wspomagające proces wytwarzania oprogramowania, tworzenia dokumentacji technicznej czy dokumentacji użytkownika. Co więcej cały proces tworzenia oprogramowania doczekał się wielu modeli i sposobów analizowania oraz porównywania, które z nich są lepsze i w jakich sytuacjach. Powstała oddzielna gałąź informatyki pod nazwą inżynieria oprogramowania, której zadaniem jest właśnie systematyzowanie i badanie procesów związanych z tworzeniem oprogramowania.

1.4.6

Sieci komputerowe

W chwili obecnej sieci komputerowe są jednym z najlepiej ugruntowanych działów informatyki. Ich korzenie sięgają 1957 roku kiedy to Departament Stanu USA powołał do życia Agencję Zaawansowanych Projektów Badawczych Departamentu Obrony USA (ang. Advanced Research Projects Agency, (ARPA)). Jednym z jej celów było opracowanie takiej formy komunikacji komputerów, która byłaby wygodna a jednocześnie odporna na ataki militarne. Pod koniec 1969 roku stworzono eksperymentalną sieć opartą na wymianie pakietów danych, projekt ten pod nazwą ARPANET wykorzystywał protokół NCP (ang. Network Control Protocol). Umożliwiał on logowanie na zdalnym komputerze, drukowanie na zdalnej drukarce oraz przesyłanie plików. Na początku rozwój sieci nie był zbyt dynamiczny, dla przykładu w 1973 roku ARPANET składała się z 23 komputerów. Dzisiejszą siłę sieć zawdzięcza globalizacji, która była możliwa dzięki zastosowaniu jej
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

1.4 Kierunki współczesnej informatyki

33

do celów cywilnych. Zaczęto dostrzegać, że można ją wykorzystywać do ułatwienia komunikacji na prawie wszystkich płaszczyznach. I tak w 1972 roku zostaje wysłana pierwsza wiadomość elektroniczna (E-mail), której autorem i pomysłodawcą idei był Roy Tomilnson. W 1974 roku zostaje opracowany stos protokołów TCP/IP, który został w tym samym roku uznany oficjalnym protokołem w sieci i pozostaje nim do dnia dzisiejszego. Autorami tej idei byli Vinton Cerf oraz Robert Kahn. Mimo tych sukcesów wciąż są to technologie drogie i niedostępne dla przeciętnego użytkownika. W 1980 roku ARPANET liczy 400 serwerów. Jednak wydarzenia nabierają tempa, już dziewięć lat później sieć przekroczyła 100 000 komputerów. Stało się to głównie za sprawą przyłączenia się do poparcia tej idei Narodowej Fundacji Nauki USA (ang. National Science Foundation), która stworzyła własną sieć NFSNET i dołączyła się do istniejących. Sieć ta stała się na tyle wydajna, że w roku 1990 zastąpiła całkowicie ARPANET i stała się oficjalnym szkieletem Internetu8 . Z pewnością większość użytkowników kojarzy Internet z dwoma usługami: pocztą elektroniczną i stronami WWW. Jednak o ile poczta jest usługą wiekową (jak na historię sieci) to WWW powstało dopiero w 1992 roku, a technologię tę opracował Tim Berners-Lee. Należy pamiętać, że sieci komputerowe to nie tylko Internet, w ich skład wchodzą również sieci lokalne, czy osiedlowe. Ponadto dziedzina ta zajmuje się również badaniem wydajności sieci i opracowywaniem nowych algorytmów do przekazywania w niej informacji. W połączeniu z kryptografią dba o bezpieczeństwo przesyłanej informacji niezależnie od jej nośnika, a obecnie poza tzw. przewodem, transmisja danych odbywa się za pomocą: podczerwieni, promieni laserowych, czy fal radiowych. Zarys zagadnień sieci komputerowych znajduje się w rozdziale 8.

1.4.7

Systemy operacyjne

System operacyjny jest wyspecjalizowanym programem, który zapewnia sprawne funkcjonowanie systemu komputerowego. Jest to program ściśle związany z architekturą konkretnego komputera w tym przede wszystkim z procesorem. Głównym zadaniem systemu operacyjnego jest dostarczanie podstawowych operacji dostępu do urządzeń i zasobów systemu komputerowego. Od jego jakości i wydajności w dużej mierze zależy wydajność całości systemu komputerowego. W dniu dzisiejszym istnieje cała gama systemów
8

Polska została przyłączona do tego szkieletu w 1991 roku.

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

34

Wstęp

operacyjnych na komputery klasy PC 9 oparte o procesory rodziny INTEL10 . Przykładowymi systemami dla takich komputerów są MS-DOS, rodzina systemów Windows, Linux, rodzina BSD, kilka komercyjnych systemów klasy UNIX. Co więcej systemy rodziny UNIX, w tym Linux i BSD, są systemami posiadającymi swoje wersje na inne wersje procesorów np. PowerPC. Istnieje również cała gama systemów operacyjnych specjalizowanych na konkretną maszynę i przeznaczonych do specjalnych celów, przykładami mogą tu być AS/400 czy OS/390. Trudno dzisiaj powiedzieć, który system był tym pierwszym. Wiemy na pewno, że w roku 1964 w firmie IBM zaczęto tworzenie systemu operacyjnego z przeznaczeniem na komputery MainFrame, bardzo złożonego i wydajnego systemu operacyjnego dla maszyn S/360 i ich późniejszych następców. Były to ogromne komputery przeznaczone do ciągłej pracy i najwyższego obciążenia, projektowane z myślą o profesjonalnych systemach. Nie mniej jednak IBM wcześniej również pisał prostsze systemy operacyjne (patrz 7.10.5). Z kolei historia rodziny UNIX sięga roku 1965, kiedy to trzy organizacje Bell Telephone Laboratories, General Electric Company i Massachusetts Institute of Technology w ramach projektu MAC podjęły próbę stworzenia systemu operacyjnego nazwanego Multics. Zadaniem tego systemu było dostarczenie mechanizmów pozwalających na dostęp jednoczesny do danego komputera przez wielu użytkowników oraz wsparcie do obliczeń i współdzielenia danych pomiędzy jego użytkownikami. Niestety prace nad tym systemem nie zostały zakończony i ponieważ nic nie wskazywało, że kiedykolwiek to nastąpi, Bell Laboratories odłączyło się od tego projektu i ok roku 1970 stworzyło pierwszą wersję systemu UNIX pracującego na maszynach PDP-11. W chwili obecnej zarówno OS/390 jak i UNIX istnieją jako dojrzałe i profesjonalne systemy operacyjne. Niejako na drugim końcu stoją systemy takie jak rodzina Windows i MacOS, które od samego początku były projektowane jako systemy nastawione na łatwość obsługi i przeznaczone na popularne komputery. Co prawda istnieją obecnie wersje zarówno Windows jak i MacOS z przeznaczeniem na serwery, jednak nie to było ich pierwotnym przeznaczeniem. Szerszy opis systemów operacyjnych znajduje się w rozdziale 7.

PC (ang. Personal Computer) komputer osobisty, ogólnego przeznaczenia. Mówiąc „rodzina INTEL” mamy na myśli procesory tej firmy oraz innych firm kompatybilne (zgodne) z nimi na poziomie listy rozkazów, przykładem może być rodzina procesorów firmy AMD.
10 c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

9

1.4 Kierunki współczesnej informatyki

35

1.4.8

Sztuczna inteligencja

Pomimo, iż termin sztuczna inteligencja (ang. Artificial Intelligence) powstał stosunkowo niedawno, to próby stworzenia urządzeń inteligentnych sięgają dość daleko wstecz. Za pierwsze należy zapewne uznać wysiłki zmierzające w kierunku skonstruowania automatu potrafiącego grać w szachy, które niestety ze względu na niedostatki w rozwoju ówczesnej techniki, najczęściej okazywały się mniej lub bardziej zręcznymi oszustwami. Pierwsza połowa XX wieku, to okres upływający pod znakiem „robotyzacji” i pierwsze wizje maszyn wyglądających i zachowujących się jak ludzie. Pod koniec lat 50-tych stajemy się świadkami narodzin informatyki jako dyscypliny nauki. Nauki zajmującej się przetwarzaniem danych i wykorzystującej w tym celu nowe urządzenie nazwane komputerem. Wkrótce, na fali ogólnoświatowego zachwytu nad możliwościami jakie otworzył on przed nami, powstają pierwsze proste programy zdolne do zadawania pytań i udzielania odpowiedzi na pytania zadane przez człowieka. Dziś jednak wcale nie jest łatwo powiedzieć czym jest inteligencja?; w znacznej mierze jest to spowodowane brakiem zrozumienia natury ludzkiego mózgu. Przez lata pojęcie sztucznej inteligencji ulegało przeobrażeniom, zależnie od bieżącego stanu rozwoju myśli ludzkiej — począwszy od automatu do grania, przez maszynę potrafiącą wykonywać pewne czynności za człowieka aż po urządzenie zdolne samodzielnie podejmować decyzje. Nie podejmujemy się w tym miejscu udzielić odpowiedzi na to pytanie. Zamiast tego powiemy czego oczekuje się obecnie od sztucznej inteligencji. Otóż celem jest rozwój w kierunku stworzenia systemu, który potrafiłby zachowywać się jak istota inteligentna, tj. na podstawie zgromadzonej wiedzy i znanych przesłanek podejmowałby decyzje wykazujące znamiona racjonalności. Pragniemy podkreślić ogólność ostatniego stwierdzenia. Pod terminem „system” można rozumieć program komputerowy, maszynę lub . . . cokolwiek innego. Również pojęcia „wiedza”, „racjonalność” są tak samo mało precyzyjne jak i sam termin „inteligencja”. Sztuczna inteligencja jako nauka ukierunkowana na stworzenie systemu naśladującego istoty rozumne z istoty rzeczy korzysta z opisu pewnych ich zachowań, budowy czy obserwowanych mechanizmów nimi rządzących. Nie dziwi zatem prowadzenie badań związanych ze sztucznymi sieciami neuronowymi, algorytmami genetycznymi, systemami działającymi w oparciu o logikę rozmytą, czy też badających zachowania populacji — algorytmy mrówkowe, algorytmy symulujące rój pszczół itp. Można powiedzieć, że zakres badań obejmuje wszystko „co się rusza”. Tak jest w istocie a wynika to z komplementarności tych poddziedzin sztucznej inteligencji. Na przykład
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

36

Wstęp

sztuczne sieci neuronowe stosują stochastyczne algorytmy dopasowania modeli poprzez uczenie nadzorowane lub nie, opierające się na nieprecyzyjnych (obarczonych pewnym błędem, tzw. szumem) danych numerycznych. Złagodzenie wymogów precyzji w procesie tworzenia modeli, a co najważniejsze dla nas ludzi, możliwość opisu złożonych systemów za pomocą zmiennych pojmowanych intuicyjnie (na przykład: lodowato, zimno, w sam raz, ciepło, gorąco) to z kolei zaleta systemów przetwarzających informacje w oparciu o zbiory rozmyte i wnioskowanie przybliżone. Połączenie obu wspomnianych wyżej dziedzin pozwala na stworzenie systemu operującego nieprecyzyjnymi określeniami zależnymi od zaistniałego kontekstu (logika rozmyta) oraz jednocześnie zdolnego do uczenia się (sztuczne sieci neuronowe). Doskonałym ich uzupełnieniem stają się algorytmy genetyczne poszukujące optymalnego rozwiązania (na przykład parametrów sieci neuronowej) na drodze kolejnych przybliżeń dobieranych poprzez wykorzystanie mechanizmów mutacji i krzyżowania chromosomów oraz oceny przez tak zwaną funkcję przystosowania. Jak więc widać, sztuczna inteligencja to bardzo szeroka dziedzina wiedzy łącząca w sobie matematykę, biologię, fizykę chemię, filozofię, lingwistykę, . . . , o której precyzyjnie za wiele powiedzieć nie można. Pewne jedynie jest to, że na dzień dzisiejszy w związku z nią zadajemy więcej pytań niż otrzymujemy odpowiedzi.

1.4.9

Teoria informacji

Teoria informacji jest jedną z niewielu dziedzin dla których znamy dokładną datę narodzin. Jest to rok 1948 kiedy to 32-letni matematyk Claude Shannon publikuje pracę w piśmie Bell System Technical Journal. Ta właśnie praca uznawana jest za podstawową w tej dziedzinie, jednak zanim opiszemy wyniki Shannona, spróbujmy ogólnie opowiedzieć czym zajmuje się ta dziedzina. Teoria informacji zajmuje się informacją, jej transmisją, jak i również kodowaniem danych w celu pewniejszego lub szybszego przesłania jej od nadawcy do odbiorcy. Kodowanie nie należy tu mylić z szyfrowaniem, gdyż nie jest celem tutaj ukrycie informacji, a wyłącznie zapewnienie, że dotrze ona do celu w całości lub jeśli nastąpi przekłamanie to o tym się dowiemy. Najprostszym kodem jaki większość zna jest kod Morse’a, który został skonstruowany by za pomocą bardzo skromnego zbioru znaków (kropka, kreska) móc przekazać całe zdania dowolnego języka opartego o litery łacińskie. Należy podkreślić, że próba przekazania jakiejkolwiek informacji przez
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

1.5 Zadania

37

dowolny nośnik może się nie powieść, lub informacja może zostać zniekształcona. Chyba każdy z nas pamięta dziecięcą zabawę w głuchy telefon, podobnie jest w przypadku rozmowy przez zwykły analogowy telefon, słychać trzaski i czasami słowa dochodzą zniekształcone. Shannon w swojej pracy podał warunki jakie muszą być spełnione by informacja dotarła bez zniekształceń, co więcej pokazał on drogę w jaki sposób przekazać informację lepiej bez zmiany parametrów kanału transmisyjnego. Zarys teorii informacji znajduje się w rozdziale 4.

1.5

Zadania

1. Wymień kluczowe postacie z historii informatyki wraz z ich dokonaniami? Spróbuj ustawić je chronologicznie. 2. Wymień dwie dziedziny informatyki i krótko je scharakteryzuj. 3. Spróbuj wyjaśnić wpływ komputerów na dzisiejszą informatykę.

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

38

Wstęp

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

Rozdział 2

Podstawowe pojęcia i definicje
Nie przejmuj się, jeśli masz problemy z matematyką. Zapewniam Cię, że ja mam jeszcze większe. Albert Einstein

2.1

Algebra Boole’a

Przy projektowaniu i analizowaniu układów cyfrowych w komputerach i innych systemach cyfrowych stosuje się algebrę Boole’a. Nazwa pochodzi od nazwiska matematyka George’a Boole’a, który zaproponował jej podstawowe zasady w traktacie zatytułowanym An Investigation of The Laws of Thought on Which to Found the Mathematical Theories of Logic and Probabilities (Badanie praw myśli, które mogą być podstawą matematycznych teorii logiki i prawdopodobieństwa). W roku 1938 Claude Shannon zasugerował zastosowanie algebry Boole’a do rozwiązywania problemów projektowania układów przekaźnikowych. Metody tej użyto następnie do analizowania i projektowania elektronicznych układów cyfrowych. W konsekwencji, dzisiejsze komputery jako urządzenia działające w oparciu o elektronikę cyfrową, pracują wykorzystując algebrę Boole’a.

2.1.1

Definicja ogólna

Niech dany będzie niepusty zbiór B, w którym wyróżnione są dwa elementy 0, 1. W zbiorze tym określamy działania sumy logicznej „+”, iloczynu
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

40

Podstawowe pojęcia i definicje

logicznego „·”1 oraz dopełnienia logicznego „− ”. Przyjmuje się, że priorytet powyższych działań określa, że działanie dopełnienia ma pierwszeństwo przed iloczynem, zaś działanie iloczynu ma pierwszeństwo przed działaniem sumy. Algebrą Boole’a nazywamy zbiór B wraz z powyższymi działaniami i taki, że dla dowolnych elementów x, y, z ∈ B zachodzą następujące prawa: Prawa przemienności x+y = y+x x·y = y·x Prawa łączności (x + y) + z = x + (y + z) (x · y) · z = x · (y · z) Prawa rozdzielności x + (y · z) = (x + y) · (x + z) (2.5) (2.6) (2.3) (2.4) (2.1) (2.2)

x · (y + z) = (x · y) + (x · z) Prawa identyczności x+0 = x x·1 = x Prawa dopełnienia x+x = 1 x·x = 0

(2.7) (2.8)

(2.9) (2.10)

Zauważmy, że jeśli w powyższych prawach zamienione zostaną odpowiednio działania sumy i iloczynu oraz jednocześnie elementy 0 i 1 to prawa te dalej pozostaną w mocy, w wyniku otrzymamy ich drugą wersję. Dla przykładu spójrzmy na wzór (2.1), jeśli w nim zamienimy znak „+” na „·”, to otrzymamy prawo (2.2). Dzieje się tak, ponieważ własności we wszystkich algebrach Boole’a są konsekwencjami powyższych praw. Zatem zasada zamiany działań i elementów wyróżnionych obowiązuje w nich wszystkich nie zmieniając własności wzorów. Powyższy fakt zwany jest zasadą dualności.
W dalszej części będziemy często pomijali symbol „·”, zatem zapis AB będzie równoważny A · B
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004 1

2.1 Algebra Boole’a

41

Twierdzenie 2.1. Następujące własności są prawdziwe w każdej algebrze Boole’a: Prawa idempotentności x+x = x x·x = x Drugie prawa identyczności x+1 = 1 x·0 = 0 Prawa pochłaniania (x · y) + x = x (2.15) (2.16) (2.13) (2.14) (2.11) (2.12)

(x + y) · x = x

Dowód. Udowodnimy po kolei prawa zacytowane w tezie twierdzenia. Dowód prawa (2.11). Korzystając z prawa identyczności (2.8) mamy x + x = (x + x) · 1 następnie stosujemy prawo dopełnienia (2.9) (x + x) · 1 = (x + x) · (x + x) teraz stosując prawo rozdzielności (2.5) (x + x) · (x + x) = x + (x · x) korzystając z prawa dopełnienia (2.10) mamy x + (x · x) = x + 0 ostatecznie stosując prawo identyczności (2.7) otrzymujemy, że x+0 =x co kończy dowód prawa (2.11). Dowód prawa (2.12) przeprowadza się analogicznie, więc go pominiemy. Dowód prawa (2.13). Korzystając z prawa dopełniania (2.9) mamy x + 1 = x + (x + x)
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

42

Podstawowe pojęcia i definicje

następnie stosujemy prawo łączności (2.3) x + (x + x) = (x + x) + x teraz stosując prawo idempotentności (2.11) (x + x) + x = x + x ostatecznie korzystając z prawa dopełniania (2.9) mamy x+x=1 co kończy dowód prawa (2.13). Dowód prawa (2.14) przeprowadza się analogicznie. Dowód prawa (2.15). Korzystając z prawa identyczności (2.8) otrzymujemy (x · y) + x = (x · y) + (x · 1) (x · y) + (x · 1) = x · (y + 1) następnie stosując drugie prawo identyczności (2.13) x · (y + 1) = x · 1 i ponownie stosując pierwsze prawo identyczności (2.8) ostatecznie otrzymujemy x·1 =x

stosując prawo rozdzielności (2.6)

co kończy dowód prawa (2.15). Dowód prawa (2.16) przeprowadza się analogicznie. Udowodniliśmy zatem tezę twierdzenia. Twierdzenie 2.2. W każdej algebrze Boole’a spełnione są następujące prawa De Morgana: (x + y) = x · y Dowód. Wpierw pokażemy, że jeśli w+z =1 i w · z = 0, (2.17) (2.18)

(x · y) = x + y

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

2.1 Algebra Boole’a

43

to z = w. Dzięki temu będziemy mogli stwierdzić, że własności w+w =1 i w · w = 0,

charakteryzują jednoznacznie w. Spróbujmy wykazać powyższe. Korzystając z prawa identyczności (2.7) mamy z =z+0 dzięki prawu dopełniania (2.10) z + 0 = z + (w · w) stosując prawo rozdzielności (2.5) z + (w · w) = (z + w) · (z + w) następnie prawo przemienności (2.1) (z + w) · (z + w) = (w + z) · (w + z) teraz wykorzystując założenie (w + z) · (w + z) = 1 · (w + z) korzystając z prawa dopełnienia (2.9) 1 · (w + z) = (w + w) · (w + z) ponownie z prawa przemienności (2.1) (w + w) · (w + z) = (w + w) · (w + z) stosując prawo rozdzielności (2.5) (w + w) · (w + z) = w + (w · z) korzystając z założenia w + (w · z) = w + 0 na koniec stosując prawo identyczności (2.7) otrzymujemy w+0=w
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

44

Podstawowe pojęcia i definicje

Zatem udowodniliśmy, że przy stawianych założeniach z=w Jeśli teraz pokażemy, że (x + y) + (x · y) = 1 (x + y) · (x · y) = 0 (2.19) (2.20)

to korzystając z wcześniej udowodnionej jednoznaczności, otrzymamy prawdziwość prawa De Morgana (2.17) stawianego w tezie twierdzenia. Zatem udowodnimy wpierw, że zachodzi (2.19). Korzystając z rozdzielności mamy: (x + y) + (x · y) = ((x + y) + x) · ((x + y) + y) = . . . następnie z łączności i przemienności · · · = (y + (x + x)) · (x + (y + y)) = (y + 1) · (x + 1) = 1 · 1 = 1. Podobnie udowadnia się (2.20), co ostatecznie pozwala stwierdzić prawdziwość prawa (2.17). Korzystając z zasady dualności widać natychmiast prawdziwość prawa (2.18).

2.1.2

Dwuelementowa algebra Boole’a

W informatyce (jak i elektronice cyfrowej) zwykle wykorzystuje się dwuelementową algebrę Boole’a, której zbiór elementów jest postaci B = {0, 1}. Od tego momentu mówiąc o algebrze Boole’a, będziemy mieli na myśli właśnie tę dwuelementową wersję. W przypadku takiej algebry możliwe jest sprawdzenie poprawności równości za pomocą tzw. metody zerojedynkowej. W metodzie tej rozważa się wszystkie możliwe przypadki jakie otrzymamy podstawiając za zmienne wartości 0 lub 1. Sprawdźmy tą metodą na przykład prawo (2.3). x 0 0 0 0 1 1 1 1 y 0 0 1 1 0 0 1 1 z 0 1 0 1 0 1 0 1 y+z 0 1 1 1 0 1 1 1 x + (y + z) 0 1 1 1 1 1 1 1 x+y 0 0 1 1 1 1 1 1 (x + y) + z 0 1 1 1 1 1 1 1

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

2.2 Pozycyjne systemy liczbowe

45

Zauważmy, że każde takie wyrażenie można zapisać w jednej z dwóch możliwych postaci kanonicznych: sumy iloczynów lub iloczynu sum. Aby to wyjaśnić zilustrujemy ten fakt przykładem, pokazującym jednocześnie jak upraszcza się skomplikowane wyrażenia booleowskie (przykład 2.1). W przykładzie tym nad znakami równości w nawiasach umieszczono numery praw z których korzystano, przy czym prawa przemienności uznaje się za intuicyjne i będą pomijane w tych oznaczeniach. Przykład 2.1. Upraszczanie wyrażeń algebraicznych

w = (x + y) (x + y) (x + z) = [x (x + y) + y (x + y)] (x + z) = = [x + xy + yx + yy] (x + z) = [x + x (y + y) + 0] (x + z)
(2.6) (2.10) (2.6,2.10)

(2.6)

(2.6)

=

(2.9,2.7)

=

(x + x0) (x + z)

(2.14,2.7)

=

= x(x + z) = xx + xz = xz v = xyz + xyz + xyz = x (yz + zy + yz) =
(2.9) (2.6) (2.6) (2.11) (2.6)

= x (yz + yz + yz + yz) = x [(yz + yz) + (yz + yz)] = = x [z (y + y) + y (z + z)] = x(z + y) = xz + xy

Uwaga 2.1. Wprowadzenie w przykładzie 2.1 nawiasów kwadratowych nie ma charakteru formalnego, zastały one użyte wyłącznie dla poprawy czytelności wyrażenia i należy je traktować jak nawiasy okrągłe. Zauważmy jednocześnie, że przedostatnia postać wyrażenia v w przykładzie 2.1 jest, z praktycznego punktu widzenia, prostsza niż ostatnia, gdyż wymaga tylko jednej operacji sumy i jednego iloczynu; druga wymaga dwóch iloczynów i jednej sumy. Oczywiście ponieważ obie postacie są sobie równoważne, zatem można w zależności od okoliczności wykorzystywać dowolną z nich, co więcej dowolną z pośrednich postaci.

2.2

Pozycyjne systemy liczbowe

Przypomnijmy jak zapisujemy liczbę naturalną, stosując do tego zapis powszechnie używany zwany zapisem dziesiętnym, lub systemem dziesiętnym.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

46

Podstawowe pojęcia i definicje

Zauważmy, że liczba n−cyfrowa w systemie dziesiętnym ma postać cn−1 ...c1 c0 , (2.21)

gdzie cn−1 , ..., c1 , c0 są znanymi cyframi systemu dziesiętnego, zatem należą do zbioru {0, 1, ..., 9}. Powyższy zapis ma wartość równą w = cn−1 · 10n−1 + · · · + c1 · 101 + c0 · 100 . Widać, iż w zapisie (2.21) ta sama cyfra będzie oznaczała inną wartość w zależności od miejsca występowania w tym napisie. Dla przykładu w napisach 601 i 610 cyfra jeden odpowiednio ma wartość jeden i dziesięć. Zatem pozycja cyfry determinuje wspólnie z nią samą wartość, stąd systemy liczbowe oparte na tej zasadzie będziemy nazywali pozycyjnymi systemami liczbowymi. Uwaga 2.2. Zupełnie inna sytuacja występuje w zapisie liczby w systemie rzymskim. Przypomnijmy, że w systemie tym kolejne liczby od 1, . . . , 9 mają postać I, II, III, IV, V, V I, V II, V III, IX Widać, że w takim zapisie pozycja „cyfry” — o ile w ogóle można mówić w tym wypadku o cyfrze, nie jest związana z wyznaczaniem jej wartości, lecz istotna jest postać całej liczby. Taki system zapisu nazywamy addytywnym systemem liczbowym. Przykład definicji systemu dziesiętnego wskazuje drogę uogólnienia jej na definicję dowolnego pozycyjnego systemu liczbowego. Pozycyjnym systemem liczbowym nazywamy parę (q, C), gdzie q jest liczbą naturalną i nazywamy ją podstawą systemu, oraz C jest skończonym zbiorem znaków {0, 1, . . . , q − 1}, zwanych cyframi. W systemie pozycyjnym, jak powiedziano na wstępie, liczbę przedstawia się jako ciąg cyfr, przy czym wartość tej liczby zależy zarówno od cyfr jak i miejsca, na którym się one znajdują w tym ciągu. Zapis ck ck−1 . . . c1 c0 ma wartość liczbową w = ck q k + ck−1 q k−1 + . . . + c1 q + c0 , (2.22)

gdzie c0 , . . . , ck ∈ C. Kolejne potęgi podstawy systemu q nazywa się rzędami. Jeśli q = 10, to otrzymujemy dziesiątkowy system liczbowy, dla q = 2 dwójkowy (binarny), dla q = 8 — ósemkowy, itd.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

2.2 Pozycyjne systemy liczbowe

47

W przypadku posługiwania się na raz kilkoma systemami liczbowymi, stosujemy zapis liczby z informacją o podstawie systemu w jakim została zapisana. Jest to bardzo ważne gdyż liczby, które „wyglądają” tak samo mogą mieć inną wartość w różnych systemach liczbowych. Dla przykładu 10(10) — liczba o wartości 10 dziesiętnie zapisana w systemie dziesiętnym, 10(2) — liczba o wartości 2 dziesiętnie zapisana w systemie dwójkowym, 10(3) — liczba o wartości 3 dziesiętnie zapisana w systemie trójkowym.

2.2.1

System dwójkowy

Ponieważ obecne komputery są konstruowane w oparciu o układy cyfrowe pracujące według reguł dwuelementowej algebry Boole’a (zob. 2.1.2), stąd w informatyce szczególną wagę przywiązuje się do systemu dwójkowego. W tym punkcie omówimy dokładnie ten system, konwersję z systemu dziesiętnego na dwójkowy i odwrotnie, oraz arytmetykę w systemie dwójkowym. Konwersja z systemu dwójkowego na system dziesiętny Korzystając z definicji pozycyjnego systemu liczbowego otrzymujemy, że podstawą systemu dwójkowego jest liczba 2, oraz cyframi tego systemu są elementy zbioru {0, 1}. Zapiszmy przykładową liczbę w tym systemie x = 1011110110(2) korzystając z (2.22), otrzymujemy w = 1 · 29 + 0 · 28 + 1 · 27 + 1 · 26 + 1 · 25 + + 1 · 24 + 0 · 23 + 1 · 22 + 1 · 21 + 0 · 20 Zastępując teraz potęgi liczby 2 odpowiadającymi im wartościami w systemie dziesiętnym, otrzymujemy w = 1 · 512 + 0 · 256 + 1 · 128 + 1 · 64 + 1 · 32 + + 1 · 16 + 0 · 8 + 1 · 4 + 1 · 2 + 0 · 1 = 758(10) Widać, że konwersja z systemu dwójkowego do systemu dziesiętnego sprowadza się do skorzystania ze wzoru (2.22), a następnie obliczenia wartości opisanej tym wyrażeniem. Należy pamiętać jedynie o tym, że potęgowanie, mnożenie i dodawanie wykonujemy już w systemie dziesiętnym, w wyniku czego otrzymujemy wartość również w systemie dziesiętnym.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

(2.23)

48

Podstawowe pojęcia i definicje

Zapiszmy teraz dla przykładu kolejne 8 liczb w systemie dziesiętnym oraz dwójkowym zapis dwójkowy 000 001 010 011 100 101 110 111 zapis dziesiętny 0 1 2 3 4 5 6 7

Konwersja z systemu dziesiętnego na system dwójkowy O ile zamiana z systemu dwójkowego na dziesiętny sprowadza się do dodawania i mnożenia, to algorytm odwrotny wymaga więcej uwagi. Załóżmy, że mamy daną liczbę x w systemie dziesiętnym, aby zapisać ją w systemie dwójkowym postępujemy według następującej procedury: 1. niech w = x, 2. dzielimy w przez 2, 3. jeśli wynikiem operacji dzielenia jest liczba całkowita, zapisujemy na boku 0, 4. jeśli wynikiem operacji dzielenie nie jest liczba całkowita — zostaje reszta, zapisujemy na boku 1, 5. całkowity wynik z dzielenia, po odrzuceniu ewentualnej reszty, zapisujemy jako w, 6. jeśli w jest różne od 0 przechodzimy z powrotem do kroku 2, 7. jeśli w jest równe 0 kończymy procedurę. Otrzymane w ten sposób cyfry 0 lub 1 zapisywane od prawa do lewa (bardzo ważne, że właśnie w takiej kolejności!) w kolejności ich otrzymywania dadzą nam oczekiwaną wartość w systemie dwójkowym. Zilustrujmy to przykładem, załóżmy, że chcemy zapisać liczbę 758 w systemie o podstawie 2. W tym celu dzielimy wpierw liczbę 758 przez 2, a następnie postępujemy dalej według powyższej procedury
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

2.2 Pozycyjne systemy liczbowe

49

758 | 2 x 379 + 0, tym razem dzielimy liczbę 379 przez 2 i zapisujemy poniżej poprzedniego wiersza, zatem mamy 758 | 2 x 379 + 0 379 | 2 x 189 + 1 analogicznie postępujemy dalej 758 379 189 94 47 23 11 5 2 1 | | | | | | | | | | 2 2 2 2 2 2 2 2 2 2 x x x x x x x x x x 379 189 94 47 23 11 5 2 1 0 + + + + + + + + + + 0 1 1 0 1 1 1 1 0 1

Powstała w ten sposób kolumna z prawej strony stanowi zapis liczby 758 w systemie o podstawie 2. Kolumnę tą czytając od dołu i pisząc od lewa, lub czytając od góry i pisząc od prawa, przekształcamy w ostateczną postać żądanej liczby 1011110110(2) , zauważmy, że otrzymaliśmy postać identyczną z (2.23), a jak sprawdziliśmy to wcześniej jest to liczba o wartości 758 zapisana w postaci dwójkowej. Przytoczymy kilka przykładów, powyższej procedury konwersji (przykład 2.2). Przykład 2.2. Ilustracja konwersji liczby zapisanej w systemie dziesiętnym na dwójkowy.

158 79 39 19 9

| | | | |

2 2 2 2 2

x x x x x

79 39 19 9 4

+ + + + +

0 1 1 1 1

108 54 27 13 6

| | | | |

2 2 2 2 2

x x x x x

54 27 13 6 3

+ + + + +

0 0 1 1 0

59 29 14 7 3

| | | | |

2 2 2 2 2

x x x x x

29 14 7 3 1

+ + + + +

1 1 0 1 1

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

50

Podstawowe pojęcia i definicje

4 2 1 0

| | | |

2 x 2 + 0 2 x 1 + 0 2 x 0 + 1 koniec

3 | 2 x 1 + 1 1 | 2 x 0 + 1 0 | koniec

1 | 2 x 0 + 1 0 | koniec

158(10) -> 10011110(2), 108(10) -> 1101100(2), 59(10) -> 111011(2)

Arytmetyka w systemie dwójkowym Komputery pracujące w oparciu o układy cyfrowe dokonują większość obliczeń na liczbach w systemie dwójkowych. Pokażemy teraz sposób wykonywania podstawowych działań na liczbach zapisanych w ten sposób. Dodawanie jest realizowane podobnie jak dla systemu dziesiętnego, należy jedynie pamiętać, że 1(2) + 1(2) = 10(2) wynika to z faktu, iż w systemie dwójkowym nie ma cyfry reprezentującej liczbę 2, 1 + 1 w tym systemie daje w wyniku 0 na pewnej pozycji, a jedność jest przenoszona na następną pozycję w liczbie. Jest to podoba sytuacja jak w przypadku dodawania 1 + 9 w systemie dziesiętnym, otrzymujemy w wyniku 0, a jedność jest przenoszona na następną pozycję. Przypatrzmy się następującym działaniom w przykładzie 2.3. Przykład 2.3. Dodawanie w systemie dwójkowym.

11 00111000 + 00010001 ---------01001001

01000001 + 00010100 ---------01010101

Odejmowanie podobnie jak dodawanie wykonywane jest według zasady identycznej jak w systemie dziesiętnym, przy czym w przypadku odejmowania 0 − 1 w systemie dwójkowym, musimy dokonać zapożyczenia 1 na następnej pozycji liczby. Obliczenia zawiera przykład 2.4
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

2.2 Pozycyjne systemy liczbowe

51

Przykład 2.4. Odejmowanie w systemie dwójkowym.

00111001 - 00001101 ---------00101100

00101101 - 00010001 ---------00011100

Mnożenie jest wykonywane analogicznie jak mnożenie w systemie dziesiętnym, ilustruje to przykład 2.5. Przykład 2.5. Mnożenie w systemie dwójkowym.

1111 x 101 ------------1111 0000 + 1111 ------------1001011

10001 x 11 --------------10001 + 10001 --------------110011

Dzielenie podobnie jak mnożenie wykonujemy tak samo jak w przypadku dzielenia w systemie dziesiętnym. Ilustrację tego procesu zawiera przykład 2.6. Przykład 2.6. Dzielenie w systemie dwójkowym.

110 ----10010:11=00000110 - 11

1011 ------1111001:1011=1011 - 1011

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

52

Podstawowe pojęcia i definicje

------1 11 11 ------0

--------10000 1011 --------1011 1011 --------0

2.2.2

Zapis liczby rzeczywistej w systemie dwójkowym

Do tej pory zamiana z i do systemu dwójkowego była ograniczona wyłącznie do liczb całkowitych. Pozostaje zatem do wyjaśnienia w jaki sposób należy reprezentować liczbę rzeczywistą w systemie dwójkowym 2 . W celu lepszego zrozumienia zapisu liczb rzeczywistych w systemie dwójkowym, przypomnijmy to co było omawiane na początku punktu 2.2. Zauważmy, że dla liczby całkowitej było powiedziane, że dopiero pozycja cyfry wraz z jej wartością niesie pełną informację. Podobnie ma się sprawa z częścią ułamkową. Jeśli mamy liczbę w systemie dziesiętnym postaci: 0, c−1 c−2 ...c−n gdzie c−1 , c−2 , . . . , c−n ∈ {0, . . . , 9} są cyframi, to wartość tej liczby jest wyliczana za pomocą następującego wzoru: w = c−1 10−1 + c−2 10−2 + · · · + c−n 10−n . Jeżeli teraz nasze rozważania przeniesiemy do systemu dwójkowego, to wartość będzie wyliczana na podstawie podobnego wzoru, zmieni się jedynie podstawa, zatem: w = c−1 2−1 + c−2 2−2 + · · · + c−n 2−n , oczywiście w tym wypadku cyfry są elementami zbioru {0, 1}.
2 Rozważania prowadzone w tym punkcie mają charakter informacyjny, gdyż będą potrzebne w następnym rozdziale. Jednak nie należy postrzegać prezentowanego zapisu liczby rzeczywistej jako tego, który jest stosowany w komputerze w sposób bezpośredni. W następnym rozdziale będzie to wytłumaczone obszernie.

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

2.2 Pozycyjne systemy liczbowe

53

Stosując powyższy wzór jesteśmy w stanie bezpośrednio zamienić ułamek zapisany w systemie dwójkowym na system dziesiętny. Rozważmy poniższy przykład: Przykład 2.7. Zamiana ułamka dwójkowego na dziesiętny.

0.11(2) = 2−1 · 1 + 2−2 · 1 = 0.5 + 0.25 = 0.75(10)

Do wyjaśnienia pozostaje sposób zamiany w drugą stronę, czyli jak zamieniać ułamek dziesiętny na dwójkowy. Ograniczymy się w tym miejscu wyłącznie do ułamków dodatnich. Schemat postępowania jest podobny do przedstawionych wcześniej. Mamy następujący algorytm (ułamek zapisany dziesiętnie oznaczmy przez x): 1. niech w = x, 2. mnożymy w przez 2, 3. jeśli wynikiem operacji mnożenia jest liczba większa od jedności, zapisujemy na boku 1, 4. jeśli wynikiem operacji mnożenia jest liczba mniejsza od jedności, zapisujemy na boku 0, 5. ułamkową część wyniku — po odrzuceniu ewentualnej części całkowitej, zapisujemy jako w, 6. jeśli w jest różne od 0 przechodzimy z powrotem do kroku 2, 7. jeśli w jest równe 0 kończymy procedurę. Następnie otrzymane zera i jedynki zapisywane w kolejności otrzymywania od lewej do prawej stanowią ułamek zapisany w systemie dwójkowym. Zilustrujmy powyższy algorytm przykładem. Przykład 2.8. Zamiana ułamka dziesiętnego na ułamek dwójkowy.

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

54

Podstawowe pojęcia i definicje

0.75(10) = ? (2) liczba dwójkowa | | część ułamkową przepisujemy | | do pierwszej kolumny 0.75 | 2 x 0.75 = 1.5 0.5 | 2 x 0.5 = 1.0 0.0 | koniec

Otrzymaliśmy zatem, że 0.75(10) = 0.11(2) Jak widać otrzymaliśmy dokładnie wartość z poprzedniego przykładu. Wykonajmy następne obliczenia: 0.625(10) = ? (2) 0.625 0.25 0.5 0.0 | | | | 2 x 0.625 = 1.25 2 x 0.25 = 0.5 2 x 0.5 = 1.0 koniec 0.625(10) = 0.101(2) Sprawdźmy: 2−1 · 1 + 2−2 · 0 + 2−3 · 1 = 0.5 + 0.125 = 0.625 I kolejny przykład 0.40625(10) = ? (2) 0.40625 0.8125 0.625 0.25 0.5 0.0 | | | | | | 2 x 0.40625 2 x 0.8125 2 x 0.625 2 x 0.25 2 x 0.5 koniec = = = = = 0.8125 1.625 1.25 0.5 1.0

Zatem otrzymujemy

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

2.2 Pozycyjne systemy liczbowe

55

Zatem 0.40625(10) = 0.01101(2) Sprawdźmy: 2−1 · 0 + 2−2 · 1 + 2−3 · 1 + 2−4 · 0 + 2−5 · 1 = 0.25 + 0.125 + 0.03125 = 0.40625 Przejdźmy teraz do zamiany liczb rzeczywistych większych od jeden, w takim przypadku, stosujemy dla części całkowitej poznany wcześniej proces, a dla części ułamkowej omówiony powyżej. Przykład 2.9. Zamiana liczby rzeczywistej dziesiętnej na postać dwójkową. 9.25(10) = ? (2) 9 4 2 1 0 | | | | | 2 x 4 + 2 x 2 + 2 x 1 + 2 x 0 + koniec 1 0 0 1 0.25 | 2 x 0.25 = 0.5 0.5 | 2 x 0.5 = 1.0 0.0 | koniec

Zatem otrzymujemy ostatecznie 9.25(10) = 1001.01(2) Wykonajmy jeszcze jeden przykład 15.375(10) = ? (2) 15 7 3 1 0 | | | | | 2 x 7 + 2 x 3 + 2 x 1 + 2 x 0 + koniec 1 1 1 1 0.375 0.75 0.5 0.0 | | | | 2 x 0.375 = 0.75 2 x 0.75 = 1.5 2 x 0.5 = 1.0 koniec

Stąd 15.375(10) = 1111.011(2) Na koniec zauważmy, że nie każdą liczbę którą da się zapisać w postaci ułamka dziesiętnego da się dobrze przedstawić w postaci ułamka dwójkowego, co ilustruje poniższy przykład:
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

56

Podstawowe pojęcia i definicje

Przykład 2.10. Zamiana ułamka dziesiętnego na ułamek dwójkowy okresowy. 0.3 (10) = ? (2) 0.3 0.6 0.2 0.4 0.8 0.6 | | | | | | 2 x 2 x 2 x 2 x 2 x ... 0.3 0.6 0.2 0.4 0.8 = = = = = 0.6 1.2 0.4 0.8 1.6

Zatem jak widać ułamek może mieć „dobrą” postać dziesiętną, zaś „złą” dwójkową. W powyższym przykładzie ułamek dwójkowy jest okresowy, zatem w którymś momencie musimy zaprzestać procedury, gdyż inaczej prowadzilibyśmy ją w nieskończoność. Jeśli jednak zaprzestaniemy, to będzie to niedokładna reprezentacja wyjściowego ułamka, dla przykłady ograniczmy się do pięciu cyfr po przecinku, otrzymamy wtedy: 0.01001(2) = 0.25 + 0.03125 = 0.28125(10) . Jak widać błąd takiego zaokrąglenie wcale nie jest mały, jednak pamiętajmy, że nie jest to „wina” systemu dwójkowego lecz samego procesu dzielenia w 1 ogóle. Zauważmy, że dla ułamka zwykłago 3 jego rozwinięcie w ułamek dziesiętny również będzie przybliżone jeśli chcemy je przechować na skończonym nośniku danych.

2.2.3

Kod szesnastkowy

Wadą systemu dwójkowego jest „rozwlekłość” zapisywanych w ten sposób liczb. Dla człowieka bywa trudne zapamiętanie ciągu zer i jedynek. Stąd też często w praktyce informatyka spotykamy się z zapisem liczby w systemie szesnastkowym lub kodzie szesnastkowym. Z definicji pozycyjnego systemu liczbowego (zob. 2.2) wynika, że w systemie szesnastkowym musimy mieć szesnaście cyfr, ale jak sobie poradzić w sytuacji, gdy mamy do dyspozycji cyfry arabskie w zakresie 0, 1, . . . , 9. Problemem ten rozwiązano poprzez nazwanie kolejnych cyfr tego systemu, poczynając od dziesięciu,
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

2.2 Pozycyjne systemy liczbowe

57

kolejnymi literami alfabetu, zatem A (16) = 10(10) , B(16) = 11(10) , itd., dopuszcza się również stosowanie małych liter zamiast dużych. Można oczywiście zapytać, czemu nie stosujemy w informatyce systemu osiemnastkowego, przyjrzyjmy się poniższej tabelce a wszystko się wyjaśni zapis dwójkowy 0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 1111 zapis szesnastkowy 0 1 2 3 4 5 6 7 8 9 A B C D E F zapis dziesiętny 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

Widać z powyższego zestawienia, że jedna cyfra kodu szesnastkowego odpowiada dokładnie czterocyfrowej liczbie systemu dwójkowego. Ponieważ bajt informacji (zob. A) składa się z 8 bitów, zatem każdy bajt danych daje się w sposób jednoznaczny przedstawić za pomocą dwu cyfr kodu szesnastkowego, co upraszcza kwestię konwersji; nie wymaga ona w praktyce obliczeń a jedynie podstawienia. Oto przykład 00011101(2) = 1D(16) , 1011111(2) = BF(16) , 11100011(2) = E3(16) .

2.2.4

Inne pozycyjne systemy liczbowe

Punkt ten należy traktować jako informację poszerzającą wiedzę ogólną, gdyż inne systemy liczbowe niż dwójkowy i szesnastkowy praktycznie nie są wykorzystywane w informatyce, lub ich obecność jest marginalna. Dla przykładu niekiedy można spotkać zapis liczby w systemie ósemkowym. Korzystając z definicji pozycyjnego systemu liczbowego, zapiszemy tą samą liczbę w systemach pozycyjnych o różnych podstawach. Przykład 2.11 zawiera ilustrację tego procesu.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

58

Podstawowe pojęcia i definicje

Przykład 2.11. Liczba 758 zapisana w pozycyjnych systemach liczbowych o różnych podstawach. 233124 = 2 · 44 + 3 · 43 + 3 · 42 + 1 · 41 + 2 · 40 = 75810 13668 = 1 · 83 + 3 · 82 + 6 · 81 + 6 · 80 = 75810 2F 616 = 2 · 162 + 15 · 161 + 6 · 160 = 75810

Jedną z możliwych metod przejścia od zapisu w systemie o podstawie p 1 do systemu o podstawie p2 jest zamiana zapisu liczby na system dziesiętny, a dopiero potem na system o podstawie p 2 . Metoda ta wydaje się naturalna, gdyż ludzie najsprawniej operują systemem dziesiętnym. Ilustruje to poniższy przykład Przykład 2.12. Zamiana liczby z systemu 25 na system 11 poprzez system dziesiątkowy. Wpierw zamieniamy liczbę z systemu o podstawie 25 na liczbę w systemie dziesiątkowym 2K25 = 2 · 251 + K · 250 = 2 · 25 + 20 · 1 = 7010 następnie z systemu dziesiątkowego na jedenastkowy 70 | 11 x 6 + 9 6 | 11 x 0 + 6 Ostatecznie więc otrzymujemy 2K25 = 6911

Zauważmy jednocześnie, że w powyższym przykładzie, przy zapisie liczby w systemie dwudziestopiątkowym, przyjęliśmy podobną notację cyfr jak w systemie szesnastkowym, zatem wykorzystaliśmy kolejne litery alfabetu. W szczególnych przypadkach można proces ten jednak znacznie uprościć. Przyjrzyjmy się przykładowym liczbą w zapisie dwójkowym, czwórkowym i dziesiętnym.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

2.2 Pozycyjne systemy liczbowe

59

zapis dwójkowy 00 01 10 11

zapis czwórkowy 0 1 2 3

zapis dziesiętny 0 1 2 3

110110012 → 31214 33214 → 111110012 Zauważmy, że zamieniając liczbę dwójkową na czwórkową, grupujemy bity po dwa, a następnie te dwójki zapisujemy za pomocą odpowiednich liczb czwórkowych (podobnie czyniliśmy to w zamianie z systemu dwójkowego na szesnastkowy). Zapisując liczbę czwórkową jako dwójkową, każdą jej cyfrę zapisujemy za pomocą odpowiedniego dwu wyrazowego ciągu zerojedynkowego. 11 01 10 01 | | | | 3 1 2 1 3 3 2 1 | | | | 11 11 10 01

Teraz zapiszmy kolejne liczby w systemach dwójkowym, ósemkowym i dziesiętnym: zapis dwójkowy 000 001 010 011 100 101 110 111 zapis ósemkowy 0 1 2 3 4 5 6 7 zapis dziesiętny 0 1 2 3 4 5 6 7

W tym przypadku aby dokonać przeliczenia, możemy grupować cyfry kodu dwójkowego po 3. 011 011 001 | | | 3 3 1
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

60

Podstawowe pojęcia i definicje

Otrzymujemy zatem 110110012 → 3318 3318 → 110110012 Do konwersji z systemu dziesiętnego na inny pozycyjny system liczbowy, stosujemy analogiczny sposób jak opisany w punkcie 2.2.1, z tą różnicą, że tym razem resztą z dzielenia mogą być nie tylko cyfry zero lub jeden. Przykład 2.13 ilustruje tę procedurę Przykład 2.13. Przykład ilustrujący zamianę liczby 758 zapisanej dziesiętnie na system czwórkowy, ósemkowy i szesnastkowy

758 (10) -> (4) 758 189 47 11 2 | | | | | 4 4 4 4 4 x x x x x 189 47 11 2 0 + + + + + 2 1 3 3 2

758 (10) -> (8) 758 94 11 1 | | | | 8 8 8 8 x x x x 94 11 1 0 + + + + 6 6 3 1

758 (10) -> (16) 758 | 16 x 47 + 6 47 | 16 x 2 + 15 (F) 2 | 16 x 0 + 2

758(10) -> 23312(4)

758(10) -> 1366(8)

758(10) -> 2F6(16)

Nic nie stoi na przeszkodzie aby stosować systemy liczbowe o podstawach różnych od potęgi 2. Niech na przykład liczba 2K będzie liczbą zapisaną w systemie o podstawie 25. Zapiszemy ją teraz jako liczbę systemu o podstawie 11. Najbardziej naturalna drogą, zasugerowana na początku tego podrozdziału, to konwersja (25) → (10) → (11) 2K25 = 2 · 251 + K · 250 = 2 · 25 + 20 · 1 = 7010 70 | 11 x 6 + 9 6 | 11 x 0 + 6 Ostatecznie więc 2K25 = 6911
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

2.2 Pozycyjne systemy liczbowe

61

Innym podejściem jest pominięcie pośredniego kroku w postaci przeliczania na system dziesiętny i operowanie od razy wyłącznie na systemie źródłowym i docelowym. W przypadku dokonywania konwersji z systemu o mniejszej podstawie do większej nie nastręcza to żadnych trudności. W tym wypadku ma zastosowanie wzór 2.22, jednak należy pamiętać, że wszelkie obliczenia muszą być prowadzone w arytmetyce docelowego systemu liczbowego. Rozważmy poniższy przykład, niech będzie dana liczba 1111101 (2) jak widać zapisana w systemie dwójkowym, załóżmy teraz, że chcemy dokonać konwersji na system szesnastkowy. Napiszmy wzór na wartość liczby w = 1 · 26 + 1 · 25 + 1 · 24 + 1 · 23 + 1 · 22 + 0 · 21 + 1 · 20 teraz wszystkie obliczenie po prawej stronie równości należy prowadzić w arytmetyce szesnastkowej, zatem w = 40 + 20 + 10 + 8 + 4 + 0 + 1 = 70 + D = 7D(16) co jak widać zgadza się ze zwykłą regułą prezentowaną w punkcie 2.2.3. Niestety sytuacja komplikuje się przy dokonywaniu odwrotnej konwersji, wtedy bowiem dzielimy liczbę w systemie źródłowym przez podstawę systemu docelowego, oraz należy pamiętać o resztach, których ilość jaką się rozważa jest zależna od systemu docelowego. Weźmy liczbę 96 (16) i zamieńmy ją na system trójkowy, w tym celu przeprowadzamy następującą serię dzieleń 96(16) -> (3) 96 32 10 05 01 | | | | | 3 3 3 3 3 x x x x x 32 10 05 01 00 + + + + + 0 2 1 2 1

96(16) -> 12120(3) Wykonajmy sprawdzenie powyższej konwersji, zamieniając postacie w obu systemach na system dziesiętny 96(16) = 9 · 161 + 6 · 160 = 144 + 6 = 150(10) 12120(3) = 1 · 34 + 2 · 33 + 1 · 32 + 2 · 31 + 0 = 81 + 54 + 9 + 6 + 0 = 150(10) .
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

62

Podstawowe pojęcia i definicje

2.3

BCD

Do tej pory zapoznaliśmy się już z systemem dwójkowym, szesnastkowy oraz innymi. Jak można było zaobserwować, istotnych różnic pomiędzy nimi nie ma i w zasadzie to jesteśmy w stanie operować dowolnym z nich, a nawet kilkoma, dokonując odpowiednich konwersji przy przejściu od jednego do drugiego. Mimo to cały czas odczuwa się jak gdyby wrodzoną skłonność do operowania systemem dziesiętnym, przejawiającą się chociażby w tym, że w konwersji najczęściej stosujemy schemat podstawa źródłowa → podstawa 10 → podstawa docelowa. Cały kłopot z systemami liczbowymi o podstawach innych niż 10 polega na tym, że ciąg cyfr jest dla nas abstrakcyjnym napisem, który nabiera znaczenia dopiero, gdy przekształcimy go do postaci dziesiętnej3 . Zilustrujemy problemy o których mówimy za pomocą prostego testu. Kto bez zastanowiednia odpowie, ile wynosi 1/3 z liczby 111100 (2)? Jak więc widać, systemem dwójkowym operujemy z konieczności, a najchętniej (najsprawniej) używamy dziesiętnego. Problemem jest konwersja z jednego na drugi. Chcąc ułatwić to zadanie wprowadzono system kodowania BCD (ang. Binary-Coded Decimal). W tym systemie liczby dziesiętne kodujemy za pomocą ciągu bitów przypisując każdej cyfrze dziesiętnej odpowiadajacy jej ciąg 4 bitów (patrz tabelka 2.1). Ponieważ mamy jedynie 10 możliwości więc wystarczy zapamiętać tylko tyle, aby móc sprawnie i szybko przechodzić z systemu dziesiętnego do BCD i odwrotnie. Zobaczmy jak odbywa się ten proces; zapiszemy liczbę dziesiętną 120 najpierw w systemie dwójkowym a następnie w formacie BCD. 120 (10) => ? (2) 120 60 30 15 7 3 1 | | | | | | | 2 2 2 2 2 2 2 * 60 + 0 * 30 + 0 * 15 + 0 * 7 + 1 * 3 + 1 * 1 + 1 * 0 + 1

120 (10) = 1111000 (2)
Oczywiście wynika to z przyzwyczajenia i wychowania w określonej kulturze. Kiedyś dla przykładu liczono tuzinami, czy kopami.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004 3

2.3 BCD

63

Cyfra dziesiętna 0 1 2 3 4 5 6 7 8 9 -

„Cyfra” BCD 0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 1111

Tablica 2.1: Cyfry dziesiętne i odpowiadające im „cyfry” BCD.

Przykład 2.14. Zamiana liczby dziesiętnej na jej postać w kodzie BCD.

(10) => (BCD) 1 (10) = 0001 (BCD) 2 (10) = 0010 (BCD) 0 (10) = 0000 (BCD) 120 (10) = 00010010000 (BCD)

Łatwo można poczynić następujące spostrzeżenia: • Pomimo, iż kod BCD do zapisu używa cyfr dwójkowych to otrzymany ciąg różny jest od odpowiadającej liczby dwójkowej.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

64

Podstawowe pojęcia i definicje

• Konwersja 10 → BCD lub BCD → 10 odbywa się na podobnych zasadach jak konwersja 2 → 16 lub 16 → 2 — grupujemy bity po 4. • Kod BCD wymaga więcej pamięci niż „tradycyjny” zapis dwójkowy. • Konwersja jest naturalna i odbywa się prawie natychmiast. Przyjrzyjmy się jeszcze dwu przykładom. 751 (10) = 1011101111 (2) = 011101010001 (BCD) 7 (10) = 0111 (BCD) 5 (10) = 0101 (BCD) 1 (10) = 0001 (BCD) 1072 (10) = 10000110000 (2) = 0001000001110010 (BCD) 1 0 7 2 (10) (10) (10) (10) = = = = 0001 0000 0111 0010 (BCD) (BCD) (BCD) (BCD)

W kodzie BCD oczywiście można wykonywać działania arytmetyczne, ilustruje to przykład 2.15. Na przykładzie tym pokazano dodawanie w kodzie BCD i tylko do dodawania ograniczymy się przy omawianiu działań. Przykład 2.15. Dodawanie bez przeniesienia w kodzie BCD.

142 (10) = 000101000010 (BCD) 311 (10) = 001100010001 (BCD) + -------------------------------010001010011 | | | | | 0011 (BCD) = 3 (10) | | | 0101 (BCD) = 5 (10) | 0100 (BCD) = 4 (10)

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

2.3 BCD

65

Niestety nie zawsze wszystko jest tak dobrze... zauważmy, że możemy w wyniku dodawania uzyskać na pewnej pozycji liczbę większą od 9, co spowoduje konieczność dokonania przeniesienia, co ilustruje przykład 2.16. Przykład 2.16. Ilustracja wystąpienia przeniesienia w dodawaniu w kodzie BCD.

9 (10) = 1001 (BCD) 6 (10) = 0110 (BCD) + ---------------------15 (10) = 1111 (BCD) ????

Otrzymany w przykładzie 2.16 wynik nie jest poprawną „cyfrą” kodu BCD! Pojawia się konieczność korekcji otrzymanych wyników. Korekcja polega na dodaniu liczby dziesiętnej 6 ( 0110 (BCD)) do niepoprawnej grupy 4 bitów; ma to miejsce gdy „cyfra” BCD jest większa od 1001 lub gdy nastąpi przeniesienie z jednej czwórki bitów do następnej, ilustruje to przykład 2.17. Przykład 2.17. Dodawanie w kodzie BCD z uwzględnieniem przeniesienia.

60 (10) = 01100000 (BCD) 55 (10) = 01010101 (BCD) + ---------------------------115 (10) = 10110101 korekcja 0110 + --------------------100010101 000100010101 | | | | | 0101 (BCD) = 5 (10) | | | 0001 (BCD) = 1 (10) | 0001 (BCD) = 1 (10)
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

66

Podstawowe pojęcia i definicje

99 (10) = 10011001 (BCD) 99 (10) = 10011001 (BCD) + ---------------------------189 (10) = 100110010 korekcja 01100110 + --------------------110011000 000110011000 | | | | | 1000 (BCD) = 8 (10) | | | 1001 (BCD) = 9 (10) | 0001 (BCD) = 1 (10)

99 (10) = 10011001 (BCD) 11 (10) = 00010001 (BCD) + ---------------------------110 (10) = 10101010 korekcja 01100110 + --------------------100010000 000100010000 | | | | | 0000 (BCD) = 0 (10) | | | 0001 (BCD) = 1 (10) | 0001 (BCD) = 1 (10)

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

2.4 Zadania

67

2.4

Zadania

1. Wykonaj uproszczenie następujących wyrażeń algebraicznych, a następnie sprawdź prawdziwość metodą zerojedynkową. a) b) c) d) e) xyz + xy + yz xz + xy + yz (x + y + z)(y + z) (xy + z)(x + yz) (xyz) + (x + y + z)

2. Dokonaj konwersji zapisu następujących liczb z systemu o podstawie 10 na liczby w systemach o podstawach 2, 8 i 16. a) f) 172 107 b) g) 517 300 c) h) 778 201 d) i) 13 472 e) j) 76 802

3. Dokonaj konwersji zapisu następujących liczb z systemu o podstawie 2 na liczby w systemach o podstawach 8, 16 i 10. a) c) e) g) i) 11100 10001010001 1110011100 1001001100 10100001 b) d) f) h) j) 1011110001 100100100 101110001 101010000 1000101

4. Dokonaj konwersji zapisu następujących liczb z systemu o podstawie 16 na liczby w systemach o podstawach 2, 8 i 10. a) f) F2 123 b) g) 11F FF c) h) AAA F0 d) i) 100 BAB e) j) 1AB 307

5. Dokonaj konwersji zapisu następujących liczb z systemu o podstawie 8 na liczby w systemach o podstawach 2, 16 i 10. a) f) 123 10 b) g) 457 27 c) h) 177 55 d) i) 65 222 e) j) 22 512

6. Dokonaj konwersji zapisu liczby z systemu o podstawie 7 na system o podstawie 5. a) f) 565 306 b) g) 100 255 c) h) 62 32 d) i) 12 836 e) j) 166 56

7. Dokonaj konwersji zapisu liczby z systemu o podstawie 5 na system o podstawie 11.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

68

Podstawowe pojęcia i definicje

a) f)

1234 1121

b) g)

4222 2041

c) h)

2131 4131

d) i)

1421 3211

e) j)

3123 3114

8. Dokonaj konwersji zapisu liczby z systemu o podstawie 13 na system o podstawie 9. a) f) C99 910 b) g) 2A5 B7 c) h) 91 18 d) i) 65 301 e) j) 3BC 40C

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

Rozdział 3

Architektura i działanie komputera
3.1 Maszyna Turinga

U podstaw definicji Maszyny Turinga legła pewna obserwacja psychologiczna dotycząca sposobu odczytywania informacji przez człowieka. Alan Turing zauważył, że niezależnie od sposobu zapisu informacji (np. różne rodzaje alfabetu) oraz od systemu zapisu 1 , człowiek czytając zawsze robi to sekwencyjnie.

2 + 4 = 6 - 4 =

Rysunek 3.1: Ilustracja procesu czytania przez człowieka. Spójrzmy na rysunek (3.1); odczytując znajdującą się na nim informację, wpierw czytamy liczbę 4, następnie operator +, z kolei drugi arguZauważmy, że w różnych kulturach informację zapisuje się od lewa do prawa, od prawa do lewa, lub z góry na dół.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004 1

70

Architektura i działanie komputera

2 + 4 =

6 - 4 =

Rysunek 3.2: Kartka przekształcona w taśmę

ment dla tego operatora, czyli liczbę 3. Na koniec napotykamy na operator =, który interpretujemy jako polecenie wykonania wcześniejszego działania. W ostatniej kratce zapisujemy wynik operacji. Zauważmy, że tylko dzięki wiedzy wyniesionej ze szkoły wiemy co znaczą poszczególne symbole, w szczególności + czy =. Z powyższego przykładu widać, że informacja była odczytywana znak po znaku, lub symbol po symbolu, zatem sekwencyjnie. W dalszym ciągu Turing zauważył, że nic nie stoi na przeszkodzie, by naszą przykładową kartkę rozciąć na kolejne „wiersze” działań. A następnie wiersze te skleić, tak by następowały jeden po drugim (rysunek 3.2). W ten sposób uzyskaliśmy taśmę, która zawiera dokładnie takie same polecenia jak macierzysta kartka z przykładami, różni się od niej jedynie formą. Powyższy przykład jest nakreśleniem idei, która pozwoliła Turingowi na konstrukcję jego maszyny. Maszyna ta składała się z głowicy czytającopiszącej (odpowiednik naszych oczu i ręki), oraz jednostki wykonującej (odpowiednik naszego umysłu). Głowica znajdowała się nad taśmą z której odczytywano zapisane informacje oraz na którą zapisywano wyniki. Turing zakładał, że maszyna ta będzie umiała: przesuwać taśmę 2 , odczytywać za pomocą głowicy znajdująca się na taśmie informację (głowica zawsze widzi tylko jeden fragment taśmy leżącej pod nią), zapisywać informacje z powrotem na taśmie, wykonywać pewien skończony zbiór operacji. Należy w tym miejscu podkreślić, że jako maszynę możemy rozważać wyłącznie jej matematyczny model, wcale nie trzeba takiej maszyny konstruować fizycznie.

3.1.1

Definicja Maszyny Turinga

Formalnie poprzez Maszynę Turinga (MT) będziemy rozumieli uporządkowaną czwórkę M = (Q, Σ, δ, s), gdzie:
Faktycznie Turing zakładał, że to głowica będzie się przesuwała nad taśmą, co oczywiście nie ma wpływu na dalsze rozważania.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004 2

3.1 Maszyna Turinga

71

• Q — skończony zbiór stanów , w których może znaleźć się maszyna. • Σ — skończony zbiór dopuszczalnych symboli taśmowych, zwany również alfabetem. Zbiór ten musi zawierać co najmniej dwa specjalne symbole i , które oznaczają odpowiednio symbol pusty, oraz symbol końcowy. • δ — funkcja następnego ruchu, która jest odwzorowaniem δ : Q × Σ −→ (Q ∪ {k, t, n} × Σ) × {←, →, −}.3 Gdzie k — stan końcowy, t — stan akceptujący, n — stan odrzucający, zaś ←, →, − oznaczają odpowiednio ruch głowicy w lewo, w prawo oraz pozostanie w miejscu, przy czym ostatnie trzy symbole nie należą do sumy Q ∪ Σ. • s – stan początkowy, jest to pewien wyróżniony stan należący do zbioru Q. Z powyższego określenia wynika, że zbiory Q i Σ są zbiorami niepustymi, zakładamy również, że mają one być rozłączne. Funkcja następnego ruchu często jest również określana jest mianem diagramu (lub tablicy) przejść. Definiuje ona dla każdego stanu, stan następny, w którym maszyna ma się znaleźć po odczytaniu symbolu z taśmy, ponadto określa, czy zostanie wykonany ruch głowicy. Co więcej maszyna może się znaleźć w pewnych specjalnych stanach, zatem może się po prostu zatrzymać — stan k, może się zatrzymać i będzie to oznaczało akceptację czegoś — stan t, bądź zatrzymać i odpowiedzieć przecząco — stan n. W istocie MT nie można uznać za kompletny model komputera, ale wiele jego składników można zamodelować przy pomocy tej maszyny, na przykład pamięć RAM. Pokażemy teraz dwa proste przykłady Maszyn Turinga. Przykład 3.1. Nasza Maszyna Turinga 1 (NMT1): zamiana symboli. Niech alfabet Σ składa się z symboli a, b, oraz koniecznych symboli pustego i końcowego, zatem Σ = { , , a, b}. Przyjmijmy poniższy diagram przejść: Stan bieżący s s s s Odczytany symbol a b Symbol do zapisu b a Kierunek ruchu głowicy → → → → Stan kolejny s s k k

3

Funkcja δ może nie być określona dla wszystkich argumentów.

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

72

Architektura i działanie komputera

Diagram ten można zapisać również w innej, bardziej sformalizowanej formie:

q∈Q s s s S

σ∈Σ a b

δ(q, σ) (s, b, →) (s, a, →) (k, , →) (k, , →)

Sytuację od której rozpoczniemy analizowanie NMT1 przedstawiono poniżej: | a |b|b|a| | | ... taśma

NMT1 rozpoczyna działanie w stanie s, odczytując zawartość aktualnie wskazywanej komórki taśmy (na rysunku powyżej oznaczono to prostokątem). Jest tam znak a zatem, zgodnie z tabelką, NMT1 zapisuje w tej komórce znak b, przesuwa głowicę w prawo (→) i przechodzi do stanu s. Ponownie odczytuje aktualnie wskazywany przez głowicę znak, jest nim b, zatem zapisuje w tej komórce znak a ... itd.

Jak widać, zdefiniowany w powyższym przykładzie „komputer” zamienia po prostu znak a na b i b na a. W kolejnym przykładzie, alfabet jest szerszym zbiorem i składa się z symboli Σ = { , , a, b, c, ?}. NMT2 (przykład 3.2) będzie wykonywała proces powielenia symboli, można ją nazwać „samoreplikującym się robakiem”.

Przykład 3.2. Nasza Maszyna Turinga 2: samoreplikujący się robak Tabela stanów:
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

3.1 Maszyna Turinga

73

Stan bieżący s

Odczytany symbol a b c a b c

Symbol do zapisu ? ? ? a b c a a b c a a b c b a b c b a b c c a b c c

sa

saa

sb

a b c ? a b c

sbb

sc

a b c ? a b c

scc

a b c ?

Kierunek ruchu głowicy → → → − → → → ← − ← ← ← → → → → ← − ← ← ← → → → → ← − ← ← ← →

Stan kolejny sa sb sc k sa sa sa saa k saa saa saa s sb sb sb sbb k sbb sbb sbb s sc sc sc scc k scc scc scc s

Poniższa tabela pokazuje jak zmieniać się będzie zawartość taśmy podczas wykonywania programu. Ramka wskazuje komórkę znajdującą się pod głowicą.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

74

Architektura i działanie komputera

Stan automatu s sa sa sa sa saa saa saa saa s sb sb sb sb sb sbb sbb sbb s sb sb sb sb sbb sbb sbb sbb s sc sc sc sc scc scc scc scc s

a ? ? ? ? ? ? ? ? a a a a a a a a a a a a a a a a a a a a a a a a a a a a

b b b b b b b b b b ? ? ? ? ? ? ? ? b b b b b b b b b b b b b b b b b b b

b b b b b b b b b b b b b b b b b b b ? ? ? ? ? ? ? ? b b b b b b b b b b

Zawartość taśmy c c c c c c a c a c a c a c a c a c a c a c a c a c a c a c a c a c a c a c a c a c a c a c a c a c a ? a ? a ? a ? a ? a ? a ? a ? a c a

b b b b b b b b b b b b b b b b b b b b b b b

b b b b b b b b b b b b b b

c c c c c

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

3.2 Bramki logiczne

75

W tym miejscu nasuwa się wniosek, że każdy program zapisany przy użyciu MT jest rozwlekły i wymaga ogromnej uwagi w czasie pisania, aby nie popełnić błędu. Skoro jest to takie nieporęczne, to po co się nim właściwie zajmować? Powód, dla którego to czynimy, to teza Churcha. Opiszemy nieformalnie o czym mówi ta teza, otóż dla dowolnego algorytmu możemy stworzyć odpowiadającą mu Maszynę Turinga 4 . Idąc dalej tym tokiem rozumowania można założyć, że dowolny problem mający rozwiązanie na znanych nam obecnie popularnych komputerach, można rozwiązać konstruując odpowiadającą mu Maszynę Turinga. Nie szkodzi, że będzie to konstrukcja bardzo złożona (a raczej nużąca). Ważne jest, że jak by nie była złożona, i realizowała nie wiadomo jak skomplikowane zadania, to cały problem można sprowadzić do bardzo elementarnych działań — manipulowania symbolami z pewnego ograniczonego alfabetu według ściśle określonych reguł.

3.2

Bramki logiczne

Dzisiejsze komputery konstruowane są głównie w oparciu o cyfrowe układy scalone. W tym punkcie omówimy podstawowy składnik wszystkich cyfrowych układów scalonych — bramkę logiczną (lub krótko bramkę). Funkcje logiczne realizowane przez cyfrowy układ scalony są wykonywane przez zespół bramek logicznych połączonych w różny sposób ze sobą. Bramka jest układem elektronicznym, którego sygnał wyjściowy jest wynikiem operacji Boole’a na sygnałach wejściowych. Podstawowymi bramkami stosowanymi w cyfrowych układach logicznych są bramki logicznego iloczynu (oznaczenie AND), logicznej sumy (OR), oraz logicznej negacji (NOT), odpowiadają one trójce zdefiniowanych wcześniej działań w algebrze Boole’a (zob. 2.1). Również często spotykanymi bramkami są negacje iloczynu i sumy logicznej, oznaczane odpowiednio NAND i NOR. Poniżej przedstawiamy działania realizowane przez te bramki.
Jest to teza, którą nie umiemy udowodnić, ponieważ nie da się precyzyjnie zdefniować „dowolnego algorytmu”.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004 4

76

Architektura i działanie komputera

X Y

XY

X Y

X+Y

X

X

AND
X Y

OR
XY X Y

NOT
X+Y

NAND
X Y

NOR
X ⊗Y

XOR

Rysunek 3.3: Symbole reprezentujące bramki AND, OR, NOT, NAND, NOR i XOR.

Symbol X Y 0 0 0 1 1 0 1 1

NOT X 1 1 0 0

AND X ·Y 0 0 0 1

OR X +Y 0 1 1 1

NAND X·Y 1 1 1 0

NOR X +Y 1 0 0 0

Elektronicy przypisali bramkom pewne symbole graficzne w celu ich łatwiejszego zobrazowania na rysunkach zwanych schematami ideowymi (rys. 3.2). Podstawowe bramki logiczne mają jedno lub dwa wejścia i jedno wyjście. W praktyce często stosuje się bramki o większej ilości wejść. Dzięki temu wyrażenie X + Y + Z może być zrealizowane za pomocą jednej bramki OR o trzech wejściach. Zwykle nie wszystkie bramki są używane do implementacji. Projektowanie i wytwarzanie cyfrowego układu scalonego jest tym prostsze, im używa się mniej różnych rodzajów bramek. Okazuje się, że wszystkie operacje logiczne można zrealizować za pomocą jednej lub kilku bramek logicznych. Czyli jedne operacje logiczne można zastąpić sekwencją innych. Stąd, zbiory bramek za pomocą których można zrealizować wszystkie pozostałe operacje logiczne nazywamy zbiorami funkcjonalnie pełnymi. Następujące zbiory są funkcjonalnie pełne: • AND, OR, NOT • AND, NOT
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

3.2 Bramki logiczne

77

X X+Y Y X Y X X XY

X XY Y X Y X X X+Y

Rysunek 3.4: Realizacja bramek AND, OR , NOT za pomocą bramek NAND lub NOR.

• OR, NOT • NAND • NOR Oczywiste jest, że bramki AND, OR, NOT tworzą zbiór funkcjonalnie pełny, ponieważ reprezentują one trzy operacje algebry Boole’a. Aby bramki AND i NOT mogły stanowić zbiór funkcjonalnie pełny, musi istnieć sposób zastąpienia operacji OR przy ich pomocy. Można to uczynić, korzystając z następującej własności (prawo de Morgana — patrz Twierdzenie 2.2): (X · Y ) = X + Y = X + Y Na podobnej zasadzie operacje OR i NOT stanowią zbiór funkcjonalnie pełny. Na rysunku 3.2 przedstawiono sposób realizacji bramek AND, OR, NOT wyłącznie za pomocą bramek NAND lub NOR. Dzięki temu układy cyfrowe mogą być i często są realizowane wyłącznie za pomocą bramek NAND lub NOR. Bardzo często spotykaną bramką jest XOR (eXclusive OR) — czyli wyłączne lub. Funkcja przez tę bramkę realizowana jest wykorzystywana między innymi do obliczeń przy kodach cyklicznych CRC (patrz 4.3.3). Symbol bramki XOR przedstawia rysunek (3.2), zaś jej działanie poniższa tabela: X 0 0 1 1 Y 0 1 0 1 X ⊕Y 0 1 1 0

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

78

Architektura i działanie komputera

3.3

Architektura współczesnego komputera

Mówiąc o architekturze komputera w tym punkcie będziemy mieli na myśli jego podstawowe komponenty, które są niezbędne do jego prawidłowego funkcjonowania, bądź trwale kojarzone z komputerami. Poprzez komputer będziemy rozumieli wszelkie urządzenia komputerowe, zatem nie tylko komputery osobiste, ale również sterowniki komputerowe czy też komputery pokładowe dla samolotów. Dlatego też będziemy pomijali wszelkie urządzenia wewnętrzne czy zewnętrzne, które powszechnie określa się mianem peryferiów, a które to nie są niezbędne. Przyjmując powyższe uproszczenie, można wyróżnić następujące trzy zasadnicze składowe dzisiejszego komputera: Procesor — główny układ wykonujący rozkazy, często też zawierający w sobie koprocesor numeryczny — realizujący operacje na liczbach zmiennoprzecinkowych. Pamięć — wyróżnia się zasadniczo dwa rodzaje pamięci: pamięcią o dostępie swobodnym — służy jako magazyn dla rozkazów (program), danych oraz wyników operacji na tych danych, oraz pamięć stałą w której „zaszyty” jest podstawowy program komputera (BIOS lub inne tego rodzaju). Wejście/Wyjście — (ang. Input/Output (I/O)), zapewniające dostęp do świata zewnętrznego. Należy zwrócić uwagę na fakt, że wymienione wyżej elementy niekoniecznie muszą występować jako osobne urządzenia. Dla przykładu mikroprocesory jednoukładowe zawierają większość z tych elementów w sobie tj. zamknięte w jednej obudowie układu scalonego. Przeznaczenie tego typu układów to wszelkiego rodzaju sterowniki np. pralki czy maszyny szwalniczej. W tych zastosowaniach twórcom często zależy na niewielkich gabarytach i zwartości układu. Komputer złożony z pamięci, procesora i układów wejścia/wyjścia będzie prawidłowo funkcjonował o ile, coś nada mu rytm pracy, tym elementem będzie zegar systemowy. Przy czym nie wyróżnia się zwykle tego elementu jako czegoś osobnego, zakłada się, że jest on niezbędny i pomija się przy omawianiu uproszczonej architektury. Schematyczna ilustracja architektury pokazana jest na rysunku 3.5. Wymienione elementy składowe muszą komunikować się ze sobą. Komunikacja ta odbywa się poprzez magistrale systemowe. Najprościej mówiąc magistrale te są rodzajem kanałów przez które płynie informacja, w jedną
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

3.3 Architektura współczesnego komputera

79

CPU

Me mo ry
główna magistrala

I/O

magistrala danych magistrala adresowa

Rysunek 3.5: Ilustracja architektury współczesnego komputera.

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

¡    ¨  § ¦ ¡ © ¡ ¨ § ¦ ¥ £ ¡ ¤¤¢¤¤¤¤¤¤¤¤¤¤¤¤¢ 

80

Architektura i działanie komputera

lub w drugą stronę. Kanały te składają się z kilku, kilkunastu, czy kilkudziesięciu przewodników elektrycznych 5 . Zasadniczo wyróżnia się trzy magistrale, które zostały omówione poniżej, przy niektórych z nich ważną cechą charakterystyczną jest tzw. szerokość magistrali, która mówi o ilości bitów informacji, która może zostać przesłana jednocześnie tą magistralą 6 . Magistrala danych — (ang. Data Bus) jest kanałem przez który płyną w obie strony dane, zatem zapewnia przesył do i z procesora, do i z pamięci itd7 . Magistrala danych ma pewną szerokość, zwykle 8, 16, 32, lub 64 bity. Magistrala adresowa — (ang. Address Bus) dostarcza informacji o adresach pod które mają trafić dane, lub spod których mają zostać odczytane8 . Szerokość magistrali adresowej jest bardzo ważna, mówi ona o tym jaką przestrzeń adresową możemy obsłużyć przy pomocy tego procesora. Przypomnijmy, że 210 = 1024, zatem na 10-cio bitowej magistrali adresowej możemy wyznaczyć adresy zaledwie 1024 komórek pamięci! Magistrala sterująca — (ang. System Bus) jest kanałem do przesyłania informacji o stanie systemu, zachowaniach urządzeń zewnętrznych itp. Szerokością tej magistrali interesują się wyłącznie producenci płyt głównych i układów towarzyszących, dla przeciętnego użytkownika nie ma to najmniejszego znaczenia. Spośród opisanych wyżej podstawowych elementów architektury komputera, procesor jest tym, który w największym stopniu rzutuje na możliwości jakimi dysponuje system komputerowy. Dlatego też poświęcimy mu szczególnie dużo miejsca, odkładając jego opis do następnego podrozdziału (zob. 3.4). Teraz skupimy się na omówieniu pozostałych dwu składowych. Powiedzieliśmy, że procesor jest tym najistotniejszym czynnikiem mającym wpływ na wydajność systemu, jednak pamięć ma również ogromny wpływ na sprawność całego układu. Obecnie dysponujemy bardzo szeroką gamą pamięci o dostępie swobodnym zwanych też pamięciami RAM (ang.
5 Mogą nimi być ścieżki drukowane — linijki miedzi na specjalnym podkładzie na którym montuje się układy scalone lub inne elementy, a mogą to być odpowiednie struktury w krzemie wewnątrz układu scalonego. 6 Jest to nierozerwalnie związane z ilością fizycznych połączeń — jedno połączenie może na raz przesłać jeden bit informacji 7 Oczywiście nie jest to proces jednoczesny, zatem np. najpierw się pobiera dane, po czym je wysyła. 8 Spotyka się również określenie wystawienie adresu określające proces adresowania.

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

3.3 Architektura współczesnego komputera

81

szybka

droga

rejestry

wolna

Rysunek 3.6: Ilustracja zależności kosztów pamięci, ich pojemności i czasu dostępu.

Random Access Memory), mając tym samym duże możliwości wyboru przy projektowaniu komputera zależnie od potrzeb i dostępnych środków finansowych. W celu lepszej ilustracji tego faktu, dokonamy porównania pamięci RAM z innymi nośnikami informacji, które nie muszą występować jako podstawowy składnik komputera9 . Najłatwiej pogrupować pamięci wszelkiego typu przypisując każdej z nich odpowiednie miejsce w piramidzie na rysunku 3.610 . W zależności od kierunku przeglądania tej piramidy możemy pamięci klasyfikować ze względu na: czas dostępu — jest to czas jaki musi upłynąć od momenty wystawienia żądania odczytu danej o zadanym adresie do momentu jej uzyskania, dla przykładu przy pamięci RAM będzie to 10 nanosekund, a dla twardego dysku nawet 10 milisekund, cenę — zwykle im większa gęstość zapisu tym wyższa cena, poprzez gęstość
Co więcej nawet jeśli występują to komunikacja pomiędzy procesorem a nimi odbywa się przez układy wejścia/wyjścia. Zatem nie należy wyciągać błędnych wniosków i w miejsce pamięci RAM wkładać dysku twardego. 10 Prezentowana piramida jest pewnym rodzajem uproszczenia, nie należy jej traktować zbyt dosłownie. Okazuje się np. że równie ważnym czynnikiem wpływającym na cenę poza pojemnością jest technologia wykonania.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004 9

% # ¥   ¡ $$¢¢" ¡    §    ¢¨¢  © § ¥ £ ¡ ¨¦¤¢ 

operacyjna

CD, DVD

HDD
tania

© !   £   ¨¢¢¤¢ 

82

Architektura i działanie komputera

zapisu będziemy rozumieli tutaj stosunek pojemności do gabarytów nośnika informacji11 . Jednak zależy to mocno od technologii i tak dla przykładu pamięć RAM jest dużo droższa od płyty CD-R a nawet CD-R/W. pojemność — na samej górze są pamięci najszybsze, ale jednocześnie małe gdyż bardzo kosztowne; na dole pamięci najwolniejsze, ale o bardzo dużej pojemności a przez to tanie (koszt rozpatrywany jest w przeliczeniu na pewną ustaloną jednostkę pojemności, na przykład bajt czy kilobajt). Stosując kryteria odwołujące się do fizycznej natury pamięci można je także pogrupować ze względu na: lokalizację — czyli położenie w komputerze, przykładowo: wewnątrz procesora (pamięć podręczna — cache), wewnątrz komputera (pamięć RAM, ROM), zewnętrzna (wszelkiego typu inne nośniki w tym dyskietki); wydajność — na ogólną wydajność wpływ będą miały następujące czynniki: czas dostępu, szybkość transmisji, długość cyklu dostępu; trwałość przechowywanej informacji — pamięć ulotna np. RAM jej zawartość ginie po odcięciu zasilania, nieulotna np. dysk twardy, wymazywalna np. płyta CD-RW, niewymazywalna np. płyta CD-R; charakter fizycznego zapisu — półprzewodnikowe, magnetyczne, optyczne; sposób dostępu — sekwencyjny np. pamięci taśmowe, bezpośredni, swobodny np. RAM. Na ogół w systemie stosuje się kilka różnych rodzajów pamięci co podyktowane jest poszukiwaniem kompromisu pomiędzy kosztem, wydajnością i możliwościami. Najwydajniejsze pamięci są bowiem jednocześnie najdroższe, dlatego ogranicza się ich rozmiar przechowując dane wykorzystując wolniejsze pamięci, ale o dużo większej pojemności i mniejszym koszcie. Patrząc na piramidę widzimy, że pamięci stanowiące podstawowy składnik architektury komputera, czyli pamięci o dostępie swobodnym są zwykle
Stąd też dysk twardy o tej samej pojemności do komputera biurowego, będzie miał niższą cenę niż do notebooka, gdzie jego wielkość musi być znacznie mniejsza.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004 11

3.4 Procesor — serce komputera

83

stosunkowo małe i bardzo drogie, zwłaszcza dotyczy to pamięci podręcznych. W chwili obecnej jednak nawet w zakresie pamięci o dostępie swobodnym mamy bardzo szeroki wybór technologii zwłaszcza, że nawet w ramach jednej z nich istnieje możliwość zakupu pamięci taktowanych różnymi częstotliwościami. Ostatnim elementem podstawowej architektury komputera są układy (urządzenia) wejścia/wyjścia. Nie są one konieczne do prawidłowego funkcjonowania systemu, ale umożliwiają interakcję pomiędzy nim a otoczeniem, które stanowi źródło danych podlegających przetworzeniu 12 . Ze względu na ich dużą różnorodność a także ścisłą zależność od specyfiki rozwiązywanych zadań nie będziemy ich tutaj bliżej przedstawiać. Nadmieńmy tylko, że w klasycznym komputerze osobistym można mówić na takich urządzeniach wejścia/wyjścia jak: port równoległy, port szeregowy (zwykle podłączana jest do niego mysz lub modem zewnętrzny).

3.4

Procesor — serce komputera

Architekturę procesora omówimy na przykładzie układu Intel 8086, przy czym zostanie ona uproszczona w sposób wystarczający do omówienia podstawowych cech. Schematyczna ilustracja wnętrza procesora znajduje się rysunku 3.7. Zasadniczo w procesorze 8086 wyróżnia się dwa główne elementy: układ wykonawczy (EU) (ang. Execution Unit), oraz układ sterowania magistrali (BUI) (Bus Interface Unit). Przy czym układ wykonawczy składa się z dalszych podjednostek, które spełniają następującą rolę: Rejestry — komórki pamięci wewnątrz procesora o specjalnym przeznaczeniu (szerszy ich opis można znaleźć w punkcie 3.4.2). ALU — jednostka arytmetyczno-logiczna, przeznaczona do wykonywania podstawowych operacji na liczbach całkowitych. FPU — jednostka zmienno-przecinkowa, zwana również koprocesorem, zajmująca się realizacją obliczeń na liczbach zmienno-przecinkowych. IU — jednostka dekodowania rozkazów, zajmująca się dekodowaniem rozkazów i żądaniem ewentualnych argumentów dla tych rozkazów. Z kolei układ sterowania magistrali składa się z następujących jednostek:
Choć niekonieczne, trudno sobie wyobrazić działanie bez nich, wszak komputer który nie komunikuje się ze światem zewnętrznym jest jakby trochę niekompletny.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004 12

84

Architektura i działanie komputera

EU ALU FPU

Reg Bus AU MMU BIU

IU

Rysunek 3.7: Architektura procesora Intel 8086.

AU — jednostka adresująca, która zajmuje się wyliczaniem adresu efektywnego. Adres efektywny to faktyczny adres pod którym znajduje się żądana dana. Nie jest ona zawsze zgodny z adresem przez który następuje odwołanie, a to ze względu na różne sposoby adresowania (patrz 3.4.4). MMU — jednostka zarządzająca pamięcią, która wspiera pobieranie danej bądź rozkazu z komórek pamięci.

3.4.1

Cykl pracy procesora

Zadaniem procesora, jak to było powiedziane już wcześniej, jest wykonywanie rozkazów zapisanych w pamięci. Samą budową rozkazu zajmiemy się w punkcie 3.4.3. W tym miejscu skoncentrujemy się na realizacji rozkazów i jej przebiegu. Realizacja rozkazu w procesorze odbywa się w cyklach. Cyklem nazwiemy pewną sekwencję stałych kroków, których celem jest realizacja bądź całego rozkazu, bądź jego fragmentu. W przypadku procesora 8086 w zależności od rodzaju rozkazu mogą wystąpić maksymalnie cztery główne cykle: pobranie — (ang. fetch) w tym cyklu rozkaz zostaje pobrany z pamięci do procesora, odczyt — (ang. read) jeśli rozkaz wymaga pobrania argumentu (argumentów) z pamięci, to zostaje wyznaczony ich adres efektywny (patrz 3.4.4), a następnie argument (argumenty) ten jest pobierany od procesora,
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

3.4 Procesor — serce komputera

85

wykonanie — (ang. execute) operacja, która jest opisana obrabianym rozkazem, jest wykonywana, zapis — (ang. write) jeśli w wyniku operacji powstał rezultat, który ma być umieszczony w pamięci, to zostaje dla niego wyznaczony adres efektywny i wykonywany jest zapis. W przypadku współczesnych procesorów stosuje się różnego rodzaju optymalizacje, które mają na celu przyśpieszenie pracy procesora, a w konsekwencji całego systemu. Nie inaczej jest w przypadku procesora 8086. Ponieważ układ wykonawczy i układ sterowania magistrali pracują niezależnie, to jeśli tylko dane zostały pobrane z BUI do EU, BUI pobiera kolejne bajty, które czekają w kolejce rozkazów. Operacja ta nosi nazwę pobrania wstępnego (ang. prefetch) i może przebiegać równocześnie z cyklem wykonywania. Dzięki temu uzyskuje się znaczny wzrost prędkości realizacji rozkazów13 . W przypadku procesora 8086, każdy rozkaz (omówienie szczegółowe rozkazów i ich przykładów znajduje się w 3.4.5), a dokładniej jego wykonanie składa się z co najmniej dwu etapów, zatem można przedstawić następujące schematy cyklów realizacji rozkazów: pobranie + wykonanie — rozkaz ADD AX, BX — dodaje wartości pochodzące z dwu rejestrów do siebie, argumentami są rejestry i nie trzeba wykonywać pobrania argumentów z pamięci, pobranie + odczyt + wykonanie — rozkaz ADD AX, ZMIENNA — dodaje do rejestru wartość zmiennej, w tym przypadku musi zostać pobrana wartość zmiennej z pamięci, pobranie + wykonanie + zapis — rozkaz MOV ZMIENNA, AX — zapisanie wartości rejestru do zmiennej, adres zmiennej w pamięci musi zostać wyznaczony i następuje zapis pod ten adres, pobranie + odczyt + wykonanie + zapis — rozkaz ADD ZMIENNA, AX — rozkaz dodaje zawartość rejestru AX do zmiennej (jej wartość musi być wpierw pobrana), wynik jest umieszczany w pamięci.
W najnowszych procesorach spotyka się jeszcze inne sposoby optymalizacji, przykładem może być przetwarzanie potokowe, które dopuszcza np. jednoczesne obrabianie pięciu rozkazów.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004 13

86

Architektura i działanie komputera

3.4.2

Rejestry procesora Intel 8086

W procesorze Intel 8086 wyróżniamy następujące rejestry • Rejestry ogólnego przeznaczenia (ang. General Purpose Registers) 14 : AX (AH/AL) rejestr akumulatora (ang. Accumulator), rejestr ten jest przeznaczony do wykonywania podstawowych operacji arytmetycznych i logicznych, w szczególności większość wyników trafia właśnie do tego rejestru. Przykładem może być rozkaz ADD AX, BX dodający zawartość rejestru AX i BX oraz zapamiętujący wynik w rejestrze AX. BX (BH/BL) rejestr bazowy (ang. Basis Register), rejestr ten często wykorzystywany jest w parze z AX przy operacjach arytmetycznych, ale również jako rejestr pomocniczy przy bardziej skomplikowanym adresowaniu pamięci. CX (CH/CL) rejestr licznika (ang. Count Register ), rejestr ten jest przede wszystkim wykorzystywany przy okazji pętli realizowanej przy pomocy rozkazu LOOP, gdzie wartość jego jest automatycznie zmieniana i w ten sposób jest on licznikiem tej pętli przez co również jednym z warunków jej zakończenia. DX (DH/DL) rejestr danych (ang. Data Register), rejestr ten na przykład wykorzystywany jest przy operacji mnożenia MUL AX, BX dwu rejestrów 16-to bitowych, w wyniku czego otrzymujemy liczbę 32 bitową i pierwsze dwa bajty będą pamiętane w rejestrze AX a ostatnie dwa właśnie w rejestrze DX. • Rejestry segmentowe (ang. Segment Registers): CS rejestr segmentu kodu (ang. Code Segment Register), rejestr ten przechowuje adres segmentu kody programu. DS rejestr segmentu danych (ang. Data Segment Register), rejestr ten przechowuje adres segmentu danych programu. SS rejestr segmentu stosu (ang. Stack Segment), rejestr ten przechowuje adres początku segmentu stosu o ile jest on wykorzystywany w programie.
14 Prawdę powiedziawszy rejestry te (co widać choćby po ich opisie), są mocno specjalizowane, zatem nazywanie ich rejestrami ogólnego przeznaczenia jest mocno na wyrost. Stosujemy jednak dosłowne tłumaczenie oryginalnej dokumentacji i jednocześnie nazewnictwo powszechnie przyjęte w literaturze polskiej.

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

3.4 Procesor — serce komputera

87

Rysunek 3.8: Rozmieszczenie flag w rejestrze flagowym (wyjaśnienie oznaczeń znajduje się w tekście).

ES rejestr dodatkowy (ang. Extra Segment Register). • Rejestry wskaźnikowe (ang. Pointer Registers): SI rejestr źródła (ang. Source Index), najczęściej wykorzystywany jest przy adresowaniu pośrednim. DI rejestr przeznaczenia (ang. Destination Index), wykorzystywany jest podobnie jak rejestr SI. IP rejestr rozkazów (ang. Instruction Pointer), wspólnie z rejestrem CS informuje o numerze aktualnie przetwarzanego rozkazu. • Rejestry stosu (ang. Stack Registers): SP rejestr wskaźnika stosu (ang. Stack Pointer). BP rejestr wskaźnika bazy (ang. Base Pointer). • Rejestry stanu (Status Registers): FLAGS rejestr flagowy (Status Flags), przechowuje informację o stanie procesora, dokładniejszy opis znajduje się w dalszej części punktu. Wszystkie wymienione rejestry są 16-to bitowe, przy czym w przypadku rejestrów ogólnego przeznaczenia jest możliwość bezpośredniego odwoływania się do górnej i dolnej ich połowy, zatem do starszego i młodszego bajtu poprzez odpowiednie nazwy: AH — odwołanie do starszego bajtu, zaś AL — odwołanie do młodszego bajtu rejestru AX, podobnie dla pozostałych rejestrów z tej grupy. Rejestr flagowy jest traktowany jako zbiór bitów, dla których przypisuje się odpowiednie znaczenie (patrz rysunek 3.8), poza kilkoma wyjątkami nie ma rozkazów odwołujących się do niego jako całości. Istnieje natomiast cała grupa rozkazów, które potrafią sprawdzać poszczególne jego bity. Poniżej znajduje się opis znaczenia poszczególnych bitów rejestru flagowego zaś ich rozkład pokazany jest na rysunku 3.8, przy czym bity ustawione
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

88

Architektura i działanie komputera

na wartość 0 lub 1 są zarezerwowane dla następnych generacji procesora. Najogólniej mówiąc rejestr flagowy, a dokładniej jego bity służą do uzyskania informacji o stanie procesora, lub wyniku ostatnio wykonywanej operacji i tak: CF — (ang. Carry Flag) flaga przeniesienia jest ustawiana przez operacje arytmetyczne, jeśli w wyniku ostatnio wykonywanej operacji nastąpiło przeniesienie na najstarszym bicie, np. dodanie dwu liczb 8-mio bitowych może dać w wyniku liczbę 9-cio bitową. PF — (ang. Parity Flag) flaga parzystości jest ustawiana przez operacje jeśli w wyniku ich wykonania liczba bitów o wartości 1 w mniej znaczącym bajcie wyniku jest nieparzysta. AF — (ang. Auxiliary Flag) flaga pomocnicza ustawiana głównie przy operacjach na kodzie BCD. Flaga ta jest ustawiana, gdy w wyniku operacji nastąpiło przeniesienie z bitu 3 na 4 lub pożyczka z bitu 4 na 3. ZF — (ang. Zero Flag) flaga zera ustawiana przez operacje arytmetyczne i logiczne, np. przy operacji porównywania dwu liczb. Jest ustawiana, gdy wynik działania jest równy zero, w przeciwnym wypadku jest zerowana. SF — (ang. Sign Flag) flaga znaku ustawiana przez operacje arytmetyczne, gdy najstarszy bit w otrzymanym wyniku jest równy 1, w przeciwnym wypadku jest zerowana. TF — (ang. Trap Flag) flaga pracy krokowej ustawiona na wartość 1 wprowadza procesor w tryb pracy krokowej (ang. Single Step Modus). W trybie tym po każdym wykonanym rozkazie wygenerowane zostaje przerwanie i przejście do specjalnej procedury obsługi. Tryb ten wykorzystywany jest do testowanie niskopoziomowych procedur, głównie przez konstruktorów systemów procesorowych. Wyzerowanie tej flagi powoduje powrót do normalnego trybu pracy. IF — (ang. Interrupt Flag) flaga zezwolenia na przerwanie ustawiona na 1 powoduje odblokowanie systemu obsługi przerwań. Wyzerowanie tej flagi powoduje, że procesor będzie ignorował zewnętrzne przerwania. DF — (ang. Direction Flag) flaga kierunku jest wykorzystywana przy działaniach na łańcuchach znaków. Jeśli jest ustawiona na 1 to rejestry
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

3.4 Procesor — serce komputera

89

Rysunek 3.9: Uproszczony schemat rozkazu procesora Intel 8086 (wyjaśnienie oznaczeń znajduje się w tekście).

DI i SI będą zwiększane przy tych operacjach, w przeciwnym wypadku zmniejszane. OF — (ang. Overflow Flag) flaga przepełnienia ustawiana jest przez rozkazy arytmetycznie, jeśli wystąpiło przepełnienie, przykładem może być rozkaz mnożenia.

3.4.3

Budowa rozkazu

Uproszczona postać rozkazu procesora Intel 8086 znajduje się na rysunku 3.9. Rozkaz składa się z 1-go do 6-ciu następujących po sobie bajtów, przy czym pierwsze dwa opisują w całości rozkaz jako taki, zaś ostatnie cztery są bądź danymi bezpośrednimi — tak będzie w przypadku rozkazu ADD AX, 05h (dodaje on do rejestru AX wartość 05 zapisaną szesnastkowo), bądź adresem gdzie szukać danych, lub gdzie umieszczać wyniki. Pierwsze dwa bajty nie są jednak interpretowane po prostu jako numer rozkazu, z rysunku wynika, że są one podzielone i każda z ich części ma określone znaczenie opisane poniżej: KOD — jest to kod rozkazu, który ma być wykonany.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

90

Architektura i działanie komputera

D — flaga informująca, czy dane mają być pobierane czy też przesyłane do rejestru. W — flaga informująca czy działania będą wykonywane na bajtach czy słowach. MOD — informuje gdzie leżą argumenty, w pamięci czy rejestrach. REG — określa jaki rejestr jest argumentem, lub jest rozszerzeniem rozkazu. R/M — określa rejestr będący argumentem lub rejestr wykorzystywany do obliczenia adresu względem początku segmentu. Z powyższego opisu można wywnioskować, że interpretacja poszczególnych kawałków rozkazu jest zależna od wielu czynników. Dla przykładu część REG może być informacją mówiącą lub inaczej doprecyzowującą o jaki rozkaz chodzi. Zauważmy, że gdyby tylko KOD mówił o tym jaki to rozkaz, to przy długości 5 bitów15 możemy mieć zaledwie 25 − 1 = 63 rozkazy, podczas gdy skądinąd wiemy, że w procesorze Intel 8086 mamy do dyspozycji ponad dwieście rozkazów. Zatem część REG składająca się z 3 bitów jest sposobem obejścia tego ograniczenia. Z pewnością przyglądając się postaci rozkazu w omawianym procesorze można zadać pytanie, skąd mamy wiedzieć jakie wartości poszczególnym częściom należy nadać by uzyskać zamierzony efekt. Do tego celu służy specyfikacja rozkazów dla danego procesora, która dokładnie omawia ułożenie zer i jedynek dla danego rozkazu. Przyznajemy, że pisanie programu w ten sposób jest bardzo męczące i w obecnej chwili nikt tego tak ne robi, a specyfikacja tych rozkazów jest potrzebna wyłącznie wąskiej grupie programistów piszących asemblery, czyli programy tłumaczące zapis mnemoniczny na kod maszynowy (patrz 3.4.5).

3.4.4

Adresowanie

W celu lepszego zrozumienia złożoności pracy procesora opiszemy również sposoby adresacji komórek pamięci. W zależności od tego gdzie dana jest umieszczona (pamiętana) dostęp do niej jest łatwiejszy, bądź trudniejszy. Co więcej przekłada się to wprost na czas potrzebny na pobranie tej danej, dalej ma to wpływ na czas realizacji konkretnego rozkazu. Dzieje się tak ponieważ dana, która ma być przetworzona zawsze musi zostać pobrana do
15

Bajt złożony jest z 8-miu bitów i odejmujemy po jednym bicie na flagi D, W, S.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

3.4 Procesor — serce komputera

91

procesora, a może przecież znajdować się w rejestrze (wtedy nic nie trzeba robić), w pamięci RAM (należy ją pobrać) czy nawet na porcie COM (należy ją odczytać). Co więcej, zanim taka dana będzie pobrana, należy stwierdzić czy wiemy wprost (natychmiast) gdzie ona się znajduje, czy też może znamy wyłącznie adres do niej. Wówczas najpierw należy wyznaczyć tzw. adres efektywny, by na końcu przy jego użyciu pobrać daną. Celem lepszego zrozumienia złożoności problematyki można posłużyć się takim prostym przykładem: wyobraźmy sobie, że musimy do kogoś zadzwonić. Po pierwsze możemy pamiętać do tej osoby numer telefonu, wtedy natychmiast wybieramy numer i trwa to chwilkę. Po drugie, możemy mieć numer zapisany w notesie, wtedy trwa to odrobinę dłużej. Po trzecie, możemy nie mieć numeru, ale pamiętamy numer do kolegi, który z pewnością ten nasz docelowy numer pamięta. Ponieważ kolegi może nie być w domu może to trwać dość długo. Po czwarte, możemy mieć numer do kolegi, który owszem ma ten numer docelowy ale zapisany, zatem nie tylko kolega musi być w domu, ale jeszcze musi odszukać notes z numerem (a jeśli kolega jest bałaganiarzem?). Po piąte, możemy mieć numer do kolegi, który zna kogoś kto zna nasz docelowy numer itd. Poniżej wypisane zostały różne sposoby adresowania, w kolejności od najprostszego do najtrudniejszego i w przybliżeniu od najszybszego do najwolniejszego16 . Przykłady zostały podane w zapisie mnemonicznym (patrz 3.4.5). 1. adresowanie natychmiastowe występuje gdy wartość podana jest bezpośrednio: MOV AX, 34h — załadowanie do rejestru AX wartość 34 szesnastkowo. (Rysunek 3.10) 2. adresowanie bezpośrednie z rejestru występuje gdy operujemy na dwu rejestrach: MOV AX, BX — załadowanie danej z rejestru BX do AX. (Rysunek 3.11) 3. adresowanie bezpośrednie z pamięci występuje gdy argument jest przekazywany przez adres: MOV AX, [0FFh] — załadowanie wartości spod adresu 0FF szesnastkowo do rejestru AX 17 . (Rysunek 3.12) 4. adresowanie pośrednie przez rejestr bazowy w tym przypadku adres efektywny jest w rejestrze BX lub BP: MOV AX, [BP] — załado16 W przybliżeniu, gdyż, czasami zależy to od konkretnego rozkazu. Jednak z pewnością operacje na rejestrach są realizowane najszybciej. 17 Zapis w nawiasie kwadratowym oznacza pobranie zawartości komórki adresowanej przez adres umieszczony w tym nawiasie.

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

92

Architektura i działanie komputera

wanie zawartości komórki, której adres jest w rejestrze BP, do rejestru AX. (Rysunek 3.13) 5. adresowanie pośrednie przez rejestr bazowy i offset 18 w tym przypadku adres jest w rejestrze BX lub BP ale dodatkowo się go przesuwa: MOV AX, [BP+5] — załadowanie zawartości komórki, której adres jest o pięć bajtów dalej niż adres umieszczony w rejestrze BP, do rejestru AX. (Rysunek 3.14) 6. adresowanie pośrednie przez rejestr indeksowy w tym przypadku adres znajduje się w rejestrze indeksowym DI lub SI: MOV AX, [SI] — załadowanie wartości komórki, której adres znajduje się w rejestrze SI, do rejestru AX. (Ilustracja analogiczna jak na rysunku 3.13 tylko z udziałem rejestru indeksowego) 7. adresowanie pośrednie przez rejestr indeksowy i offset w tym przypadku adres jest w rejestrze DI lub SI ale dodatkowo jest on przesuwany: MOV AX, [SI+6] — załadowanie zawartości komórki, której adres jest o 6 bajtów dalej niż adres zapisany w rejestrze SI, do rejestru AX. (Ilustracja analogiczna jak na rysunku 3.14 tylko z udziałem rejestru indeksowego) 8. adresowanie pośrednie przez rejestr bazowy i indeksowy adres komórki jest wyliczany na podstawie zawartości dwu rejestrów: MOV AX, [BX+SI] — załadowanie zawartości komórki, której adres jest sumą adresów rejestru BX i SI, do rejestru AX. (Rysunek 3.15) 9. adresowanie pośrednie przez rejestr bazowy, indeksowy i offset podobnie jak poprzednie adresowanie ale dochodzi jeszcze przesunięcie: MOV AX, [BX+SI+5] — załadowanie zawartości komórki, której adres jest sumą rejestru BX, SI i wartości 5, do rejestru AX. (Rysunek 3.16) 10. adresowanie układów wejścia/wyjścia — służy do odnoszenia się portów wejścia/wyjścia w celu wysłania lub pobrania z nich informacji. Do takich portów podłączona jest np. drukarka. Jak widać programista ma bardzo szerokie możliwości pobierania zawartości komórek pamięci, co więcej prawie wszystkie te sposoby można mieszać. Powoduje to z jednej strony dużą elastyczność, a z drugiej spore
18

offset to po prostu przesunięcie.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

3.4 Procesor — serce komputera

93

OC

ARG

Rysunek 3.10: Adresowanie natychmiastowe.

OC

REG1 REG2

ARG1(AX) ARG2(BX) ... IP BP SI ...
Rysunek 3.11: Adresowanie bezpośrednie z rejestru.

OC

MEM1 MEM2

ARG2 ARG1

Rysunek 3.12: Adresowanie bezpośrednie z pamięci.

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

94

Architektura i działanie komputera

OC

REG

AX BX ... IP BP SI ...

ARG

Rysunek 3.13: Adresowanie pośrednie przez rejestr bazowy.

OC

OFFSET

AX BX ... IP BP SI ...

OFFSET

+

ARG

Rysunek 3.14: Adresowanie pośrednie przez rejestr bazowy i offset.

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

3.4 Procesor — serce komputera

95

OC

AX BX ... IP BP SI ...

SI

+

ARG

Rysunek 3.15: Adresowanie przez rejestr bazowy i indeksowy.

OC

OFFSET

AX BX ... IP BP SI ...

+ +
ARG

Rysunek 3.16: Adresowanie przez rejestr bazowy, indeksowy i offset.

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

96

Architektura i działanie komputera

kłopoty dla początkujących adeptów programowania procesora na tak niskim poziomie.

3.4.5

Asembler

Jak można było zauważyć we wcześniejszym punkcie, pisanie programów za pomocą kodu maszynowego byłoby uciążliwe i monotonne. W konsekwencji łatwo prowadzące do błędów19 oraz właściwie uniemożliwiające ich zlokalizowanie. W celu ułatwienia procesu programowania procesora stworzono język asembler. Język ten jest tzw. językiem mnemonicznym, gdyż składa się z mnemoników: krótkich nazw odpowiadających konkretnym rozkazom maszynowym konkretnego typu procesora 20 . Co więcej w języku tym można stosować symboliczny zapis adresów, na przykład zamiast konkretnego adresu zmiennej można zastosować jej nazwę. Uwalnia to programistę od żmudnego przeliczania adresów, które po za tym, że jest nużące, może również powodować powstawanie błędów. Ponieważ w większości sytuacji rozkazy są tłumaczone jeden-do-jednego, zatem proces zamiany programu zapisanego w języku asembler na kod maszynowy sprowadza się niejako do podstawienia pod dany mnemonik jego reprezentacji binarnej. Fakt ten powoduje, że osoba programująca w asemblerze niejako pisze w kodzie maszynowym, co jest jedną z głównych przyczyn dla których pisane w ten sposób programy są najwydajniejsze pod względem prędkości działania jak i zajmowanej przez nie ilości pamięci 21 . Niestety jak się za chwilę przekonamy pisanie w tym języku jest żmudne i z pewnością nie efektywne pod względem prędkości tworzenia programów. Poniżej przedstawiamy przykładowe rozkazy procesora Intel 8086, dla ułatwienia zastały one pogrupowane według przeznaczenia. Przy większości rozkazów występuje informacja o tym na jakie flagi w rejestrze flagowym ten rozkaz może mieć wpływ. Dodatkowo podany jest czas realizacji danego rozkazu w jednostkach cyklu zegarowego tzw. taktu. Przy czym podajemy zakres czasu wykonania rozkazu, najkrócej będą realizowane rozkazy na
Chyba nikt nie wątpi, że pisząc sześć kolejnych bajtów metodą zero-jedynkową bardzo łatwo coś przeoczyć czy też zamienić miejscami. 20 Bardzo ważne jest zrozumienie, że język ten jest ściśle związany właśnie z typem procesorem, zatem dla innego procesora będzie inna lista rozkazów, być może podobna i podobne funkcje realizująca ale inna. I tak Intel będzie miał inną listę rozkazów niż Motorola, ale AMD będzie miało w pewnym podzbiorze zgodną z Intelem. 21 Oczywiście zakłada się w tym momencie, że osoba pisząca w tym języku doskonale się nim posługuje, a przeciwnym razie można napisać program długi, nieczytelny i niekoniecznie szybki czy mały.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004 19

3.4 Procesor — serce komputera

97

rejestrach najdłużej zaś np. na komórkach pamięci adresowanych pośrednio (patrz 3.4.4). Rozkazy przesunięć • MOV AX, BX — rozkaz przesłania zawartości rejestru BX do AX. Flagi: brak. Czas: 2-14. • PUSH SI — odłożenie na stos zawartości rejestru SI. Flagi: brak. Czas: 14. • POP DX — pobranie ze stosu wartości do rejestru DX. Flago: brak. Czas: 12. • XCHG AL, BL — wymiana wartości pomiędzy młodszymi bajtami rejestrów AX i BX. Flagi: brak. Czas: 4-20. • PUSHF — przesłanie zawartości rejestru znaczników na stos. Flagi, które mogą ulec zmianie: Brak. Czas: 10. • POPF — pobranie ze stosu wartości i zapisanie jej do rejestru znaczników. Flagi: wszystkie. Czas: 8. • IN AX, DX — pobranie wartości z portu o adresie danym w rejestrze DX do rejestru AX. Flagi: brak. Czas: 8-14. • OUT DX, AX — wysłanie zawartości rejestru rejestru AX do portu o adresie danym w rejestrze DX. Flagi: brak. Czas: 8-14. Rozkazy arytmetyczne i logiczne • ADD AX, BX — rozkaz dodania zawartości rejestru BX do AX, wynik zostanie zapamiętany w AX. Flagi: AF, CF, OF, PF, SF, ZF. Czas: 3-25. • SUB AX, BX — rozkaz odjęcia zawartości rejestru BX od AX, wynik zostanie zapamiętany w rejestrze AX. Flagi: AF, CF, OF, PF, SF, ZF. Czas: 3-25. • CMP AX, BX — rozkaz porównania zawartości rejestru BX i AX. Flagi: AF, CF, OF, PF, SF, ZF. Czas: 3-14. • INC AX — zwiększenie zawartości rejestru AX o 1. Flagi: AF, OF, PF, SF, ZF. Czas: 2-23. • DEC AX — zmniejszenie zawartości rejestru AX o 1. Flagi: AF, OF, PF, SF, ZF. Czas: 2-23.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

98

Architektura i działanie komputera

• MUL BL — pomnożenie rejestru AX (zawsze) przez młodszy bajt rejestru BX, wynik zostanie zapamiętany w AX i DX. W przypadku tego rozkazu liczby zapisane w rejestrach są traktowane jako liczby bez znaku. Flagi: CF OF. Czas: 70-143. • IMUL CL — pomnożenie rejestru AX (zawsze) przez młodszy bajt rejestru CX, wynik zostanie zapamiętany w AX i DX. W przypadku tego rozkazu liczby zapisane w rejestrach są traktowane jako liczby ze znakiem. Flagi: CF, OF. Czas: 80-164. • DIV CL — dzielenie zawartości rejestru AX (zawsze) przez młodszy bajt rejestru CX. W przypadku tego rozkazu liczby traktowane są jako liczby bez znaku. Flagi: brak. Czas: 80-320. • IDIV CL — dzielenie zawsze zawartości rejestru AX przez młodszy bajt rejestru CX. W przypadku tego rozkazu liczby traktowane są jako liczby ze znakiem. Flagi: brak. Czas: 101-194. • NOT AX — negacja logiczna bitów rejestru AX. Flagi: brak. Czas: 3-24. • XOR BX, CX — różnica symetryczna bitów w rejestrach BX i CX. Flagi: CF, OF, PF, SF, ZF. Czas: 3-25. • SHL AX, 1 — przesunięcie logiczne w lewo. Flagi: CF, OF, PF, SF, ZF. Czas: 2-30. • SAL AX, 1 — przesunięcie arytmetyczne w lewo. Flagi: CF, OF, PF, SF, ZF. Czas: 2-30. • ROL AX, 1 — przesunięcie cykliczne w lewo. Flagi: CF, OF. Czas: 2-30. • RCL AX, 1 — przesunięcie cykliczne z przeniesieniem w lewo. Flagi: CF, OF. Czas: 2-30. Rozkazy skoków • JZ etykieta — rozkaz wykonania skoku warunkowego do etykiety22 , skok zostanie wykonany jeśli flaga ZF=1. Flagi, które mogą ulec zmianie: brak. Czas: 16 — jeśli wystąpił skok, 4 — w przeciwnym razie.
Poprzez etykietę rozumiemy pewne miejsce w programie, które posiada pewną nadaną nazwę — zwaną etykietą.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004 22

3.4 Procesor — serce komputera

99

• JMP etykieta — rozkaz wykonania skoku bezwarunkowego do etykiety. Flagi: brak. Czas: 16. Rozkazy przerwań • INT — skok do podprogramu. Flagi: TF, IF. Czas: 52-72.

• IRET — powrót z podprogramu. Flagi: AF, CF, DF, IF, PF, SF, TF, ZF. Czas: 32-44.

Rozkazy sterujące stanem procesora • HLT — zatrzymanie procesora aż do wystąpienia przerwania. Flagi: brak. Czas: 2. • NOP — nic nie rób, pusty rozkaz. Flagi: brak. Czas: 3. Omówimy teraz klika elementarnych przykładów programów w asemblerze, celem lepszego zrozumienia sposobu programowania procesora. Spójrzmy na przykład 3.3, program ten (a dokładniej fragment programu) wczytuje do rejestrów AX i BX odpowiednio wartości zmiennych oraz wykonuje dodawanie. Wynik zostaje zapamiętany w zmiennej SUMA. Następnie to samo wykonywane jest dla różnicy. Przykład 3.3. Fragment programu w asemblerze

MOV MOV ADD MOV

AX, ZMIENNA1 BX, ZMIENNA2 AX, BX SUMA. AX

MOV AX, ZMIENNA1 SUB AX, BX MOV ROZNICA, AX

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

100

Architektura i działanie komputera

3.5

Reprezentacja informacji

Ponieważ obecne komputery są konstruowane w oparciu o cyfrowe układy scalone realizujące operacje dwuelementowej algebry Boole’a (zob. 2.1), zatem wszelka informacja zapamiętywana w komputerze i przez niego przetwarzana ma reprezentację zero–jedynkową. Zatem chcąc przedstawić w komputerze w jakiś sposób zwykłe znaki typu „A”, „3”, „+” itp. należy przyjąć pewną umowę co do ich oznaczania za pomocą ciągów bitów. Mówiąc o informacji należy pamiętać, że mówimy o wszelkich danych przetwarzanych za pomocą komputera. Dane wprowadzane za pomocą klawiatury, czyli znaki alfabetu, cyfry i znaki takie jak „!”, „?”, itd. nazywane są znakami alfanumerycznymi. Obliczenia w komputerze prowadzone są już na liczbach, które powstają na skutek konwersji pomiędzy zapisem „słownym” a jego reprezentacją numeryczną. By lepiej uzmysłowić problem, spróbujmy zapytać co znaczy napis „12”? Odpowiedzią będzie, że jest to liczba o wartości 12, ale skąd to wiemy? Przecież jeśli napiszemy „XII”, to również jest to liczba o wartości 12, choć zapis wygląda inaczej. Podobnie jak w przypadku nauki słów języka naturalnego, uczymy się odczytywać liczby i zmieniać napisy je reprezentujące na ich wartość numeryczną. Taki sam proces musi zachodzić w komputerze, aby dodać dwie liczby należy znać ich wartość, aby ją znać musi istnieć procedura zamiany napisu reprezentującego liczbę na jej wartość numeryczną. Takie procedury są częścią oprogramowania podstawowego w każdym komputerze. Niezależnie od tego jak taki proces przebiega, musi istnieć sposób reprezentacji znaków alfanumerycznych (3.5.1), liczb naturalnych (3.5.2), całkowitych (3.5.3) i rzeczywistych (3.5.5) w komputerze.

3.5.1

Znaki alfanumeryczne

Litery, cyfry i pozostałe znaki alfabetu łacińskiego są zapisywane w komputerze, podobnie jak wszelka informacja, za pomocą ciągów zer i jedynek, a zbiór tych znaków nosi miano znaków alfanumerycznych. Proces zamiany znaku wpisanego z klawiatury, lub innego urządzenia wczytującego, na jego reprezentację cyfrową nazwiemy kodowaniem. Jednym z najczęściej stosowanych standardów kodowania informacji w komputerze jest kod ASCII (ang. American Standard Code for Information Interchange — zob. dodatek D), w którym zdefiniowano kody 128 znaków stanowiących podstawowy zbiór znaków stosowanych w informatyce (zob. dodatek B). Tych 128 znaków odpowiada literom i cyfrom alfabetu łacińskiego oraz dodatkowo pewnym znakom sterującym. Dodatkowo zostało opisane w tym standarc 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

3.5 Reprezentacja informacji

101

ą B1 Ą A1

ć E6 Ć C6

ę EA Ę CA

ł B3 Ł A3

ń F1 Ń D1

ó F3 Ó D3

ś B6 Ś A6

ź BC Ź AC

ż BF Ż AF

Tablica 3.1: Kodowanie polskich znaków według standardu ISO 8859-2 (hexadecymalnie).

dzie następne 128 znaków, które w głównej mierze składają się ze znaków semigraficznych, służących do tworzenie tabel czy ramek, oraz dodatkowo ze znaków narodowych. Oczywiście zestaw tych znaków będzie zależał od kraju, czyli języka narodowego, ale również od producenta sprzętu komputerowego. Manipulując tekstem z wykorzystaniem kodu ASCII zawsze musimy wiedzieć przy użyciu jakiej strony kodowej został on napisany. Strony kodowe to wersje kodu ASCII różniące się interpretacją symboli o numerach od 128 do 255. Na przykład dla języka polskiego stworzono stronę kodową ISO 8859-2 nazywaną popularnie ISO Latin-2, gdzie poszczególnym literom charakterystycznym dla naszego języka przypisano następujące wartości liczbowe (tabela 3.5.1). Niestety oprócz powyższego standardu powstało także wiele innych; łączne zestawienie częściej spotykanych zawiera dodatek C. Problem ten stanie się jeszcze bardziej wyraźny jeśli operujemy na tekstach wielojęzycznych. Ponadto udostępniana przestrzeń 128 znaków, które możemy dowolnie zdefiniować dla wielu celów jest niewystarczająca (język chiński, symbole matematyczne). Problem ten często był rozwiązywany poprzez wprowadzanie specjalnych znaków sterujących zmieniających sposób interpretacji kodów po nich następujących; na przykład

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

102

Architektura i działanie komputera

W celu ujednolicenia sposobu interpretacji kodów liczbowych jako znaków, oraz zapewnienia możliwości reprezentowania za ich pomocą zarówno tekstów pochodzących z przeróżnych zakątków świata jak i pewnych dodatkowych symboli zaproponowano nowy sposób kodowania znaków znany pod nazwą Unicode. Oto zasadnicze cele dla których Unicode został stworzony oraz jego podstawowe właściwości: Jednoznaczność Każdemu znakowi zakodowanemu za pomocą Unicode odpowiada zawsze ta sama wartość liczbowa i odwrotnie. Uniwersalność „Repertuar” dostępnych znaków obejmuje wszystkie powszechnie używane języki oraz symbole. 16-bitowa reprezentacja Każdy znak reprezentowany jest w postaci 16bitowej liczby. Pozwala to na reprezentację ponad 65 tysięcy znaków (w 1996, gdy obowiązywał Unicode Standard 2.0 pozostawało jeszcze ponad 18 tysięcy wolnych miejsc). Ponadto można zdefiniować około miliona dodatkowych znaków poprzez zastępczy mechanizm rozszerzeń (ang. surrogate extension mechanism), który bez żadnych problemów może być równolegle wykorzystywany z podstawową wersją. Efektywność Stosowanie Unicode ułatwia znacznie manipulowanie tekstami, gdyż identyfikacja znaku nie zależy od sekwencji sterujących czy znaków następujących bądź poprzedzających. Identyfikacja nie reprezentacja Standard ten określa jakie kody odpowiadają jakim znakom. Nie definiuje natomiast jak znaki te mają wyglądać, proszę spojrzeć na poniższy przykład

kod=litera_a a a a a a
Znaczenie W celu umożliwienia wykorzystania algorytmów operujących na tekstach (na przykład sortowanie) zdefiniowano tablicę własności znaków. Własności tych nie określa na ogół ani nazwa znaku ani jego położenie w tabeli kodów. Czysty tekst Unicode odpowiada za kodowanie czystego tekstu (ang. plain text) bez żadnych dodatkowych informacji typu język w jakim tekst napisano, rodzaj czcionki czy jej wielkość.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

Identyfikacja

Wygląd

3.5 Reprezentacja informacji

103

Logiczny porządek Kodowany tekst przechowywany jest w porządku logicznym, to znaczy w porządku w jakim są pisane znaki a nie w ich faktycznej kolejności w jakiej występują w dokumencie. Rozróżnienie to istotne jest w przypadku języków, w których tekst piszemy od prawej do lewej, co ilustruje poniższy przykład

Ujednolicenie Wszystkie powtarzające się znaki zastąpione zostały jednym znakiem. Nie wprowadza się sztucznych podziałów — w pewnych językach ten sam znak ma inną nazwę lub funkcję (na przykład "," jest separatorem tysięcy w liczbie dla języka angielskiego zaś dziesiątek dla francuskiego). Wciąż jest to jednak ten sam znak, zatem zwykle wszystkie one zostają zastąpione jednym. „Zwykle”, gdyż niektóre znaki pomimo to pozostawiono, chcąc zapewnić kompatybilność z pewnymi już istniejącymi standardami.

3.5.2

Liczby naturalne

Liczba naturalna zapisana za pomocą zer i jedynek, będzie ciągiem bitów powstałym w wyniku zamiany zapisu dziesiętnego na dwójkowy (zob 2.2.1). Wówczas, dysponując n bitami, możemy przedstawić wszystkie liczby z przedziału [0, 2n − 1]. Jeśli założymy, że n = 8 wówczas możemy przedstawić 256 różnych liczb naturalnych z przedziału [0, 255], na przykład: 000000002 000000012 000110002 100000002 111111112 = = = = = 010 110 2410 12810 25510

3.5.3

Liczby całkowite

W przypadku liczb całkowitych nasuwa się podobny wniosek jak dla liczb naturalnych, by reprezentować je po prostu za pomocą zapisu dwójkowego
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

104

Architektura i działanie komputera

tej liczby. Co jednak począć ze znakiem? Musimy jakoś odróżnić liczby całkowite ujemne od całkowitych dodatnich. Rozważmy jeden z możliwych sposobów: przeznaczony zostanie jedne bitu (np. najstarszego) na znak. Ten sposób zapisu liczby ujemnej zwany jest reprezentacją znak–moduł. Przyjmijmy teraz, że 0 na najstarszym bicie oznacza, iż pozostałe bity reprezentują liczbę dodatnią, zaś ujemną, gdy bit ten jest równy 1. Stosując taką konwencję, na n bitach będziemy mogli zapisać 2n liczb z przedziału [−2n−1 + 1, 2n−1 − 1]. Jeśli założymy na przykład, że n = 8, wówczas możemy zapisać 256 różnych liczb, z przedziału [−127, 127], przy czym zero może być reprezentowane na dwa sposoby: +010 = 000000002 −010 = 100000002 Rozważmy następujące działania, by upewnić się, że zapis ten spełnia nasze oczekiwania. W tym celu weźmy następujące liczby i ich reprezentację zero–jedynkową +5710 = 001110012 −1310 = 100011012

przy czym, zgodnie z wcześniejszą umową, pierwszy bit od lewej jest bitem znaku. Działanie 57−13 możemy rozważać jako (+57)−(+13), albo jako (+57)+ (−13). Rozważmy przykład: 00111001 + 10001101 ---------11000110 ( = ( = ( = + 57 ) - 13 ) - 69 )

Otrzymaliśmy −69!!!, co z pewnością nie jest poprawnym wynikiem. Jak widać z powyższego przykładu, taka reprezentacja nie prowadzi do prawidłowych wyników przy „klasycznej” technice dodawania. A zatem w przypadku dodawania liczb o znakach przeciwnych należy to uwzględniać i zamiast dodawać, należy odjąć bity reprezentujące wartość a dopiero na koniec przeprowadzić analizę znaku wyniku. Rozważmy zatem przykład 0 0111001 + 1 0001101 ----------0 0101100 ( = ( = ( = + 57 ) - 13 ) + 44 )

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

3.5 Reprezentacja informacji

105

Tym razem otrzymaliśmy poprawny wynik, ale niestety kosztem dodatkowych operacji związanych z ustalaniem znaku. W szczególności, gdy w wyniku odejmowania otrzymamy, jak w powyższej sytuacji, wynik dodatni, to znak należy ustawić na dodatni. Te dodatkowe operacje nie są naturalne z punktu widzenia algebry Boole’a, a co więcej zajmują niepotrzebnie czas procesora. Jak więc widać z powyższych rozważań, operowanie na liczbach ze znakiem wcale nie jest takie oczywiste jak byśmy sobie tego życzyli. Wymaga, oprócz samego wykonania działania, szeregu czynności, które mają na celu ustalenia poprawnego znaku wyniku. Aby ominąć te niedogodności wprowadzono reprezentację uzupełnień do dwu.

3.5.4

Reprezentacja uzupełnień do dwu

Reprezentację uzupełnień do dwu opracowano w celu wyeliminowania problemów jakie występowały w przypadku stosowania reprezentacji znak-moduł. Podobnie jak w poprzedniej reprezentacji, najbardziej znaczący bit informuje o znaku liczby. Jednak inaczej interpretuje się pozostałe bity. Zanim jednak zdefiniujemy precyzyjnie nowy sposób zapisu przyjrzyjmy się poniższemu przykładowi 00001110 ( + 14 ) + 1xxxxxxx ( - 14 ) ---------00000000 ( 0 ) Mamy w powyższym przykładzie pewne działanie w którym chcemy dokonać sumowania liczby 14 zapisanej w postaci binarnej i liczby −14. W wyniku tego dodawanie chcemy otrzymać 0, przy czym wszystkie te liczby mają być zapisane zero–jedynkowo. Inaczej mówiąc chciałbym dodać 14 + (−14) tak by otrzymać 0, wykonując naturalne dodawanie w systemie dwójkowym, bez zastanawiania się czy coś jest znakiem czy nie. W powyższym przykładzie łatwo wywnioskować jaką postać powinien mieć drugi składnik sumy, zapiszmy ponownie to działanie 00001110 + 11110010 ---------00000000 Widzimy teraz, że po dodadniu otrzymamy faktycznie 0. Z wyjątkiem faktu, że będziemy mieli przeniesioną 1 na 9−ty bit, ale nie stanowi to problemu,
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

106

Architektura i działanie komputera

gdyż prowadzimy obliczenia (w tym przypadku) na 8−miu bitach. Drugi składnik sumy jest uzupełnieniem dwójkowym liczby 14 zapisanej na 8−miu bitach. Po tym wprowadzeniu możemy wprowadzić pojęcie uzupełnienia dwójkowego. Uzupełnieniem dwójkowym liczby x zapisanej za pomocą n bitów nazywamy liczbę xu2 = 2n − x. Rozważmy następujące przykłady • Niech x = 0101. Uzupełnieniem dwójkowym jest x u2 = 24 − 0101 = 10000 − 0101 = 1011. • Niech y = 1011. Uzupełnieniem dwójkowym jest y u2 = 24 − 1011 = 0101. Otrzymaliśmy zatem, że yu2 = x. Ponieważ jednak y = xu2 , więc ostatecznie możemy zapisać, że (xu2 )u2 = x. Obserwacja ta, potwierdzona analitycznie, nasuwa wniosek, że uzupełnienie dwójkowe danej liczby reprezentuje liczbę do niej przeciwną. Wprawdzie ten sposób zapisu jest dla człowieka dość kłopotliwy, stosuje się go jednak powszechnie, gdyż ułatwia on implementację podstawowych operacji arytmetycznych. Kłopoty pojawiają się wyłącznie przy dokonywaniu konwersji z systemu dziesiętnego na uzupełnień do dwu i odwrotnie. Jednak konwersję tę wykonuje się wyłącznie przy wprowadzaniu danych lub ich wyprowadzaniu, pomiędzy tymi operacjami dane pozostają w kodzie uzupełnień do dwu, co nikomu nie przeszkadza. Ponieważ operacji konwersji jest dużo mniej niż działań na nich, więc opłaca się je wykonywać zamiast stosować reprezentację znak-moduł i wykonywać dodatkowe operacje przy każdym działaniu. Konwersja pomiędzy systemem dziesiętnym a systemem uzupełnień do dwu Do konwersji liczb pomiędzy zapisem uzupełnień do dwu a reprezentacją dziesiętną można stosować tablicę wartości (tab. 3.2). Ale ten sposób jest żmudny i niewygodny dla komputera. Stąd w praktyce wykorzystuje się jeden z dwu poniższych algorytmów. Pierwszy z nich sprowadza się do operacji dopełnienia i sumy, drugi korzysta wprost z definicji dopełnienia i wymaga odejmowania. Algorytm 1 1. Weźmy dowolną liczbę ujemną x daną w zapisie dziesiętnym.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

3.5 Reprezentacja informacji

107

Tablica 3.2: Tabela zawierająca porównanie reprezentacji znak-moduł i uzupełnień do dwu. Reprezentacja Reprezentacja Reprezentacja dziesiętna znak-moduł uzupełnienia do dwu +7 0111 0111 +6 0110 0110 0101 0101 +5 +4 0100 0100 0011 0011 +3 +2 0010 0010 +1 0001 0001 0000 0000 +0 -0 1000 — 1001 1111 -1 -2 1010 1110 -3 1011 1101 1100 1100 -4 -5 1101 1011 1110 1010 -6 -7 1111 1001 — 1000 -8 2. Wykonujemy konwersję modułu liczby x na zapis dwójkowy, przy czym musi być w tym momencie ustalone na ilu bitach będziemy reprezentowali tę liczbę. 3. Bierzemy uzupełnienie Boole’a każdego bitu liczby (łącznie z bitem znaku). 4. Traktując wynik poprzedniego kroku, jako liczbę całkowitą pozbawioną znaku, dodajemy 1. 5. Wynik jest zapisem w kodzie uzupełnień do dwu liczby x. Algorytm 2 1. Weźmy dowolną liczbę ujemną x daną w zapisie dziesiętnym. 2. Wykonujemy konwersję modułu liczby x na zapis dwójkowy, przy czym musi być w tym momencie ustalone na ilu bitach będziemy reprezentowali tę liczbę.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

108

Architektura i działanie komputera

3. Uzyskana reprezentacja liczby x jest pewnej długości, oznaczmy ilość jej bitów przez k. 4. Zapiszmy w systemie dwójkowym liczbę 2 k . 5. Odejmijmy od tak zapisanej liczby liczbę x w systemie dwójkowym. 6. Wynik jest zapisem w kodzie uzupełnień do dwu liczby x. Przykład 3.4. Ilustracja algorytmu 1 zamiany liczby dziesiętnej na kod uzupełnień do dwu.

1) 2) 3) 4)

Weźmy liczbę -14 Moduł liczby w kodzie dwójkowym na 8 bitach => 00001110 Uzupełnienie Boole’a 00001110 => 11110001 Wykonujemy dodawanie 11110001 + 00000001 ---------11110010 <= Liczba -14 w kodzie uzupełnień do dwu

Przykład 3.5. Ilustracja algorytmu 2 zamiany liczby dziesiętnej na kod uzupełnień do dwu.

1) Weźmy liczbę -14 2) Moduł liczby w kodzie dwójkowy na 8 bitach => 00001110 3,4) 8-ta potęga 2 => 100000000 5) Wykonujemy odejmowanie 100000000 - 00001110 ---------11110010 <= Liczba -14 w kodzie uzupełnień do dwu

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

3.5 Reprezentacja informacji

109

3.5.5

Liczby rzeczywiste

Stosując przedstawianą w poprzednich punktach notacje możliwe jest reprezentowanie dodatnich i ujemnych liczb całkowitych z określonego przedziału. Ponadto, jeśli umówimy się, że pewna grupa bitów oznacza część całkowitą pozostałe zaś część ułamkową, możliwe stanie się reprezentowanie liczb ze składnikiem ułamkowym. Nie jest to jednak rozwiązanie optymalne, gdyż z góry narzuca nam ilość bitów przeznaczonych na zapis części całkowitej i ułamkowej. W takiej sytuacji z jednaj strony dochodziłoby do „marnowania miejsca” przeznaczonego na zapis liczby (tak będzie gdy przykładowo liczba będzie całkowita, wtedy część ułamkowa w ogóle nie będzie wykorzystywana). Z drugiej zaś strony, i jest to argument dużo ważniejszy, takie rozwiązanie narzuca arbitralnie precyzję zapisu takiej liczby. Rozwiązanie takie, zwane zapisem stałoprzecinkowym, jest czasami wykorzystywane gdy obliczenia prowadzone są zawsze z pewną dokładnością, czego przykładem mogą być kalkulacje walutowe. Rozważmy następujący przykład. Załóżmy, że dysponujemy 8 bitami na zapis liczby. Daje nam to 256 różnych wartości. Jeśli ustalimy, że pierwsze 4 bity od lewej to część całkowita, a pozostałe to ułamkowa, wówczas możliwa stanie się reprezentacja liczb z przedziału [0.0, 15.9375] przy czym najmniejsza odległość pomiędzy kolejnymi liczbami (tzw. granulacja), to 1/16 czyli 0.0625. Chcąc teraz zwiększyć zakres reprezentowanych liczb, możemy na część całkowitą przeznaczyć 6 bitów. Możliwa stanie się wówczas reprezentacja liczb z przedziału [0.0, 63.75] z dokładnością 1/4 czyli 0.25. Zwykle potrzeba by zarówno zakres reprezentowanych liczb jak i ich dokładność były odpowiednio duże, co wiązałoby się z przeznaczeniem odpowiedniej ilości bitów na obie części. Co jednak w przypadku, gdyby operacje były dokonywane tylko na liczbach bardzo małych (albo bardzo dużych)? Wówczas część odpowiedzialna za reprezentacje części całkowitej (lub ułamkowej) nie byłaby w ogóle wykorzystywana. Sposobem obejścia tych ograniczeń jest zastosowanie modyfikacji powszechnie przyjętej notacji naukowej do zapisu liczb binarnych. W dziesiętnej notacji naukowej liczbę bardzo dużą, np. 456000000000000 można zapisać jako 4.56 × 1014 , a bardzo małą 0.0000000000000456 jako 4.56 × 10 −14 . W ten sposób, stosując dynamiczne przesunięcie przecinka oraz używając potęgi liczby 10 do określenia jego rzeczywistego położenia, możemy reprezentować dowolne liczby za pomocą tylko kilku cyfr. Takie samo podejście można zastosować do liczb binarnych. Zauważmy, że można zapisać ogólny wzór dla reprezentacji naukowej w dowolnym poc 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

110

Architektura i działanie komputera

zycyjnym systemie liczbowym w postaci: z m M × B zc C , gdzie: M — mantysa, C — wykładnik (cecha), B — używane podstawa, z m — znak mantysy, zc — znak cechy. Zatem przechodząc do systemu dwójkowego, mamy: z m M × 2 zc C , przy czym zarówno mantysa jak i cecha zakodowane są binarnie. Przyjmując taki zapis i mając każdym ciągu bitów o określonej długości, który reprezentuje liczbę w oparciu o powyższą notację, należy wyróżnić cztery grupy bitów. Grupa pierwsza reprezentuje znak takiej liczby (wystarczy jeden bit), druga mantysę, trzecia znak cechy (znów jeden bit), i ostatnia cechę23 . Mantysa będzie zawierać informację o cyfrach znaczących liczby, zaś cecha będzie informowała na ile duża bądź też na ile mała jest ta liczba. Zauważmy również, że podstawa B nie musi być przechowywana, gdyż jest z góry ustalona (w rozważanym przypadku wynosi 2). Można też zauważyć, że nie trzeba pamiętać znaku cechy jeśli przyjmie się następującą notację zm M × 2C−KC , gdzie KC jest stałą zależną od konkretnej implementacji. W tej sytuacji ujemny wykładnik wystąpi gdy wartość cechy będzie mniejsza od zadanej stałej. Innym podejściem jest zapamiętanie cechy w postaci liczby w systemie uzupełnień do dwu, wtedy w sposób naturalny omijamy problem znaku. Niemniej jednak pozostaje jeszcze pewien problem jak optymalnie zapamiętać mantysę. Zauważmy, iż liczbę rzeczywistą można przedstawić na wiele równoważnych sposobów stosując omawiany tok rozumowania, a mianowicie: 4.56 × 10−14 = 456 × 10−16 = 0.456 × 10−13

W celu uniknięcia pewnych problemów numerycznych oraz celem ułatwienia obliczeń wymaga się aby liczby te były w jakiś sposób znormalizowane. To znaczy przyjmuje się, że wartość mantysy ma spełniać pewną nierówność, która dla liczb w systemie dziesiętnym miała by postać: 1.0 ≤ M < 10.0

Zauważmy, że to jest tylko pewna propozycja, tak naprawdę nie ma znaczenia kolejność występowania poszczególnych grup.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

23

3.5 Reprezentacja informacji

111

zaś dla liczb w systemie dwójkowym, następującą nierówność (zapisaną dziesiętnie): 1.0 ≤ M < 2.0 Można spytać czemu właśnie taką nie równość a nie inną? Odpowiedź na to pytanie jest prosta, jeśli przeliczymy lewą nierówność na zapis dwójkowy, wtedy otrzymamy: 1.0 ≤ M Z powyższego wynika, że lewy bit mantysy jest zawsze równy 1, zatem nie ma potrzeby go przechowywać — wystarczy pamiętać tylko te bity mantysy, które występują po kropce, czyli 1.bn . . . b0 gdzie bi , (i = 0, . . . , n) są cyframi systemu binarnego. W celu lepszego zrozumienia dotychczasowych rozważań przyjrzyjmy się następującemu przykładowi. Załóżmy, że dysponujemy 8 bitami. Niech pierwszy bit od lewej oznacza znak, kolejne 3 mantysę zaś ostatnie 4 bity będą cechą. Jako wartość stałej KC przyjmijmy 7. Najmniejsza liczba dodatnia jaką możemy w ten sposób reprezentować to +1.0 × 2 −7 zapisane jako 00000000 czyli 0.0078125, największa zaś to +1.875 × 2 8 zapisana jako 01111111 czyli 480. Chcąc uzyskać podobne rezultaty stosując wcześniejszy sposób zapisu liczb (stałopozycyjny), otrzymano by, że część ułamkowa musiałaby mieć 7 bitów (0.00000012 = 2−7 = 0.007812510 ), zaś część całkowita bitów 9 (wprawdzie na 9 bitach można zapisać liczbę o wartości większej od 480, gdyż 111111111.02 = 51110 , jednak 8 bitów to za mało, bowiem 11111111.02 = 25510 ). Dodając do tego wszystkiego bit znaku otrzymalibyśmy łącznie 17 bitów. Przy tak przyjętej reprezentacji możliwe jest przedstawienie liczb z następujących zakresów: – liczby ujemne między −480.0 a −0.0078125 11111111 = −1.111 × 28 = −1.875 × 256 = −480 10000000 = −1.000 × 2−7 = −1.0 × 0.0078125 = −0.0078125 – liczby dodatnie między 0.0078125 a 480 Zakresy te nie obejmują pięciu obszarów:
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

112

Architektura i działanie komputera

– liczby ujemne mniejsze niż −480, określane jako przepełnienie ujemne; – liczby ujemne większe niż −0.0078125, określane jako niedomiar ujemny; – zero; – liczby dodatnie mniejsze niż 0.0078125, określane jako niedomiar dodatni; – liczby dodatnie większe niż 480, określane jako przepełnienie dodatnie; Należy zatem zwrócić uwagę na następujące fakty. W tak przyjętym formacie nie da się przedstawić zera (gdy stosowana była notacja uzupełnieniowa do dwóch, możliwe było reprezentowanie wszystkich liczb całkowitych od −2n do 2n − 1, gdzie n było liczbą bitów przeznaczonych na zapis liczby). W praktyce stosuje się specjalny wzór bitowy do oznaczenia zera. Należy jasno zdawać sobie sprawę, że za pomocą notacji zmiennopozycyjnej nie reprezentuje się większej liczby pojedynczych wartości. Maksymalna liczba różnych wartości, które mogą być przedstawione za pomocą 8 bitów nadal wynosi 28 . Ponadto stosowanie takiej reprezentacji powoduje, że liczby nie są równomiernie rozmieszczone na osi liczbowej, jak miało to miejsce w przypadku liczb stałopozycyjnych. Możliwe wartości są rozłożone gęściej na początku osi, a rzadziej w miarę oddalania się od początku. W przypadku tak ustalonego formatu następuje wymienność między zakresem a dokładnością. Jeśli zwiększymy liczbę bitów wykładnika, to poszerzymy w ten sposób zakres liczb możliwych do wyrażenia. Ponieważ jednak tylko ustalona liczba różnych wartości może być wyrażona, zredukujemy w ten sposób gęstość tych liczb i tym samym dokładność. We współczesnych komputerach przyjęto właśnie taki sposób przedstawiania liczb rzeczywistych. Dokładne informacje na temat ilości bitów i ich interpretacji, oraz wszelkie dodatkowe ustalenia zebrane zostały w Normie IEEE 75424 . Epsilon maszynowy Precyzją p zmiennopozycyjnego systemu liczbowego nazywać będziemy liczbę bitów użytych do zapisu mantysy (wliczając w to pierwszy bit o stałej wartości). W naszych dotychczasowych rozważaniach p = 4 (3 bity na zapis
24

Dokładnie nazwa brzmi: IEEE Standard 754 for Binary Floating-Point Arithmetics
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

3.6 Zadania

113

mantysy + 1 bit domyślny o stałej wartości 1). Stąd każda liczba rzeczywista x zapisana z precyzją p może być wyrażona jako x = zm (1.bp−2 . . . b1 b0 )2 = 1 + 2C−KC Najmniejszą taką liczbę x, większą od 1.0 jest x = (1. 0 . . . 0 1)2 = 1 + 2−(p−1)
p−2

(3.1)

Różnica pomiędzy tą liczbą a 1.0 nazywana jest epsilonem maszynowym 25 . Podajemy tutaj definicję za [Ove01], zgodnie z tym co napisano, wynosi on ε = (0. 0 . . . 0 1)2 = 1 + 2−(p−1)
p−2

Dla dowolnej liczby x przedstawionej w postaci (3.1) definiuje się ulp(x) = 0. 0 . . . 0 12 × 2C−KC = 2−(p−1) × 2C−KC = ε × 2C−KC
p−2

Jeśli x > 0 (x < 0) wówczas ulp (ang. Unit In The Last Place) informuje o odległości od następnej, tj. większej (poprzedniej, tj. mniejszej) (reprezentowanej w przyjętym systemie) liczby.

3.6

Zadania

1. Stosując dwójkową reprezentację znak-moduł zapisz następujące liczby całkowite (zakładamy, że liczba dwójkowa powinna zajmować wielokrotność 8 bitów). a) f) -20 -128 b) g) +20 +372 c) h) +47 -420 d) i) -18 -42 e) j) +37 -15

2. Stosując dwójkową reprezentację uzupełnień do dwu zapisz następujące liczby całkowite (zakładamy, że liczba dwójkowa powinna zajmować wielokrotność 8 bitów). a) f)
25

-20 -99

b) g)

+87 -300

c) h)

+65 +77

d) i)

-18 -77

e) j)

+27 +503

Czasem epsilon maszynowy definiowany jest jako połowa tej różnicy

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

114

Architektura i działanie komputera

3. Przedstaw poniższe dziesiętne liczby rzeczywiste jako rzeczywiste liczby dwójkowe. a) e) i) 0.5625 2.8125 15.375 b) f) j) 7.4375 13.875 5.0625 c) g) k) 11.0625 9.625 3.3125 d) h) l) 4.6875 14.75 8.1875

4. Przedstaw poniższe dwójkowe liczby rzeczywiste jako rzeczywiste liczby dziesiętne. a) e) i) 1111.01 0.1111 101.1101 b) f) j) 1011.101 1100.0001 10.1001 c) g) k) 1000.111 100.0111 1101.011 d) h) l) 1.1011 1001.001 1010.1

5. Załóżmy, że operujemy następującym zmiennopozycyjnym formatem zapisu liczby rzeczywistej: na zapis przeznaczamy 8 bitów, najstarszy bit, to bit znaku, kolejne 3 to mantysa, ostatnie 4 to cecha. Wartość stałej KC przyjmujemy równą 7. Jakie liczby rzeczywiste reprezentują poniższe ciągi: a) e) i) 01000000 00000100 00101011 b) f) j) 10100101 10100010 10011111 c) g) k) 00010010 01111101 01010110 d) h) l) 10101001 10001110 10000011

6. Dane są tabelki przejść oraz początkowy stan taśmy dla następujących trzech Maszyn Turinga, napisz tabelę ruchów dla każdej z nich. (Prostokąt oznacza początkową pozycję głowicy) a) Tabela przejść Stan bieżący s s s s Odczytany symbol a b Symbol do zapisu b a a Kierunek ruchu głowicy → → → − a a b b Stan kolejny s s s k

Początkowy stan taśmy b b) Tabela przejść a b a b

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

3.6 Zadania

115

Stan bieżący s s s s 1 1 1 1

Odczytany symbol a b

Symbol do zapisu a b

a b

a a

Początkowy stan taśmy b c) Tabela przejść Stan bieżący s s s s 1 1 1 1 2 2 2 2 Odczytany symbol a b Symbol do zapisu a b a a a a b b b b a a a b

Kierunek ruchu głowicy → → → − → → → − b a

Stan kolejny s s 1 k 1 1 s k

a b

a b

Początkowy stan taśmy a a b a b

Kierunek ruchu głowicy → → ← − → → ← − → → ← − b

Stan kolejny s s 1 k 1 1 2 k 2 2 1 k

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

116

Architektura i działanie komputera

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

Rozdział 4

Teoria informacji
Wiadomości są symbolami informacji, których znaczenie musi być wyuczone. Mehlis

4.1

Informacja vs. wiadomość

Mówimy dziś: informacja rządzi Światem. Gdy jednak zapytamy czym jest informacja z trudem z lawiny odpowiedzi wyłowimy jakąś sensowną i przekonywującą. Informacja nie jest bowiem czymś co można zobaczyć lub dotknąć. Niestety informacja jest czymś zasadniczym w informatyce, czymś niezastampialnym, gdyż cała informatyka zasadza swoje istnienie na przetwarzaniu informacji. Zatem brak informacji oznacza w konsekwencji brak informatyki. Drugim pojęciem silnie związanym z informatyką i informacją jest wiadomość. Niestety ani informacji ani wiadomości nie można precyzyjne zdefiniować1 . Dlatego też przyjmuje się, że oba te pojęcia są niedefiniowalne i rozumienie ich sprowadza się do intuicji. Podobnie my spróbujemy te pojęcia wyjaśnić. Zastanówmy się nad poniższymi dwoma wypowiedziami: Jest trzynasta. Godzinę temu była dwunasta. Jest trzynasta. Godzinę temu był ostatni w tym dniu samolot do Polski. Zauważmy, że obie wypowiedzi składają się z dwóch zdań. O ile w pierwszym przypadku wyraźnie widać, że każde ze zdań przekazuje tą samą wiaGłównie dlatego, że próba ich definicji sprowadza się do używania wcześniej nie zdefiniowanych pojęć, itd.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004 1

118

Teoria informacji

domość (więc jedno z nich jest zbędne) o tyle w drugim usunięcie jednego z nich pozbawia nas części. . . no właśnie — informacji. Co więcej dla tej samej informacji mogą istnieć różne wiadomości, które ją niosą, dla przykładu dwie poniższe wypowiedzi niosą tę samą informację: Nazywam się Bond. My name is Bond. Dalej prowadząc te rozważania można zauważyć, że ta sama wypowiedź może mieć różną wartość w zależności od tego kto lub gdzie ją wypowiada 2 . Rozważmy kolejny przykład, wypowiedź: „Pada śnieg”, co innego będzie znaczyć (będzie mieć inną wartość), gdy padnie z ust mieszkańca stacji badawczej gdzieś na Antarktydzie, a co innego jeśli wypowie ją członek karawany idącej przez Saharę. Dlaczego tak się dzieje? Tu niestety musimy odwołać się do intuicji, która podpowiada nam, że wiadomość jest rodzajem nośnika (sposobem jej zapisu) informacji, informacja jest zaś treścią wiadomości. Wiele osób próbowało odpowiedzieć bardziej formalnie na postawione pytanie, poniżej przedstawiamy próbę zdefiniowania informacji, sposobu jej opisu i mierzenia. A. Mazurkiewicz podaje następującą definicję informacji: Informacją nazywamy wielkość abstrakcyjną, która może być przechowywana w pewnych obiektach, przesyłana między pewnymi obiektami, przetwarzana w pewnych obiektach i stosowana do sterowania pewnymi obiektami. Przez obiekty rozumie się tutaj organizmy żywe, urządzenia techniczne oraz systemy takich obiektów.

4.2

Geneza i zakres teorii informacji

W ogólności pod pojęciem teorii informacji rozumiemy zagadnienia związane z kodowaniem, przesyłaniem i dekodowaniem wiadomości. Jako datę narodzin teorii informacji przyjęto uważać rok 1948, kiedy to w czasopiśmie The Bell System Technical Journal [Sha48] pojawiła się praca Claude Shannona zatytułowana A Mathematical Theory of Communication. Ciekawe również jest to, że jest to jedna z tych dziedzin, które niesamowicie
Zatem otoczenie również wnosi dodatkową informację lub mówiąc inaczej jest jej kontekstem.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004 2

4.2 Geneza i zakres teorii informacji

119

szybko się rozwijały i ich wartość zaważono od samego początku 3 Należy zaznaczyć, że przed Shannonem również inni zajmowali się podobnymi zagadnieniami. Warto wspomnieć choćby o budowaniu kodów przez Samuela Morsa, który w 1838 wymyślił używany do dziś kod. Kod Morsa przyporządkowuje kolejnym literom alfabetu kropki i kreski, przy czym Mors zastosował zasadę, że im dana litera częściej jest stosowana to ma krótszy kod i odwrotnie4 . Co prawda bazując na obecnej wiedzy, można by poprawić kod Morsa o 15% dla języka angielskiego, jednak w tamtych latach kod ten naprawdę był świetny. W 1943 roku Kongres amerykański zlecił wykonanie połączenia telegraficznego łączącego Waszyngton z Baltimore. W założeniu chciano umieścić przewody pod ziemią, jednak okazało się, że zakłócenia są tak duże, że ostatecznie zawieszono je na słupach. Odpowiedź na pytanie dlaczego tak się działo znajdziemy w teorii fizycznej, tu wystarczy jeśli zaakceptujemy fakt, że przenoszenie sygnału elektrycznego jakąkolwiek drogą niesie ze sobą niebezpieczeństwo zniekształcenia tego sygnału. Przekładając to na wcześniej wprowadzoną terminologię możemy powiedzieć, że na skutek zniekształcenia wiadomości poprzez własności jej nośnika zniekształcona zostaje również informacja niesiona przez tę wiadomość. Zauważono, że częściowym rozwiązaniem problemu może być wolniejsze telegrafowanie. I tu również znajdujemy odniesienie do codzienności, jeśli jesteśmy w głośnym pomieszczeniu to zupełnie bezsensowne jest szybkie mówienie do kogoś, nasz głos utonie w ogólnej wrzawie. Dużo lepiej jest mówić wolno i wyraźnie. W przypadku telegrafowania spowolnienie transmisji było nie dobrym rozwiązaniem, zatem próbowano pokonać trudności ustalając kilka wartości prądu i odpowiadające mu sygnały. Zatem próbowano zwiększyć ilość liter w alfabecie służącym do zapisu wiadomości 5 . W 1924 roku Harry Nyquist opublikowanej pracę wyjaśniającej na drodze matematycznej zależności pomiędzy szybkością telegrafowania a liczbą wyróżnionych wartości prądu jakich w tym celu użyto. W pracy tej stwierdzono, że jeśli kolejne sygnały wysyła się w ustalonych odstępach czasu, to następujący związek jest prawdziwy W = K log 2 n,
Dla przykładu pierwsza międzynarodowa konferencja, która dotyczyła wyłącznie teorii informacji odbyła się w 1950 roku, a w 1953 roku pojawiło się czasopismo poświęcone wyłącznie tej teorii. 4 Intuicja nam podpowiada, że jest to słuszne, w latach poźniejszych potwierdzono to formalnie i podano warunki konstruowana kodów optymalnych 5 Zauważmy, że Mors stosował tylko dwa symbole.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004 3

120

Teoria informacji

gdzie W oznacza szybkość telegrafowania, n — liczbę różnych wartości prądu a K jest stałą charakteryzującą szybkość wysyłania sygnałów. Z powyższego wzoru wynika, że prędkość wysyłania sygnału rośnie logarytmicznie wraz ze wzrostem ilości różnych sygnałów występujących we wiadomości. Stąd już łatwo wysunąć wniosek, że ze względu na charakter funkcji logarytm od pewnego momentu nie warto dodawać kolejnych sygnałów, gdyż daje to znikomy zysk prędkości. Cztery lata później Nyquist wykazał, że w każdym wysyłanym sygnale elektrycznym część energii jest zbędna; można ją usunąć a mimo to nadany sygnał będzie można odtworzyć. Składowe te nazwał nadmiarowymi, obecnie mówmy też o redundancji — nadmiarowości. W 1928 Hartley po raz pierwszy w historii, podaje definicję ilości informacji. Jeśli przyjmiemy, że wiadomość składa się z n - symboli alfabetu, który składa się z S liter, to miara H ilości informacji wynosi H = n log2 S. Jak później udowodnił to Shannon wzór Hartleya nie jest zawsze prawdziwy. Jest on słuszny tylko w przypadku, gdy dodatkowo zostanie założone, że wybór poszczególnych symboli jest niezależny oraz, że wystąpienia poszczególnych liter alfabetu są niezależne. W dalszym ciągu Shannon pracował nad teorią informacji i ostatecznie wykazał, że można nie zmniejszająć prędkości transmisji sygnału przekazać go możliwie wiernie przez kanał transmisyjny. Należy podkreślić, że praca Shannona wskazywała na możliwość rozwiązania tego problemu jednak nie podawała efektywnej metody jak to zrealizować. Spowodowało to rozłam w dalszych pracach nad teorią informacji na dwa główne nurty: teoretyczny — koncentrował się wokół poszukiwania miar ilości informacji, oraz praktyczny — w którym próbowano znaleźć najlepsze sposoby kodowania by przekazywać informację w sposób optymalny. Podamy teraz kilka sposobów kontroli czy informacja, która zastała przekazana (przesłana) jest poprawna, przy czym skoncentrujemy się na algorytmach stosowanych w komputerach i służących kontroli poprawności przesyłanych danych. Zatem koncentrujemy się wyłącznie na wiadomościach przekazywanych za pomocą kodu dwójkowego.

4.3

Metody kontroli poprawności danych

Informacje, które są dostarczane do komputera jak i do wszelkiego typu urządzeń mu towarzyszących, w odróżnieniu od tych, którymi posługuje się
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

4.3 Metody kontroli poprawności danych

121

człowiek, muszą być przekazywane bezbłędnie i jednoznacznie. Nie znaczy to, że człowiek nie powinien dostać informacji bezbłędnej, ale zwykle jeśli dostanie ją w jakimś stopniu uszkodzoną to „domyśli” się części brakującej. Komputer nie posiada tej cechy, zatem musi otrzymywać informację w formie oryginalnej, lub należy tak tę informację zakodować by mógł on w razie jej uszkodzenia ją odtworzyć. Niestety często zdarza się, że dane, będąc przekazywane w różny sposób, potrafią docierać niekompletne lub uszkodzone. Istnieje kilka metod pozwalających wykryć niepoprawnie przeprowadzoną transmisję, a nawet wskazać, gdzie ten błąd nastąpił. Przedstawione zostaną tutaj najpopularniejsze z nich. Ich cechą wspólną jest wydłużenia oryginalnego komunikatu przez dopisanie na jego końcu pewnego ciągu bitów, którego długość i sposób interpretacji jest zależny od przyjętej metody postępowania.

4.3.1

Bit parzystości

Najprostszym sposobem zabezpieczania informacji jest dodanie do niej bitu parzystości. W tym sposobie zabezpieczenia, do każdego komunikatu jest dopisywany dodatkowy bit. Bit ten ma wartość 1 — gdy liczba jedynek w komunikacie oryginalnym była nieparzysta, lub 0 — w przeciwnym przypadku. Przykład 4.1. Obliczenie i dodanie bitu parzystości do oryginalnej wiadomości. Komunikat przeznaczony do zakodowania: 10011011 Liczba "jedynek": 5 - nieparzysta Dodatkowy bit bedzie miał wartość 1 Komunikat po dopisaniu bitu parzystości: 100110111 | bit parzystości

W przypadku tej metody nadawca wysyła komunikat z dopisanym bitem parzystości. Po otrzymaniu informacji odbiorca oblicza bit parzystości dla otrzymanej wiadomości (czyli dla ciągu bitów po opuszczeniu ostatniego bitu — bitu parzystości). Jeśli otrzymany bit zgodny jest z bitem parzystości z komunikatu oznacza to, że wszystko jest w porządku.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

122

Teoria informacji

Niestety tego typu kontrola sprawdzi się jedynie w najprostszych sytuacjach. O tym, że jest to proste zabezpieczenie, świadczą poniższe przykłady. Przykład 4.2. Błędy nie wykrywalne przez bit parzystości.

Komunikat oryginaly...............................: 10011011 Bit parzystości dla oryginalnego komunikatu.......: 1 Błąd 1: zamiana miejscami bitu 7 i 8 Komunikat zniekształcony..........................: 01011011 Bit parzystości dla zniekształconego komunikatu...: 1 Błąd 2: ustawienia wartości bitów 4 i 5 na przeciwne Komunikat zniekształcony..........................: 10000011 Bit parzystości dla zniekształconego komunikatu...: 1 Błąd 3: pierwsze 6 bitów, bez względu na ich rzeczywistą wartość, ustawiana jest na 1 Komunikat zniekształcony..........................: 10111111 Bit parzystości dla zniekształconego komunikatu...: 1

Tego typu przekłamania zdarzyć się mogą tym łatwiej im dłuższe komunikaty będą przesyłane, oraz im gorszym medium będzie prowadzona transmisja (światłowód będzie wprowadzał mała ilość przekłamań, a łącze podczerwone we mgle ogromną).

4.3.2

Suma kontrolna

Kolejny sposób zabezpieczania danych to obliczenie i dopisanie sumy kontrolnej do oryginalnej wiadomości. W tym przypadku nadawany komunikat dzielony jest na „porcje” jednakowej długości, jeśli zdarzy się, że wiadomość składa się z ilości bitów niepodzielnej przez długość porcji to do oryginalnej wiadomości dopisywana jest odpowiednia ilość zer. Porcje następnie są dodawane do siebie. Przy czym suma jest przedstawiana na takiej ilości bitów
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

4.3 Metody kontroli poprawności danych

123

jak i porcje danych. Jeśli pojawi się przepełnienie, to jest ono ignorowane 6 . Ostatecznie suma kontrolna jest dopisywana na końcu wiadomości. Przykład 4.3. Obliczanie sumy kontrolnej przy porcji równej 3 bity. Komunikat oryginalny..........: 10011011 Komunikat uzupełniony zerami..: 010011011 Kolejne porcja................: P1=011, P2=011, P3=010 Obliczanie sumy...............: S1=P1=011 S2=S1+P2=110 S3=S2+P3=1000 pierwsza jedynka od lewej stanowi przepełnienie, zatem jest pomijana, stąd S3=000 Komunikat przesyłany (z dopisaną sumą kontrolną): 00010011011

To zabezpieczenie jest pewniejsze od poprzedniego, poniższe przykłady ilustrują w jaki sposób błędy w komunikacie przenoszą się na sumę kontrolną. Przykład 4.4. Błędy wykryte przez sumę kontrolną. Komunikat oryginalny..........: Komunikat uzupełniony zerami..: Kolejne porcja................: Suma..........................: 10011011 010011011 P1=011, P2=011, P3=010 000

Błąd 1: zamiana miejscami bitu 7 i 8 Komunikat oryginalny..........: 10011011 Komunikat zniekształcony......: 01011011 Komunikat uzupełniony zerami..: 001011011 Kolejne porcja................: P1=011, P2=011, P3=001 Suma..........................: 111
Możemy je także potraktować jako kolejną „porcję” danych i dodać do istniejącej sumy.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004 6

124

Teoria informacji

Błąd 2: ustawienie wartości bitów 4 i 5 na przeciwne Komunikat oryginalny..........: 10011011 Komunikat zniekształcony......: 10000011 Komunikat uzupełniony zerami..: 010000011 Kolejne porcja................: P1=011, P2=000, P3=010 Suma..........................: 101

Niestety i ten sposób nie jest pozbawiony wad, ilustrują to poniższe przykłady. Przykład 4.5. Błędy nie wykrywalne przez sumę kontrolną. Komunikat oryginalny..........: Komunikat uzupełniony zerami..: Kolejne porcja................: Suma..........................: 10011011 010011011 P1=011, P2=011, P3=010 000

Błąd 1: pierwsze 6 bitów, bez względu na ich rzeczywistą wartość, ustawiana jest na 1 Komunikat oryginalny..........: 10011011 Komunikat zniekształcony......: 10111111 Komunikat uzupełniony zerami..: 010111111 Kolejne porcja................: P1=111, P2=111, P3=010 Suma..........................: 000 Błąd 2: omyłkowe potraktowanie sygnału jako jeden z bitów komunikatu (8, wartość 0) i zamiana miejscami porcji P2 i P3 Komunikat oryginalny..........: 10011011 Komunikat zniekształcony......: 011010011 Komunikat uzupełniony zerami..: 011010011 Kolejne porcja................: P1=011, P2=010, P3=011 Suma..........................: 000

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

4.3 Metody kontroli poprawności danych

125

Z powyższych przykładów widać, że zastosowanie sumy kontrolnej nie daje się już tak łatwo „oszukać” jak wykorzystanie bitu parzystości, ale wciąż nie jest to metoda pewna detekcji błędów transmisji.

4.3.3

Kod CRC

Próbą ominięcia niedoskonałości wyżej omawianych sposobów zabezpieczania danych jest kod CRC (ang. Cyclic Redundancy Code) — cykliczny kod nadmiarowy. W tej metodzie do danych dopisywany jest kod CRC, który jest otrzymywany przez nieco bardziej złożoną procedurę obliczeniową niż poprzednio. W praktyce cały algorytm daje się sprowadzić do elementarnych operacji i może być bardzo łatwo zaimplementowany sprzętowo. Do obliczenia kodu CRC potrzebny jest tak zwany wielomian generacyjny. Stanowi on podstawę do obliczeń i dlatego jego znajomość jest niezbędna zarówno dla nadawcy jak i odbiorcy. Wielomianem generacyjnym to łańcuch bitów, w którym kolejne pozycje są współczynnikami przy odpowiednich potęgach wielomianu. Na przykład wielomian postaci x4 + x 2 + 1 możemy zapisać jako 1x4 + 0x3 + 1x2 + 0x1 + 1x0 co ostatecznie daje następujący ciąg zerojedynkowy 10101 Zarówno najstarszy jak i najmłodszy bit wielomianu generacyjnego musi być równy 1, a ciąg przeznaczony do zakodowania musi być od niego dłuższy. Przy obliczeniach obowiązuje arytmetyka modulo 2, przy czym nie uwzględnia się przeniesień przy dodawaniu i pożyczek przy odejmowaniu. Operacje te są zatem równoznaczne z funkcją XOR. Na przykład 10110011 + 11001010 ---------01111001 11110000 - 10100110 ---------01010110

Do ciągu danych należy dodać na końcu tyle bitów zerowych ile wynosi stopień wielomianu generacyjnego (w przykładzie 4.6 wynosi 4). Następnie należy rozszerzony tak ciąg danych podzielić przez wielomian generacyjny. Dzielenie jest wykonywane według następującego algorytmu:
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

126

Teoria informacji

1. Rozpoczynamy od bitu danych położonego z lewej strony. 2. Jeśli jest on równy 1, to poniżej przepisujemy wielomian generacyjny. 3. Jeśli 0, to wpisujemy same zera. 4. Wykonujemy dodawanie modulo 2 (czyli stosujemy funkcje XOR). 5. Próba odczytu z ciągu danych kolejnego bitu danych. 6. Jeśli próba się powiodła to idziemy do kroku 2. 7. W przeciwnym razie koniec. Reszta, która pozostanie z dzielenia, jest właśnie poszukiwanym kodem CRC. Bity te należy wpisać na koniec ciągu danych zamiast uprzednio dodanych bitów zerowych. Przykład 4.6. Obliczanie kodu CRC.

Ciag z danymi..............: 10011011 Generator..................: 10101 Ciag z dopisanymi zerami...: 100110110000 100110110000 : 10101 10101....... -----....... 00110....... 01100...... 00000...... -----...... 01100...... 11001..... 10101..... -----..... 01100..... 11001.... 10101.... -----.... 01100.... 11000...
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

4.3 Metody kontroli poprawności danych

127

10101... -----... 01101... 11010.. 10101.. -----.. 01111.. 11110. 10101. -----. 01011. 10110 10101 ----00011 0011 <- kod CRC Ciag z dopisanym kodem CRC...: 100110110011

Przy sprawdzaniu poprawności danych stosuję się taki sam algorytm, lecz tym razem dla całego ciągu bitów (łącznie z kodem CRC), jeśli transmisja była bezbłędna, reszta z dzielenia będzie wynosiła zero. W praktycznych zastosowaniach używa się następujących wielomianów CRC-16 CRC-16/CCITT CRC-32 = = = x16 + x15 + x2 + x0 x16 + x12 + x5 + x0 x32 + x26 + x23 + x21 + x16 + x12 + x11 + x10 + x8 + x7 + x5 + x4 + x2 + x1 + x0

Dla uproszczenia zapisu binarną postać wielomianu podaje się w zapisie szesnastkowym pomijając najbardziej znaczący bit. Wówczas podane powyżej wielomiany zapisać można jako CRC-16 CRC-167 CRC-32 = = = 1.1000.0000.0000.0101 1.0001.0000.0010.0001 1.0000.0100.1010.0001.0001.1101.1011.0111 8005 1021 04A11DB7

Szczególnie dwa ostatnie wielomiany godne są polecenia, przy ich wykorzystaniu możliwe jest: wyłapanie wszystkich błędów pojedynczych i podwójnych, błędów z nieparzystą liczbą bitów, błędów seryjnych o długości
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

128

Teoria informacji

mniejszej od 16 bitów oraz 99.99% błędów seryjnych 18-bitowych i dłuższych.

4.4

Zadania

1. Oblicz wartości sumy kontrolnej dla następujących ciągów bitów (suma kontrolna zapisywana jest na 4 bitach). a) 1001 1110 0111 1100 1101 0101 b) 1001 0110 0101 0001 0000 0010 c) 0010 0101 1010 1100 1010 1000 d) 0101 0100 1000 0100 1000 0101 2. Oblicz wartości kodu CRC dla ciągów bitów z poprzedniego ćwiczenia. Jako wielomian generacyjny przyjmujemy 10101. 3. Który z poniższych ciągów nie uległ przekłamaniu podczas transmisji? (Wielomian generacyjny jak w poprzednim przykładzie). a) 1000 0100 0100 1000 1010 0100 0100 b) 0110 0100 0100 0100 0000 1100 0101 c) 0000 0010 0010 1000 1001 0010 0110 d) 0110 0010 0010 1000 1001 0010 1111

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

Rozdział 5

Algorytmy i struktury danych
Programy stanowią w końcu skonkretyzowane sformułowania abstrakcyjnych algorytmów na podstawie określonej reprezentacji i struktur danych. Niklaus Wirth

5.1

Pojęcie algorytmu

Słowo algorytm, jak to wyjaśniono w punkcie 1.2.1, pochodzi od nazwiska arabskiego matematyka. Oznacza ono procedurę czy też przepis na realizację jakiegoś zadania. Przykładem może być tu wspominany algorytm Euklidesa znajdowania największego wspólnego dzielnika liczb (zob. 1.2.1), czy wyznaczanie podziału kąta na dwie równe części za pomocą cyrkla i linijki. Ale równie dobrze algorytmem możemy nazwać procedurę wymiany uszkodzonego elementu w komputerze. Pierwsze opisy, które później nazwano algorytmami, dotyczyły rozwiązań zadań matematycznych. Już w starożytnym Egipcie i Grecji stworzono wiele metod, które pozwalały rozwiązywać pewne zadania w sposób algorytmiczny. Intuicyjnie algorytm kojarzy się z metodą rozwiązywania zadania, z przepisem postępowania czy ze schematem działania. Należy jednak podkreślić, że nie każda metoda czy schemat jest algorytmem. Algorytm powinien spełniać następujące wymagania: 1. Musi posiadać określony stan początkowy, czyli operację od której zaczyna się jego realizacja.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

130

Algorytmy i struktury danych

2. Ilość operacji potrzebnych do zakończenia pracy musi być skończona — warunek dyskretności. 3. Musi dać się zastosować do rozwiązywania całej klasy zagadnień, a nie jednego konkretnego zadania — warunek uniwersalności. 4. Interpretacja poszczególnych etapów wykonania musi być jednoznaczna — warunek jednoznaczności. 5. Cel musi być osiągnięty w akceptowalnym czasie — warunek efektywności1 . 6. Musi posiadać wyróżniony koniec. Wymaga się zatem, aby algorytm składał się ze skończonej liczby operacji (poleceń) sformułowanych w sposób jednoznaczny (i jasny 2 ) oraz możliwy do wykonania w skończonym i zarazem rozsądnym czasie. Poprzez „rozsądny” rozumiemy czas akceptowalny dla danego zadania, w dalszym ciągu zostanie to jeszcze szerzej omówione. Zaś „ jasny” oznacza tyle co zrozumiały dla ludzi znających zasady konstruowania i czytania algorytmów. W przeszłości matematycy czasami próbowali rozwiązywać różne problemy w sposób algorytmiczny, ale na ogół nie przywiązywano większej wagi do precyzji opisu kolejnych kroków procedury. Wraz z rozwojem matematyki pojawiły się problemy, co do których nie było pewności czy można je rozwiązać w sposób algorytmiczny, czy też nie. Dopiero wówczas zaczęto zastanawiać się nad tym, jak precyzyjnie sformułować pojęcie algorytmu. Aby wykazać, że dany problem można rozwiązać w sposób algorytmiczny wystarczy podać konkretny algorytm rozwiązania tego problemu. Jeśli jednak chcemy udowodnić, że dany problem nie da się rozwiązać algorytmicznie potrzeba dowieść, że nie istnieje algorytm rozwiązania danego problemu. A to powoduje, że potrzebna jest ścisła definicja algorytmu. W latach dwudziestych XX w. zagadnienie ścisłego, w sensie matematycznym, określenia pojęcia algorytmu było jednym z głównych problemów matematyki. Definicje te zostały opracowane m. in. przez Alana Turinga, E. L. Posta i A. Churcha. Na podstawie tych definicji wykazano, że w matematyce istnieje wiele problemów, których nie można rozwiązać w sposób algorytmiczny.
Warunek ten nie jest konieczny do poprawności działania algorytmu, ale z drugiej strony nikt nie będzie używał poprawnego algorytmu na którego wynik będzie musiał czekać kilkaset lat... 2 Podobnie jak poprzednia uwaga, nie jest to warunek konieczny, ale z drugiej strony szkoda tracić czas na analizę niejasnych procedur.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004 1

5.1 Pojęcie algorytmu

131

Zainteresowanie algorytmami wzrastało wraz z rozwojem informatyki. W latach pięćdziesiątych pojawił się nowy wykonawca algorytmu — komputer. Oznaczało to z jednej strony potencjalny wzrost prędkości przetwarzania danych, a co za tym idzie coraz więcej algorytmów stawało się rozwiązalnych w skończonym czasie. Lecz z drugiej strony, wymagało to dopracowania formalizmu definicji algorytmów, jak i powstania języków programowania, za pomocą których te algorytmy miały być zapisywane i wpisywane do komputera. Spróbujmy zastanowić się nad rolą algorytmów i języka programowania. Jak to zostało powiedziane we wcześniejszych rozdziałach (zob. 3), komputer dysponuje pewnym określonym zestawem operacji. Aby więc przekazać mu do wykonania jakieś zadanie, należy opisać algorytm, a następnie zapisać go przy pomocy tych instrukcji, które dany komputer potrafi wykonać; instrukcje te muszą być przekazane w formie zrozumiałej dla niego. Wyrażony w ten sposób algorytm nazywa się programem 3 . Początkowo jedynym językiem komunikowania się człowieka z komputerem był język wewnętrzny maszyny a następnie asembler (zob. 3.4.5). Jednak trudności jakie temu towarzyszyły, doprowadziły szybko do powstania wielu nowych, niezależnych od konkretnego komputera, języków programowania (zob. 6). Wraz z rozwojem informatyki algorytm stał się pojęciem podstawowym. Jest on, z jednej strony, narzędziem pracy informatyka, służącym do rozwiązywania z pomocą komputera różnego rodzaju problemów, z drugiej zaś, stanowi przedmiot badań. Interesujące jest nie tylko, jak skonstruować algorytm rozwiązujący dane zagadnienie, ale także czy jest on poprawny oraz realizowalny w skończonym czasie, tzn. czy daje prawidłowe rezultaty i czy czas jego wykonania jest „rozsądny”. „Rozsądny” w tym kontekście oznacza, że czas realizacji jest miarą względną, nie zaś bezwzględną. Przykładowo dla algorytmu obliczającego pewne skomplikowane zadanie numeryczne (np. symulacja położenia planet), zaakceptujemy czas obliczenia rzędu minut, jeśli jednak poszukujemy pierwiastków trójmianu kwadratowego, taki czas wydaje się być zbyt długi. Problematyka związana z projektowaniem, oceną jakości i poprawności algorytmów nosi nazwę analizy algorytmów.

Oczywiście w skład dużego programu może wchodzić wiele pomniejszych algorytmów, jednak można je wszystkie postrzegać jako jeden złożony algorytm realizujący pewne złożone zadanie.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

3

132

Algorytmy i struktury danych

5.2

Struktury danych

Dzisiejsze komputery w dużym stopniu służą do przechowywania i wyszukiwania informacji, której jest coraz więcej. Ponadto oczekujemy również możliwości kategoriowania tej informacji i jej hierarchizacji. Wszystko po to by móc znaleźć potrzebne dane szybciej i dokładnej 4 . Wszelka informacja przechowywana w komputerach jest tylko pewnym wycinkiem świata rzeczywistego, można powiedzieć iż stanowi ona jedynie pewien abstrakcyjny model. Co więcej model ten albo inaczej mówiąc wycinek rzeczywistości może być inny w zależności od tego dla jakich potrzeb będzie on konstruowany. Dla przykładu jeśli potrzebujemy stworzyć program do ewidencji pacjentów w szpitalu (abstrakcyjny model pacjenta) będziemy potrzebowali innych danych o fizycznie istniejącym pacjencie niż w programie do ewidencji dłużników, chodź w obu przypadkach możemy mówić o tej samej osobie. Zatem abstrakcja w tym miejscu oznacza ignorowanie tych cech danego rzeczywistego obiektu, które nie są dla rozwiązania danego problemu potrzebne5 . W przypadku przystępowania do rozwiązania problemu za pomocą komputera należy najpierw zastanowić się jakie dane będziemy przetwarzać, a następnie w jaki sposób będą one w komputerze reprezentowane — czyli jakie struktury danych będą je przechowywały. Często przy wyborze sposobu reprezentacji bierze się pod uwagę pewne ograniczenia maszyny wynikające chociażby z architektury dzisiejszych komputerów. Dla przykładu wiemy, że liczby zmiennoprzecinkowe są reprezentowane z pewną dokładnością. Zatem wybór reprezentacji często nie jest łatwy, co więcej bywa często tak, że daną informację można reprezentować na kilka sposobów i każdy z nich ma pewne wady i zalety. Prowadząc dalej to rozumowanie dochodzimy do wniosku, że wybór struktury danych determinuje wybór algorytmów na nich operujących. To w konsekwencji prowadzi do określonej wydajności i sprawności całego programu.

5.2.1

Typ danych

Jeśli przypomnimy sobie pewne elementarne pojęcia z matematyki to wśród nich znajdziemy takie elementy jak liczby rzeczywiste czy liczby całkowite. Dalej mówimy o zmiennych całkowitych lub zmiennych rzeczywistych, czyli
4 Najprostszym przykładem może być poszukiwanie informacji za pomocą wyszukiwarek internetowych: zwykle znajdujemy wiele stron zawierających dane słowo. Jednak odszukanie naprawdę przydatnej dla nas informacji spoczywa niestety na nas. 5 Wynika stąd wprost, że model jest zawsze uproszczeniem rzeczywistości.

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

5.2 Struktury danych

133

elementach odpowiednio zbioru liczb całkowitych i rzeczywistych. W przypadku algorytmów przetwarzających dane i dalej języków programowania w których te algorytmy są zapisywane również wprowadza się pojęcie zmiennej i jej typu, czyli zbioru dopuszczalnych jej wartości 6 . Zwykle wyróżnia się typy proste oraz typy złożone. Do typów prostych zalicza się z reguły typy liczbowe takie jak całkowity czy rzeczywisty; typ znakowy — dla zmiennych zapamiętujących znaki alfanumeryczne; typ logiczny7 — dla zmiennych logicznych, i wiele innych które są już zależne od konkretnego języka programowania. Z kolei typy złożone są konglomeratami zmiennych prostych lub innych typów złożonych, i tu zasadniczo wyróżnia się tablicę, rekord, plik, zbiór. Omówimy teraz kolejno wymienione typy złożone, przy czym skupimy się wyłącznie na najważniejszych ich cechach bez wchodzenia w niuanse implementacyjne czy też warianty tych typów. Szersze omówienie zarówno struktur danych jak i algorytmów można znaleźć w specjalistycznych książkach.

5.2.2

Tablica

Tablica (ang. array) jest jedną z najpowszechniej stosowanych struktur danych, ponadto występuje na poziomie implementacyjnym w prawie wszystkich językach programowania. Jest to struktura danych pozwalająca zapamiętywać wyłącznie elementy tego samego typu. Wyobraźmy sobie pojemnik na płyty CD, nie mamy możliwości włożyć do niego np. kasety VHS. Kolejną cechą tablic jest dostęp swobodny (ang. random access) do jej elementów — oznacza to, że dostęp do dowolnego elementu tablicy odbywa się w taki sam sposób, oraz że można do nich się odwoływać w dowolnej kolejności. Tu znów posłużmy się analogią ze stojakiem na płyty CD: po każdą z płyt można sięgnąć w ten sam sposób — swobodnie. W przypadku tablic do odwołania się (sięgnięcia) do konkretnego jej elementu stosuje się indeksowanie. To znaczy podajemy oprócz nazwy tablicy numer elementu do którego chcemy się odwołać. Dla przykładu niech T będzie nazwą zmiennej tablicowej, odwołanie do elementu o indeksie 1 i zapisanie do niego wartości 3 będzie miało postać: T[1] := 3
Istnieją języki programowani w których nie ma konieczności informowania jakiego typu jest dana zmienna, jednak nie będziemy ich tu szczegółowo omawiać. Według naszej opinii u początkujących informatyków wprowadzają one niepotrzebne zamieszanie i uczą złych nawyków. 7 Zwany również typem boolowskim.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004 6

134

Algorytmy i struktury danych

W prezentowanym przykładzie zastosowano znak := oznacza on przypisanie wartości, dla odróżnienia od znaku =, który będzie oznaczał porównanie dwu wartości. Przy czym zapis powyższy jest sprawą umowną, która jest zależna od przyjętej symboliki, lub języka programowania, jeśli jest to zapis w konkretnym już języku8 . Definicję typu tablicowego będziemy zapisywali w następujący sposób: type tablica = array[1..10] of Integer i zapis ten oznacza definicję typu o nazwie tablica, który będzie się składał z dziesięciu elementów typu całkowitego, przy czym pierwszy dopuszczalny indeks będzie miał wartość 1, ostatni zaś 10. Przy definiowaniu typu tablicowego dla celów analizy algorytmu, dopuszcza się często indeksu dowolne, tj. powyższa definicja mogłaby mieć postać: type tablica = array[-1..8] of Integer i również oznaczałaby typ tablicowy złożony z liczb całkowitych, jednak o innych dopuszczalnych wartościach najmniejszych i największych niż w poprzednim przykładzie9 Nic też nie stoi na przeszkodzie by mówić o tablicach wielowymiarowych, w których każdy z indeksów może przebiegać inny przedział wartości. Jeśli chodzi o intuicję dla dwuwymiarowej tablicy to jest ona prosta, jeśli przypomnimy sobie określenie macierzy w matematyce. Jest to jest to właśnie tablica dwuwymiarowa. Jako przykład tablicy trójwymiarowej można podać choćby kostkę Rubika, natomiast jeśli chodzi o większą ilość wymiarów to tu już wyobraźnia zawodzi10 . Niemniej jednak można podać prosty przykład uzasadniający stosowanie tego typu konstrukcji. Rozważmy wielopiętrowy budynek — jego adres jest pierwszym wymiarem, następnie piętro — drugi wymiar, korytarz — trzeci, numer pokoju na korytarzu — czwarty. Oczywiście można ten przykład rozwinąć bez problemu na 5 czy 6 wymiarów dołączając miasto, kraj itp.

5.2.3

Rekord

Jak można było zauważyć pewną wadą tablicy jest konieczność przechowywania elementów tego samego typu11 . W pewnych sytuacjach jest to nieMy zapożyczyliśmy symbolikę z języka Pascal. Nie można tej własności przenieść na języki programowania — nie wszystkie pozwalają na coś takiego. 10 Przynajmniej piszących te słowa autorów. 11 Co prawda konkretne realizację tablic, w istniejących językach programowania, dopuszczają wkładanie do tablicy elementów różnych typów, ale tak naprawdę są to nowe
9 c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004 8

5.2 Struktury danych

135

dogodne, ale do takich celów zastał zaprojektowany rekord (ang. record), który jest najbardziej ogólną metodą na stworzenie struktury danych. W rekordzie istnieje możliwość zapamiętania dowolnej ale określonej ilości danych dowolnych typów. Elementy występujące w rekordzie nazwiemy jego polami. Jako przykład takich konstrukcji może służyć rekord danych osobowych, gdzie będzie zapamiętane imię, nazwisko, wiek itp. Dopuszcza się również, by elementami (składowymi) rekordu były inne typy złożone, czyli np. tablice, rekordy, zbiory itd. Głównym przeznaczeniem rekordu jest powiązanie różnych informacji w jedną logiczną całość, tak by móc się do niej odwoływać wspólnie. Przykładowo jeśli znajdziemy poszukiwaną osobę z imienia i nazwiska, z pewnością będą nas interesowały dodatkowe informacje z nią związane, jak np. adres zamieszkania12 . Typ rekordowy będziemy definiowali w następujący sposób: type osoba = rekord imie: napis; nazwisko: napis; wiek: Integer; end Powyższy przykład definiuje rekord składający się z imienia. nazwiska, wieku13 . W przeciwieństwie do tablicy w rekordzie nie ma mowy o 5-tym czy 2-gim elemencie, mówimy wyłącznie o polu o określonej nazwie. Zatem odwołanie od konkretnego pola odbywa się poprzez określenie o jaki rekord nam chodzi i o jakie jego pole, zwykle te nazwy oddzielone są kropką 14 . Przyjmując definicje rekordu osoba z powyższego przykładu, odwołanie się do jego pól może mieć postać: o: osoba; wypisz(o.imie); o.wiek := 12; Powyższy zapis oznacza definicję zmiennej o nazwie „o”, która jest typu „osoba”, a następnie odwołanie się do imienia z tego rekordu i wypisanie go, po czym wpisanie wartości do pola „wiek” w tym rekordzie.
konstrukcje struktur złożonych, tzw. tablice z wariantami. 12 Faktycznie adres zamieszkania może stanowić również rekord. 13 W definicji użyto typu „napis”, który wcześniej nie był zdefiniowany, ale przyjmujemy jego intuicyjne znaczenie. 14 „Zwykle” ponieważ zależy to od przyjętej notacji — kropkę stosuje większość języków programowania.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

136

Algorytmy i struktury danych

Wprowadza się również pojęcie rekordu z wariantami, jest to taka konstrukcja, która może mieć pewne pola alternatywne, dla przykładu dla osoby można zapamiętać wiek, lub datę urodzenia. Jednak nie będziemy tej wersji omawiali tu szerzej, zainteresowany czytelnik może znaleźć opis tej struktury danych np. w książce [Wir01].

5.2.4

Zbiór

Kolejną strukturą danych, o której warto wspomnieć jest zbiór (ang. set). Jest to odpowiednik pojęcia z matematyki, zatem dla każdego zbioru mamy określone operacje: przecięcia (część wspólna), sumy, różnicy, przynależności. Definicja typu zbiorowego ma następującą postać: type T = set of typBazowy; oznacza ona, że T jest zbiorem elementów typu typBazowy. Wartościami zmiennej typu T są wszystkie możliwe podzbiory elementów typu bazowego. Zbiór jest ciekawą konstrukcją, która nie posiada cech swoich poprzedników. To znaczy nie możemy odwołać się do 3-go elementu, ponieważ elementy w zbiorze nie mają kolejności, ani też nie możemy odwołać się poprzez nazwę elementu, gdyż jej nie posiadają. Za to łatwo możemy odpowiedzieć na pytania, czy zbiór jest pusty, czy dany element należy do zbioru itp.15

5.2.5

Plik

Omawiane do tej pory struktury danych charakteryzowały się tym, że była znana ich wielkość16 . Zatem stosunkowo łatwo było je przechowywać w pamięci. Niestety istnieje całą rzesza dynamicznych struktur danych takich jak kolejki, drzewa, grafy itp., które nie posiadają tej cechy. Przejrzyjmy się jednej z takich struktur danych, a mianowicie plikowi. Plik reprezentuje strukturę danych, która odpowiada koncepcji ciągu. Ciąg jest sekwencyjną strukturą danych, gdzie elementy są ustawione jeden po drugim. Elementami ciągu mogą być rekordy, tablice i inne typy złożone, przy czym ich ilość jest nieograniczona, zatem zawsze można dopisać następny element do ciągu17 . Podstawowe operacje na ciągach to: pobranie elementu pierwszego,
15

Jest to dokładny odpowiednik pojęcia zbioru znanego z matematyki. Oczywiście o ile znana była wielkość ich elementów. 17 O ile oczywiście wystarczy nośnika danych.
16 c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

5.3 Metody opisu algorytmów

137

dodanie elementu na koniec, połączenie dwu ciągów w jeden ciąg. Fizycznie ciąg jest reprezentowany poprzez plik sekwencyjny, gdyż to pozwala odwzorować charakter ciągu i operacji na nim wykonywanych. Definicja typu plikowego ma postać: type T = file of typBazowy; Dla pliku zdefiniowane są podstawowe operacje, takie jak: utworzenie pliku, otwarcie pliku, dopisanie elementu na końcu pliku, odczytanie następnego elementu pliku.

5.3

Metody opisu algorytmów

Jak wspomniano wcześniej algorytm jest pewnego rodzaju sformalizowanym zapisem pewnej procedury. W celu uniknięcia nieporozumień co do znaczenia pewnych słów, która mogłaby spowodować złą interpretację algorytmu, wprowadza się umowne sposoby zapisu algorytmów. I tak zasadniczo wyróżnia się zapis przy pomocy: schematu blokowego, metajęzyka programowania oraz ograniczonego podzbioru języka naturalnego. Omówimy pokrótce każdy z tych sposobów.

5.3.1

Schemat blokowy

Schematy blokowe są uniwersalną i dającą odpowiedni stopień ogólności formą zapisu algorytmu. Cechuje je przy tym duża przejrzystość i czytelność. Schematy te odzwierciedlają dobrze strukturę algorytmu w tym takie elementy jak rozgałęzienia czy punkty decyzyjne. Punkt decyzyjny to miejsce w którym podejmowana jest decyzja, którą z gałęzi (dróg) algorytmu pójść, decyzja ta jest podejmowana na podstawie pewnego warunku lub ich zespołu. Zatem punkt decyzyjny jest miejscem rozgałęzienia algorytmu. Rysunek 5.1 zawiera podstawowe elementy schematu blokowego, których znaczenie jest następujące: Stan — Określa zwykle moment startu i końca. Zapis/odczyt — Wskazuje miejsce w których odbywa się zapis danych (bądź ich odczyt) na nośniki informacji. Instrukcje — Blok instrukcji, które mają być wykonane. Decyzja — Wyliczenie warunku logicznego znajdującego się wewnątrz symbolu i podjęcie na jego podstawie decyzji.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

138

Algorytmy i struktury danych

Rysunek 5.1: Przykładowe symbole stosowane na schematach blokowych.

Łącznik — Połączenie z inną częścią schematu blokowego, np. gdy nie mieści się on na jednej stronie. Schemat blokowy, zwany również siecią działań, tworzony jest według pewnych reguł: 1. schemat blokowy składa się z bloków połączonych zorientowanymi liniami; 2. bloki obrazują ciąg operacji; 3. zawsze wykonywane są albo wszystkie instrukcje w bloku albo żadna; 4. dalsze operacje nie zależą od poprzednich wariantów, chyba że zależności te zostały przekazane za pomocą danych; 5. kolejność wykonania operacji jest ściśle określona przez zorientowane linie łączące poszczególne bloki; 6. do każdego bloku może prowadzić co najwyżej jedna linia; 7. linie mogą się łączyć a punkty takiego połączenia określane są jako punkty zbiegu. Na rysunku 5.2 znajduje się fragment algorytmu do obliczania pierwiastków trójmianu. Łączniki A i B informują, że dalsze części algorytmu znajdują się na osobnym schemacie lub schematach. Wtedy tamte schematy muszą zaczynać się od odpowiedniego łącznika.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

5.3 Metody opisu algorytmów

139

Rysunek 5.2: Schemat blokowy — fragment algorytmu znajdowania pierwiastków trójmianu.

5.3.2

Schemat zapisu algorytmu za pomocą pseudo-języka

Inną metodą przedstawienia algorytmu jest użycie zapisu za pomocą pewnego pseudo-języka programowania. Zaletą tego podejścia jest bardzo łatwa późniejsza implementacja za pomocą już konkretnie wybranego, istniejącego języka programowania 18 . Wadę stanowi natomiast mniejsza przejrzystość. Dla osób nie przywykłych do analizowania kodu źródłowego programów, dużo czytelniejsze jest przedstawienie w postaci schematu blokowego. Poniżej przedstawiamy typowe operacje użyte do opisu algorytmu za pomocą omawianej metody, przy czym dla porównania, tam gdzie to jest
Dzieje się tak ponieważ większość konstrukcji podstawowych takich jak warunek, czy pętla, mają podobną postać w różnych językach.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004 18

140

Algorytmy i struktury danych

Rysunek 5.3: Instrukcja warunkowa if zapisana w postaci schematu blokowego: a) warunek if (W) instrukcje, b) if (W) instrukcje1 else instrukcje2.

sensowne, przedstawiamy za pomocą rysunku ich postać blokową. Podstawowe instrukcje służące do opisu algorytmów: • instrukcja podstawienia a:=b; W wyniku wykonania tej instrukcji zmienna a przyjmuje wartość zmiennej b. • instrukcja warunkowa if (warunek) then begin instrukcje realizowane gdy warunek jest spełniony end else begin instrukcje realizowane gdy warunek jest niespełniony end Podstawowa instrukcja warunkowa, wpierw wyliczany jest warunek w nawiasie19 , następnie jeśli jest on prawdziwy wykonywane są in19

„Wyliczany” oznacza w tym miejscu tyle co wyznaczana jest jego wartość logiczna.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

5.3 Metody opisu algorytmów

141

Rysunek 5.4: Pętla while i do--while zapisana w postaci schematu blokowego.

strukcje w pierwszym bloku, w przeciwnym razie w drugim bloku, po słowie else. Często używany jest również w uproszczonej wersji, bez części „w przeciwnym razie”. Ilustracja graficzna znajduje się na rysunku 5.3. • instrukcje pętli do–while i while do begin ... ciąg instrukcji powtarzanych dopóki warunek jest spełniony ... end while (warunek); while (warunek) begin ... ciąg instrukcji powtarzanych dopóki warunek jest spełniony ... end Są to dwie pętle, umożliwiające wykonywanie cykliczne instrukcji, które znajdują się w ich wnętrzu. Przy czym instrukcje te są wykonywane tak długo, jak długo warunek ma wartość logiczną prawda.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

142

Algorytmy i struktury danych

Rysunek 5.5: Pętla for zapisana w postaci schematu blokowego.

Różnica pomiędzy tymi dwoma pętlami sprowadza się do kolejności wykonania instrukcji wewnętrznych i sprawdzenia warunku. Pierwsza z nich wpierw wykonuje instrukcje a następnie sprawdza warunek, druga zaś odwrotnie. Ilustracja graficzna omawianych pętli znajduje się na rysunku 5.4. • instrukcji pętli for for i:=1 to 10 step 1 do begin ... instrukcje powtarzane 10 razy ... end Pętla ta wykonuje cyklicznie instrukcje zawarte pomiędzy słowani begin i end. Przy czym wykonanie zaczyna się od przypisania zmiennej i wartości 1 — początek (P), po czym następuje sprawdzenie czy i nie przekracza wartości granicznej 10 — warunek końca (K). Dalej jeśli warunek ten jest fałszywy wykonane zostają instrukcje, następnie zostaje zwiększona zmienna i o wartość występującą po słowie step
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

5.4 Podstawowe algorytmy

143

(S) i na koniec następuje przejście do sprawdzania warunku (K). Ilustracja graficzna z odpowienim oznaczeniem literowym znajduje się na rysunku 5.5. Można jeszcze dodać, że zamiast słowa to można zastosować słowo downto, które informuje, że zmienna ma być zmniejszana o zadany krok a nie zwiększana. Można również pomijać jawne wskazanie wartości kroku, w takim przypadku przyjmuje się, że jest on równy 1. Zdarza się również, że w schematach blokowych wstawia się nieformalne instrukcje wyrażone w języku naturalnym. Można spotkać taki oto zapis if (plik=OTWARTY) then begin Zapisanie danych do pliku end else begin Komunikat o błędzie end Gdzie oczywiście zapisy ,,Zapisanie danych na dysk’’ i ,,Komunikat o błędzie’’ są tylko informacjami o tym co w tym miejscu ma wystąpić, a czego z jakiś przyczyn nie chcemy w danej chwili wyspecyfikowywać. Często są to po prostu odwołania do innych procedur, które są dobrze znane i opisane gdzie indziej.

5.4

Podstawowe algorytmy

Pokażemy teraz kilka algorytmów zapisanych bądź w pseudo-języku, bądź, gdzie to będzie zbyt skomplikowane, ograniczymy się jedynie do nieformalnego zarysowania ich idei. Algorytmy te reprezentują pewne charakterystyczne typy występujące w algorytmice. Z konieczności ograniczymy się do kilku podstawowych grup.

5.4.1

Algorytmy obliczeniowe

Jest to cała gama algorytmów służących do wyliczania pewnych wartości bądź rozwiązywania problemów numerycznych. Przykładami takich algorytmów mogą być NWD czy NWW, w tym miejscu pokażemy obliczanie pierwiastków trójmianu, przykład 5.1. W prezentowanym przykładzie,
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

144

Algorytmy i struktury danych

użyto procedury wypisz, co do której przyjmujemy intuicyjnie jej działanie polegające na wyświetleniu podanego w cudzysłowie napisu. Przykład 5.1. Algorytm obliczania rzeczywistych pierwiastków równania kwadratowego.

if (A=0) then begin wypisz("To nie jest równanie kwadratowe"); end else begin D:=B^2-4*A*C; if (D<0) then begin wypisz("Nie ma rzeczywistych rozwiązań"); end else begin if (D>0) then begin R1:=(-B-Sqrt(D))/(2*A); R2:=(-B+Sqrt(D))/(2*A); end else begin R1:=-B/(2*A); R2:=R1; end end wypisz("Pierwiastkami są: ", R1, R2); end

5.4.2

Algorytmy sortowania

Bardzo często czynnością wykonywaną przez każdego z nas, często w sposób nieświadomy, jest procedura sortowania. Mówiąc inaczej jest to algorytm
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

5.4 Podstawowe algorytmy

145

porządkowania, czy też układania pewnego zbioru elementów według zadanego klucza. Bardzo istotnym jest tu wskazanie klucza, który będzie wyróżnikiem na którym będzie dokonywana operacja porównania. Zauważmy, że jeśli mamy zbiór osób, i teraz ustawimy je rosnąco względem wzrostu, to nie jest to równoznaczne z porządkiem względem wagi, a już na pewno względem długości włosów. Jako przykład prostego algorytm sortowania opiszemy sortowanie przez wstawianie, zwane również sortowaniem przez proste wstawianie. Spójrzmy wpierw na rysunek 5.6, znajdują się na nim w linii zatytułowanej START liczby całkowite w losowej kolejności.

Rysunek 5.6: Ilustracja procesu sortowania przez wstawianie. Naszym zadaniem jest poukładanie tych ośmiu liczb rosnąco, w tym celu bierzemy pod uwagę wpierw element drugi. Dla tak wyróżnionego elementu sprawdzamy czy nie można go wstawić gdzieś przed nim, tj. w tym konkretnym przypadku czy nie jest on mniejszy od pierwszego elementu. Zauważamy, że nie jest, zatem pozostawiamy go na miejscu i przechodzimy do następnego — trzeciego elementu. Dla niego sprawdzamy wpierw czy nie możemy go wstawić na pierwsze miejsce, rzeczywiście możemy, gdybyśmy nie mogli to sprawdzilibyśmy jeszcze miejsce drugie. Całą procedurę powtarzamy kolejno dla pozostałych elementów. Spróbujmy zapisać opisany algorytm za pomocą pseudo-języka. Przy pierwszej próbie zapisania ten algorytm będzie miał on następującą postać: for i:=2 to n do begin
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

146

Algorytmy i struktury danych

x:=a[i]; "wstaw x w odpowiednie miejsce w ciągu a[1]...a[i-1]"; end Teraz sprecyzujemy pseudokod procesu wyszukiwania miejsca wstawienia elementu x, oraz uzupełnimy powyższy algorytm o ten fragment. W efekcie otrzymamy prostą implementację algorytmu sortowania przez wstawianie zaprezentowaną w przykładzie 5.2. Zauważmy jednocześnie, że algorytm ten działa niejako jak procedura układania książek na półce, wyjmujemy rozważaną książkę, następnie przesuwamy w prawo książki od niej większe, a następnie wsadzamy ją w miejsce zwolnione i dla niej przeznaczone. Teraz by można było tę książkę wyjąć należy mieć wolną rękę, która ją przytrzyma. Podobnie w algorytmie: musimy liczbę gdzie przechować by móc przesunąc na jej miejsce inne. Stąd w przykładzie 5.2, występuje przypisanie a[0]:=a[i], które właśnie zapamiętuje w pomocniczej komórce tablicy — nie wykorzystywanej do przechowywania elementów — element, który w danej chwili rozważamy. Przykład 5.2. Algorytm sortowania przez wstawianie.

for i:=2 to n do begin a[0]:=a[i]; j:=i-1; while (a[0] < a[j]) begin a[j+1]:=a[j]; j:=j-1; end a[j+1]:=a[0]; end

5.4.3

Algorytmy wyszukujące

Drugim często spotykanym zagadnieniem jest poszukiwanie informacji o zadanym kluczu. Czyli mamy na przykład zbiór osób, które posiadają jakąś
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

5.4 Podstawowe algorytmy

147

Rysunek 5.7: Ilustracja procesu przeszukiwania połówkowego.

cechę (klucz), niech to będzie wzrost. Następnie szukamy osoby o wzroście 178cm. Możemy spotkać się z dwoma przypadkami, pierwszy to taki gdy osoby te są w dowolnym porządku — czyli nie poukładane, oraz drugi, gdy mamy grupę osób ustawioną względem tego klucza — jak na wojskowej paradzie. Jeśli będziemy rozpatrywali pierwszy przypadek, to nie pozastaje nam nic innego jak brać po jednym z elementów i sprawdzać czy pasuje do naszego wzorca, taki sposób nosi też nazwę wyszukiwania liniowego. Jednak w drugim przypadku możemy poradzić sobie lepiej, to znaczy efektywnej jeśli zastosujemy wyszukiwanie połówkowe lub binarne. Wyjaśnimy ten sposób realizacji procedury szukania na przykładzie. Spójrzmy na rysunek 5.7, w pierwszym wierszu są przedstawione liczby całkowite posortowane od najmniejszej do największej. Będziemy poszukiwali wartości 15, jeśli spróbujemy jej szukać tak jak w zbiorze nieuporządkowanym i zaczniemy naszą procedurę od pierwszego elementu, to znajdziemy tę wartość w czwartym kroku. Możemy jednak poszukiwać inaczej, wpierw wybieramy element środkowy w tym ciągu20 . Porównujemy wartość tego elementu z wartością szukaną, jeśli jest to ta wartość to zwracamy numer tego elementu. W przeciwnym razie wiemy, że poszukiwany element jest bądź na prawo, bądź na lewo od środka21 . I dalej rozważamy tylko ten fragment ciągu, w którym potencjalnie znajduje się nasz element 22 . Cały proces powtarzamy dla okrojonego zbioru, jeśli znajdziemy element zwracamy jego pozycję, jeśli dojdziemy do zbioru pustego zwracamy informację, że nie znaleźliśmy tego elementu. Proces ten jest właśnie zilustrowany na rysunku 5.7.
Jeżeli jest parzysta ilość elementów, to umawiamy się że jest to jeden z elementów leżących przy środku. Ważne jest tylko by zawsze to robić konsekwentnie. 21 Zauważmy, że to jest miejsce w którym wykorzystujemy wiedzę o uporządkowaniu zbioru. Gdyby te elementy były ułożone przypadkowo nie można by było wysunąć takiego wniosku. 22 Potencjalnie, gdyż zaczynając poszukiwania nie wiemy czy on w ogóle jest w tym zbiorze.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004 20

148

Algorytmy i struktury danych

Zauważmy, że element poszukiwany 15 został znaleziony w trzech krokach, zatem o jeden krok szybciej niż w przypadku przeszukiwania liniowego. Możemy oczywiście wzruszyć ramionami i stwierdzić, że to niewielka oszczędność czasu. Jednak zauważmy, że poszukiwanie jakiegokolwiek elementu w tym zbiorze będzie zajmowało co najwyżej cztery kroki w przypadku wyszukiwana połówkowego, zaś aż 9 w przypadku wyszukiwania liniowego. Oczywiście gdy mamy pecha i poszukujemy akurat elementu występującego na końcu. Ale często to włąśnie te najgorsze przypadki bierze się pod uwagę przy analizowani sprawności (wydajności) algorytmów. Drugą taką miarą jaką się rozważa jest średnia ilość kroków, jaką trzeba wykonać w przypadku jednego bądź drugiego algorytmu.

5.5

Rekurencja vs. iteracja

W tym punkcie zasygnalizujemy dwa podstawowe podejścia do realizacji prostych algorytmów: podejście iteracyjne i podejście rekurencyjne. Zacznijmy od wyjaśnienia pojęcia rekurencji. Obiekt nazwiemy rekurencyjnym, jeśli częściowo składa się z siebie samego lub jego definicja odwołuje się do niego samego. Innym słowem, które jest odpowiednikiem rekurencji jest rekursja. Z rekurencją mamy do czynienie również w życiu codziennym — wystarczy ustawić na przeciw siebie dwa lusterka i zobaczyć jaki otrzymamy obraz. Powinniśmy zobaczyć lusterko, a w nim lusterko, a w nim lusterko, a w nim . . . Siła rekursji wyraża się w możliwości definiowania nieskończonego zbioru obiektów za pomocą skończonego wyrażenia. Dokładnie na tej samej zasadzie, nieskończoną liczbę obliczeń można zapisać za pomocą skończonego programu rekurencyjnego. Narzędziem umożliwiającym tworzenie programów rekurencyjnych jest funkcja (można też mówić o procedurze, czy podprogramie). Funkcją nazwiemy zbiór instrukcji, który posiada nazwę, wtedy nazwa ta stanowi jednocześnie nazwą tej funkcji. Do funkcji można przekazać dane poprzez jej argumenty, można też żądać od niej zwrotu policzonych wartości23 . Jeśli jakaś funkcja zawiera odwołanie do siebie samej to nazwiemy ją funkcją rekurencyjną. Podobnie jak pętle, które pozwalają nam iterować (czyli powtarzać wielokrotnie zestaw instrukcji), funkcje rekurencyjne dopuszczają możliwość wykonywania powtórzeń z tym wszakże wyjątkiem, że nieskończonej ich
23

To co przyjmuje funkcja i co zwraca określa jej twórca.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

5.5 Rekurencja vs. iteracja

149

liczby24 . Wiąże się z tym konieczność rozwiązania problemu stopu. Zasadniczym wymaganiem w takiej sytuacji jest uzależnienie rekurencyjnego wywołania funkcji od warunku, który w pewnym momencie przestaje być spełniony co w konsekwencji powoduje zatrzymanie procesu rekurencji. W celu wykazania, że proces wywołań rekurencyjnych się skończy, należy pokazać, że osiągnięty będzie warunek stopu. Algorytmy rekurencyjne są szczególnie odpowiednie wtedy, gdy rozważany problem lub przetwarzane dane są zdefiniowane w sposób rekurencyjny. Dla przykładu rozważmy dwa sposoby realizacji funkcji obliczającej silnię. Pierwszy z nich, pokazany w przykładzie 5.3 realizuje silnię za pomocą pętli, zatem w sposób iteracyjny. Przy czym przyjmijmy na potrzeby tego rozdziału, że zapis silnia i := s będzie oznaczał, że funkcja silnia i będzie zwracała wartość s — wyliczoną wartość silni. Przykład 5.3. Funkcja obliczająca silnię — podejście iteracyjne.

silnia_i(n) begin i:=0; s:=1; while (i<n) do begin i:=i+1; s:=s*i end silnia_i := s; end

Drugi sposób realizacji silni jest pokazany w przykładzie 5.4 i jest to podejście rekurencyjne. Przykład 5.4. Funkcja obliczająca silnię — podejście rekurencyjne.

Oczywiście ta nieskończona ilość operacji jest wyłącznie teoretyczna, w praktyce przecież realizacja wykonania funkcji odbywa się na komputerze, który nie jest wieczny.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

24

150

Algorytmy i struktury danych

silnia_r(n) begin if (n=0) then w := 1; else w := n*silnia_r(n-1); silnia_r := w; end

Przykład 5.5. Przebieg wywołań rekurencyjnych przy obliczaniu wartości 5!. s_r(5) . | . 5*s_r(4) . . | . . 4*s_r(3) . . . | . . . 3*s_r(2) . . . . | . . . . 2*s_r(1) . . . . . | . . . . . 1*s_r(0) . . . . . . | . . . . . . <---1 . . . . . . | . . . . . <---1*1 . . . . . | . . . . <---2*1 . . . . | . . . <---3*2 . . . | . . <---4*6 . . | <---5*24 | 120
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

5.5 Rekurencja vs. iteracja

151

Jednak nie zawsze funkcje, która jest dana wzorem rekurencyjnym powinno się realizować takim algorytmem. W istocie istnieją bardziej skomplikowane schematy rekurencyjne, które mogą i powinny być przekształcane do postaci iteracyjnych. Dobry przykład stanowi tutaj zagadnienie obliczania liczb ciągu Fibonacciego. Ciąg Fibonacciego, dla n > 1, zdefiniowany jest następująco f ibn+1 := f ibn + f ibn−1 natomiast wyrazy 1 i 0 przyjmują wartość 1. Rekurencyjna implementacja funkcji obliczającej liczby ciągu Fibonacciego jest zaprezentowana w przykładzie 5.6. Przykład 5.6. Funkcja obliczająca n-tą liczbę ciągu Fibonacciego — podejście rekurencyjne.

fib_r (n) begin if (n=1) then w := 1; if (n=0) then w := 1; w := fib_r(n-1)+fib_r(n-2); fib_r := w; end

Podejście rekurencyjne nie zdaje egzaminu w tym przypadku; każde wywołanie powoduje dwa dalsze wywołania, tj. całkowita liczba wywołań rośnie wykładniczo (patrz rysunek 5.8). Co szybko prowadzi do wyczerpania stosu i jednocześnie działa wolno, gdyż funkcja dla tych samych danych jest wyliczana kilkukrotnie25 . W tym przypadku lepiej użyć funkcji iteracyjnej, jak to zaprezentowano w przykładzie 5.7.
25

Nawet kilkadziesiąt, bądź kilkaset razy.

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

152

Algorytmy i struktury danych

fib(5) fib(4) fib(3) fib(2) fib(3) fib(2) fib(1)

fib(2)

fib(1)

Rysunek 5.8: Drzewo wywołań dla rekurencyjnego obliczania 5-ego wyrazu ciągu Fibonacciego.

Przykład 5.7. Funkcja obliczająca n-tą liczbę ciągu Fibonacciego — podejście iteracyjne.

fib_i (n) begin i:=1; x:=1; y:=1; while (i<n) begin z:=x; i:=i+1; x:=x+y; y:=z; end fib_i := x; end

Celem lepszego uzmysłowienia różnic powyższych funkcji dokonajmy porównania czasu wykonania programów powyższych programów. Poniższa
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

5.6 Analiza złożoności

153

tabela zawiera czasy dla algorytmu rekurencyjnego. Wersja iteracyjna oblicza 1000-ny wyraz w czasie nie mierzalnym przy wykorzystaniu stopera. Test przeprowadzono na komputerze 486 DX 4 120 MHz, w środowisku MS-DOS26 . wyraz 30 31 32 33 34 35 36 37 38 39 40 czas (s) 2 2.56 3.54 5.10 7.11 12.67 18.84 29.40 48.12 75.82 >90.0

5.6

Analiza złożoności

Często ten sam problem można rozwiązać za pomocą różnych algorytmów, co było pokazane choćby w punkcie 5.5. W takim razie należy się zastanowić jakie są różnice pomiędzy tymi algorytmami, oraz czy różnice te mają wpływ na wybór konkretnego algorytmu do implementacji. Przy porównywaniu algorytmów zwykle bierze się pod uwagę ich efektywność (szybkość działania), zapotrzebowanie na zasoby pamięciowe systemu, wreszcie ich czytelność. Tak naprawdę czytelność jest rzeczą względną i zależy od długości praktyki osoby czytającej, stąd też rzadko jest brana pod uwagę przy porównywaniach. Istotne różnice, z punktu widzenia praktyki, to prędkość działania algorytmu i jego zapotrzebowanie na pamięć. Przy czym od razu należy zaznaczyć, że różnice między algorytmami są najczęściej bez znaczenia przy przetwarzaniu małej ilości danych, ale rosną razem z nią, i to liniowo, logarytmicznie, z kwadratem, wykładniczo itd. W celu porównywania algorytmów pod względem ich prędkości działania, wprowadzono pojęcie złożoności obliczeniowej. Dysponując takim pojęciem, posiadamy pewną miarę, która pozwala na porównywanie różnych algorytmów, ocenę efektu poprawienia istniejącego algorytmu, czy wreszcie możemy szacować czas realizacji danego zadania.
Test przeprowadzono specjalnie na starym komputerze, by widać było wyraźniej różnice.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004 26

154

Algorytmy i struktury danych

Złożoność obliczeniowa określa, ilu zasobów wymaga użycie danego algorytmu lub jak jest ono kosztowne. Koszt ten można oczywiście mierzyć na wiele różnych sposobów, zależnych od postawionego zadania. Stąd czas wyrażany jest za pomocą umownych jednostek. Rozważmy następujący przykład, mamy funkcje zmiennej całkowitej n daną następującym wzorem: f (n) = n2 + 100n + log 10 n + 1000 Dla małych wartości n ostatni składnik jest największy. Jednak wraz ze wzrostem n maleje jego znaczenie na rzecz pozostałych. Ostatecznie, gdy n przekroczy wartość 100000 jego znaczenie w ogólnym wyniku jest marginalne. Również pozostałe czynnik, oprócz n 2 , mają coraz mniejszy wkład. To właśnie głównie od pierwszego składnika zależy wartość funkcji i dla dużych n można przyjąć, że funkcja ta jest postaci f (n) = cn2 gdzie c jest pewną stałą. W wyniku takiej obserwacji jak powyższa wprowadzono pewne notacje mające na celu określenie złożoności oblicznieowej danego algorytmu. Notacji tych jest kilka, my przedstawimy chyba najprostsza z nich — notację wielkie O. Dla danej pary funkcji f i g o dodatnich wartościach rozważymy następujące definicje. Definicja 5.1. Notacja „wielkie O” Powiemy, że funkcja f (n) jest rzędu O(g(n)), jeśli istnieją dodatnie liczby c i N takie, że 0 ≤ f (n) ≤ cg(n) dla wszystkich n ≥ N . Na przykład wyrażenie n2 + 100n + log10 n + 1000 można zapisać jako n2 + 100n + O(log10 n) co oznacza, że część „obcięta” jest zawsze mniejsza niż pewna ustalona stała pomnożona przez log10 n. Własności notacji „wielkie O”. Własność 1 Jeśli f (n) jest O(g(n)) i g(n) jest O(h(n)), to f (n) jest O(h(n)). Inaczej: O(O(g(n))) jest O(g(n)). Własność 2 Jeśli f(n) jest O(h(n)) i g(n) jest O(h(n)), to f(n)+g(n) jest O(h(n)).
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

5.7 Zadania

155

5.7

Zadania

1. Napisz funkcję do obliczania xy , gdzie x, y są liczbami naturalnymi, przy czym dopuszczamy, że y może być 0. Napisz wersję iteracyjną i rekurencyjną. 2. Dla iteracyjne wersji funkcji określonej w poprzednim zadaniu narysuj schemat blokowy. 3. Mając poniższy schemat blokowy zamień go na zapis w pseudo-języku programowania, zastanów się co robi ten algorytm.

Rysunek 5.9: Schemat blokowy pewnego algorytmu.

4. Napisz funkcję rekurencyjną, która realizuje algorytm wyszukiwania połówkowego opisany w punkcie 5.4.3.

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

156

Algorytmy i struktury danych

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

Rozdział 6

Języki programowania
Jedne języki zaprojektowano po to, aby rozwiązać problem, inne, aby wykazać słuszność teorii. Dennis M. Ritchie Jeden język nie musi być koniecznie lepszy od drugiego tylko z tego powodu, że ma cechę, której ten drugi nie posiada. Bjarne Stroustrup

6.1

Czym jest język programowania?

Tytułowe pytanie może być postawione przez każdą osobę, która po raz pierwszy spotyka się z pojęciem języka programowania. Istnieje na nie bardzo prosta odpowiedź: język programowania jest takim sposobem zapisu algorytmu, by komputer mógł go wykonać. W literaturze [Mac99] spotkamy podobną definicję Język programowania jest językiem, który jest przeznaczony do wyrażania programów komputerowych oraz taki, że można w nim wyrazić dowolny program komputerowy. Jeśli jednak zastanowić się szerzej nad tym pojęciem, to można by postawić pytanie: „Czy nie wystarczyłby język polski, czy też angielski? A jeśli tak to po co tworzymy nowy język?”. Odpowiedź na tak postawione pytanie jest w chwili obecnej zdecydowanie negatywna. Komputery dopiero uczą się rozpoznawać polecenia wydawane przez człowieka, przy czym polecenia te są wydawane za pomocą małego podzbioru słów języka naturalnego.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

158

Języki programowania

Od języka programowania wymaga się by był na tyle prosty aby komputer mógł łatwo go „zrozumieć” i na tyle elastyczny, by pozwalał wyrazić (zapisać) za jego pomocą dowolny algorytm. Zatem można zadać pytanie, czemu istnieje tyle języków programowania, czy nie wystarczyłby jeden? Okazuje się, że w zależności od tego co chcemy opisać, jakie zjawiska z życia codziennego zamodelować w języku maszyny, potrzebujemy do tego innych słów czy też innych konstrukcji językowych. Różnorodność języków programowania prowadzi do tego, że w jednych jest łatwiej pisać bazy danych a w innych obliczenia numeryczne. Istnieją też takie języki, które w ogóle nie wspierają pewnych operacji i nie da się za ich pomocą zrealizować części zadań. Możemy zatem zadać kolejne pytanie, czy nie można stworzyć jednego uniwersalnego języka programowania, w którym łatwo można by pisać dowolne programy, czy to obliczeniowe, czy też bazy danych. Niestety jak w większości dziedzin życia, okazuje się że wszystko co uniwersalne przestaje być optymalne i odwrotnie. A mnogość problemów dla których mają być pisane programy wymusza często tworzenie specjalistycznych języków. Język uniwersalny zawierałby tak dużo słów i styli pisania, że nikt nie umiał by się nim posługiwać. Możemy wyobrazić sobie, że konstruujemy język naturalny w którego skład wchodzą wszystkie słowa języków nowożytnych, czy istniałby ktoś kto nauczy się tego języka? Ponadto za tworzeniem nowych języków programowania przemawia fakt pojawiających się coraz to wydajniejszych komputerów, a co za tym idzie możliwości wymyślania coraz nowszych narzędzi dla programistów. Języki programowania zostały skonstruowane by stać się pomostem pomiędzy człowiekiem a komputerem. Pomostem pomiędzy myśleniem abstrakcyjnym a chłodną precyzją maszyny. Spróbujmy zastanowić się nad zdaniami wypowiadanymi przez człowieka codziennie. Spostrzeżemy, że wiele z tych zdań jest nie precyzyjnych, a mimo to drugi człowiek nie ma kłopotu z ich zrozumieniem. Weźmy dla przykładu zdanie: „Proszę idź do sklepu i kup litr mleka”. W zdaniu tym ani nie podajemy lokalizacji sklepu, ani też nie podajemy rodzaju mleka czy sposobu jego zapakowania. Dlaczego tak się dzieje? Osoba z nami mieszkająca z reguły wie jakie mleko najczęściej spożywamy, również sklep wybierze dobrze sobie znany lub też najbliższy. Zatem prośba zostanie spełniona. W przypadku komputerów musimy formułować swoje polecenia bardzo precyzyjnie, gdyż można powiedzieć, że komputer nie ma własnych przyzwyczajeń, czy też intuicji i niekoniecznie musi się domyślać co też wydający polecenie chciał powiedzieć. Zauważmy, że jeśli tylko trochę zmienimy powyższe zdanie: „Skocz do sklepu i kup
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

6.2 Ewolucja języków programowania

159

mleko”, stanie się ono dodatkowo niezrozumiałe poprzez dwuznaczność jaką zawiera w sobie zwrot „skocz” (czy komputer umie podskakiwać?). Nasze krótkie rozważania prowadzą do dwu pojęć ściśle związanych z językami programowania: składni i semantyki.

6.1.1

Składnia i semantyka

Składnia jest to zbiór elementów języka z których buduje się zdania oraz reguł gramatycznych według których te zdania się konstruuje, uwzględniając w tym „interpunkcję”. Szczególnie ten ostatni element jest często frustrujące dla początkujących programistów. Pytają: „A co to za różnica czy stawiam tu kropkę czy nie?” Możemy jedynie zauważyć, że ma to znaczenie również w języku codziennym. Wyobraźmy sobie dialog dwojga osób, z których pierwsza pyta: „Ty mnie chyba nie lubisz?”, a druga odpowiada: „Nie lubię” lub „Nie, lubię”. Fakt wystąpienia bądź pominięcia przecinka zmienia zupełnie znaczenie zdania. Większość języków programowanie posiada bardzo sztywne reguły składniowe, co może być na początku przyczyną irytacji (bo wciąż czegoś ten komputer od nas chce). Jednak w końcowym efekcie okazuje się dla nas korzystne, bo wykryte zostają przypadkowe błędy. Semantyka odpowiada regułom znaczeniowym, czyli nadającym znaczenie zdaniu. Mówiąc inaczej mamy zdanie napisane poprawnie gramatycznie, ale jego interpretacja jest inna niż chcieliśmy, ponieważ popełniliśmy błąd semantyczny. Cała składnia byłaby bezwartościowa gdybyśmy nie nadali znaczenia słowom, które ona ze sobą wiąże. Błędy semantyczne wynikają z niewłaściwego zrozumienia przez nas znaczenia bądź konkretnych słów, bądź całej konstrukcji zdania, a być może po prostu przejęzyczenia. Powiemy do kolegi „Podaj mi książkę”, jednak się przejęzyczyliśmy ponieważ chodziło nam o zeszyt. Jak widać nie mamy żadnych szans na automatyczne wykrycie takiego rodzaju błędów. Objawiają się one jedynie w niespodziewanych efektach działania programu. W językach programowania dąży się do formalnej i bardzo precyzyjnej semantyki, nie może być nie jednoznaczności jak w zdaniu „Skocz do sklepu”.

6.2

Ewolucja języków programowania

Podajemy tu krótki rys historyczny języków programowania, który ułatwi zrozumienie ich kolejnych generacji. Przypomnijmy to co było powiedziane we wstępie, że pierwszym programistą była Ada Augusta hrabina Lovelace,
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

160

Języki programowania

która napisała całkiem pokaźny zbiór programów na nie istniejącą maszynę analityczną. Potem języki programowania musiały czekać na swoje ponowne narodziny aż do połowy lat pięćdziesiątych ubiegłego wieku, kiedy to powstawały pierwsze komputery elektronowe i należało zacząć je oprogramowywać. Spójrzmy na przykład 6.1, który zawiera fragment programu dla komputera IBM 650 (przykład zaczerpnięto z [Mac99]). Przykład 6.1. Kawałek programu dla komputera IBM 650

LOC 1107 1061 1019 1026 1033 1041 1048 1105 1063 1067 8003

OP 46 30 20 60 30 20 60 30 44 10 69

DATA 1112 0003 1023 8003 0003 1045 8003 0003 1067 1020 8002

INST 1061 1019 1026 1033 1041 1048 1105 1063 1076 8003 1061

COMMENTS A może użyjemy pętlę? Zapamiętaj C

Zapamiętaj B

Czy tu wołamy operację 02? Idź do podprogramu 01

Kolumna LOC zawiera numer adresu pod którym znajduje się instrukcja, OP zawiera kod operacji, DATA dane, INST adres instrukcji, która ma być wykonana jako następna. Cóż . . . należy przyznać, że czytelność powyższego programu jest przytłaczająca . . . Na szczęście w tym samym czasie w firmie IBM Johna Backusa prowadził badania nad stworzeniem języka wygodniejszego, czytelniejszego z przeznaczeniem do obliczeń matematycznych 1 . I tak w listopadzie 1954 roku została ogłoszona pierwsza specyfikacja języka FORTRAN, nazwa jest akronimem słów the IBM Mathematical FORmula TRANslating system. Spójrzmy na przykładowy program 6.2 zapisany w tym języku, a dokładnie w jego pierwszej wersji.
W tamtych czasach właściwie można było mówić o dwu typowych wykorzystaniach komputerów: obliczeniach i przetwarzaniu danych. Gry i rozrywka musiały poczekać jeszcze wiele lat na swoją kolej.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004 1

6.2 Ewolucja języków programowania

161

Przykład 6.2. Program w języku FORTRAN I.

10

30 25 20

40

50

DIMENSION DTA(900) SUM = 0.0 READ 10, N FORMAT(I3) DO 20 I = 1, N READ 30, DTA(I) FORMAT(F10.6) IF (DTA(I)) 25, 20, 20 DTA(I) = -DTA(I) CONTINUE DO 40 I = 1, N SUM = SUM + DTA(I) CONTINUE AVG = SUM/FLOAT(N) PRINT 50, AVG FORMAT(1H, F10.6) STOP

Bez zbędnego wgłębiania się w znaczenie kolejnych linii a posiadając choć minimalne doświadczenie w jakimkolwiek języku programowania, można zauważyć, że program ten jest dużo czytelniejszy od poprzedniego. Język ten okazał się na tyle dobry, że przechodził kolejne udoskonalenia. W 1962 komitet ANSI zaczął pracę nad jego standaryzacją i w 1966 roku gotowa jest specyfikacja języka FORTRAN IV. Popularność tego języka wciąż rośnie, coraz więcej firm tworzy własne implementacje. I tak w 1977 roku pojawia się kolejny chyba najpopularniejszy standard nazwany FORTRAN 77. Faktycznie język ten jest rozwijany do dziś i istnieje obecnie standard FORTRAN 90, ale nie tak popularny jak jego poprzednik. Szybko się przekonano, że i ten język nie był wystarczający, głównie dlatego, że był to język przeznaczony praktycznie wyłącznie do obliczeń numerycznych i tylko w takich zastosowaniach się sprawdzał. Co więcej, czytelność zapisanych w nim programów wciąż pozostawiała wiele do życzenia. W latach sześćdziesiątych pewnym antidotum na brak czytelności stał się język Algol-60. Wprowadzono w nim pętlę for oraz możliwość dzielenia
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

162

Języki programowania

programu na podprocedury, które można było nazwać i odwoływać się do nich przez tę nazwę. Co prawda FORTRAN również zawierał podprogramy (procedury), ale wtedy w Algolu-60 było to znacznie bardziej uporządkowane. Przykładowy program w języku Algol-60 prezentuje listing 6.3. Przykład 6.3. Program w języku Algol-60

begin real x, y; integer N, i; real procedure cosh(x); real x; begin cosh := (exp(x)+exp(-x))/2; end begin Read Int (N); for i := 1 step 1 until N do begin x := i/N; y := cosh(x); Print Real (y); end; end end

Ale i ten język nie przyjął się na długo na rynku programistycznym, można jedynie odnotować fakt, że powstał jeszcze jeden jego standard Algol68. Niemniej jednak jest to bardzo ważny język, gdyż był on wzorem dla wielu innych takich jak PL/I, Pascal, CPL czy Simula 67. W 1968 roku Niklaus Wirth rozpoczął pracę nad nowym językiem pod nazwą Pascal. Ponieważ wcześniej Wirth uczestniczył w pracach nad Algolem, więc świetnie znał zalety i wady tegoż języka. W opracowaniu Pascala skorzystał z przejrzystej i czytelnej składni Algolu, jednak zwrócił uwagę, że struktury danych w tym języku wystarczały wyłącznie do działań arytmetycznych zatem postanowił obejść tę niedogodność wprowadzając bardziej
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

6.2 Ewolucja języków programowania

163

elastyczne typy. Dodał do języka typy wyliczeniowe, dzięki czemu były możliwe deklaracje zmiennych jak poniżej type dzien_tyg = (pon, wt, sro, czw, pia, sob, nie ); Zapis powyższy oznacza definicję typu wyliczeniowego o nazwie dzien tyg, który to typ może przyjąć wartości jedynie spośród zadeklarowanych nazw po prawej stronie. Wprowadzenie typów wyliczeniowych wpłynęło na ograniczeni pewnej klasy błędów popełnianych przez programistów. Zauważmy, że gdyby typ dzien tyg był np. liczbą całkowitą to można zmiennej tego typu przypisać by wartość 10, która nie odpowiadałaby żadnemu z dni. Następnie Wirth wzbogacił język o możliwość wprowadzenie ograniczeń na typy wbudowane, czyli konstruowania tzw. podtypów, poniższy przykład ilustruje takie ograniczenie var dzien_miesiaca: 1 .. 31; I znów mamy ilustrację pewnego wsparcia dla porządnego pisania programów i zapobiegania podobnym błędom jak poprzednio. Do zmiennej dzien miesiaca nie przypiszemy wartości mniejszych od 1 i większych od 31. Dalej język Pascal zawierał możliwość definiowania typu zbiorowego i , co więcej, sprawdzania czy coś należy do pewnego zbioru czy nie. Wszystkie te cechy spowodowały, że Pascal stał się językiem niezwykle przejrzystym i czytelnym co spowodowało, że szybko przyjął się jako język dydaktyczny. Z resztą sam Wirth pisząc go robił to z myślą właśnie o aspekcie dydaktycznym. Ze względu na swoje cechy w tym na łatwość jego nauki stał on się również bardzo chętnie wykorzystywanym językiem przez świerzo upieczonych programistów. Firma Borland widząc duże zainteresowanie wypuściła na rynek bardzo dobre środowiska programistyczne w oparciu o Pascala, wpierw Turbo Pascal (zawierał wiele rozszerzeń w stosunku do oryginału), a potem nawet środowiska typu RAD (ang. Rapid Application Development) pod nazwą Delphi2 . Przykładowy program w języku Pascal ilustruje wydruk 6.4. Przykład 6.4. Program w języku Pascal

2

Ale to już były lata dziewięćdziesiąte ubiegłego wieku.

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

164

Języki programowania

program prg1 (input, output); var x, y: real; N, i: integer; function cosh( x: real): real; begin cosh := (exp(x)+exp(-x))/2; end; begin readln(N); for i := 1 to N do begin x := i/N; y := cosh(x); writeln(y); end; end.

Wróćmy do lat siedemdziesiątych ubiegłego wieku. W informatyce nastąpił kilka lat wcześniej „kryzys oprogramowania”. W największym skrócie można ten okres określić jako moment w którym zrozumiano, że ówczesne techniki programistyczne i metodologie tworzenia programów nie wystarczają do tworzenia dużych i złożonych systemów przy jednoczesnym zachowaniu niezawodności, jakości oraz akceptowalnego czasu realizacji tych projektów. Z jednej strony spowodowało to narodziny nowej dziedziny informatyki — inżynierii oprogramowania, której celem było badanie problematyki tworzenia oprogramowani i konstruowanie nowych metod postępowania. A z drugiej strony zrodziły się pomysły na ulepszenie języków programowania. Kolejną ewolucję języków prześledzimy na przykładzie powstania języka ADA. W 1971 i 1972 roku D. L. Parnas zaproponował kilka reguł określających w jaki sposób dzielić program na kilka mniejszych modułów. Jedną z tych reguł było umieszczanie w jednym module informacji i funkcji, które służą pewnej określonej i zamkniętej pojęciowo cesze. Dzięki temu, jeśli w przyszłości trzeba było poprawić coś w działaniu tego modułu, czy nawet
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

6.2 Ewolucja języków programowania

165

w całości go przebudować nie wpływało to (a raczej wpływać nie powinno) na resztę systemu. Cecha ta nosi miano ukrywania informacji i jest doskonale znana dzisiejszym programistom piszącym w językach obiektowych. W tym samym okresie, dokładniej w połowie 1970 roku Departament Obrony USA określa potrzebę stworzenia (bądź wybrania istniejącego) języka programowania dla systemów osadzonych (ang. embedded systems), oraz krytycznych aplikacji do celów wojskowych. Chodziło o wybranie takiego języka, który umożliwiał by pisanie oprogramowania dla systemów obronnych, czy systemów sterowania bronią. Oczywistą sprawą jest, że takie systemy powinny być niezawodne, a co za tym idzie narzędzia w których będzie pisane oprogramowanie muszą wspierać tę niezawodność. Kolejnym czynnikiem skłaniającym do wyboru jednego języka był fakt, że w tamtych czasach istniało około 400 różnych języków i ich dialektów służących. Zatem koszty przenoszenia i wspierania takiej różnorodności narzędzi były ogromne. W 1975 roku powołano grupę Higher-Order Language Working Group (HOLWG), której zadaniem było stworzenie jednego języka do omawianych celów. Co więcej w 1976 roku oszacowano, że stworzenie takiego języka pozwoli zaoszczędzić 12-17 miliardów dolarów w przeciągu następnych 17 lat. Język o którym mowa powinien posiadać kilka kluczowych cech, głównymi z nich była prostota (ang. simplicity) oraz czytelność (ang. readability). Poza tymi ogólnikami zakładono, że język ten będzie wspierał ukrywanie informacji, programowanie współbieżne, oraz posiadał wsparcie do weryfikacji pisanych w nim programów. W 1977 roku HOLWG przeanalizowała 26 języków programowania i sformułowała wnioski, że żaden z nich nie odpowiada postawionym wymaganiom. Następnie spośród tych języków metodą eliminacji starano wybrać się język najbardziej zbliżony do ideału. Zwycięzcą okazał się język opracowany przez grupę CII-Honeywell-Bull, której przewodniczył Jean Ichbiah. W 1979 roku HOLWG nadała temu językowi nazwę ADA. Język ten stał się standardem do celów wojskowych w styczniu 1983 roku, zaś w czerwcu 1984 został wybrany jako obowiązkowy i jedyny do wszystkich krytycznych zastosowań3 . Celem tego języka, poza samymi mechanizmami dla programistów, było obniżenie kosztów tworzenie aplikacji poprzez zwiększenie przenoszalności oraz wsparcia dla powtórnego użycia komponentów (ang. reuse). Stąd też zabroniono budowania wszelkich pod lub nadzbiorów języka, czy własnych klonów, miał być jeden standard bez odstępstw. Niewątpliwie ten krok, choć może wydawać się bardzo drastyczny spowodował dużą stabilność
3

Jako standard ISO został zatwierdzony w 1987 roku.

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

166

Języki programowania

środowiska pracy. Zauważmy, że przy omawianu Pascala nadmieniliśmy, że firma Borland stworzyła własny dialekt, który był nieprzenośny. W języku ADA można wyróżnić cztery podstawowe konstrukcje językowe: deklaracje, wyrażenia, instrukcje i typy. Wyrażenia i instrukcje są praktycznie odpowiednikami tego co było znane wcześniej np. w Pascalu. Typy również są podobne do znanych wcześniej, z tą różnicą, że programista ma większą swobodę w definicji własnych typów. Największa zmiana jaką wprowadzono w ADA znajduje się w deklaracjach, i tu wyróżnia się pięć ich typów: obiekty, typy, podprogramy, pakiety, oraz zadania. Deklaracje obiektów pozwalały na określanie funkcji i zmiennych 4 . Podprogramy przypominały deklarację funkcji i procedur, jednak dodatkowo można było deklarować operatory np. + czy -. Największą innowacją było prowadzenie pakietów i zadań. Obie te konstrukcje pozwalały na definicję modułów, czyli zamkniętych funkcjonalnie zbiorów. Zadanie posiadało jednak dodatkową własność, a mianowicie można je było wykonywać w sposób równoległy bądź rozproszony i istniały w języku jako takim mechanizmy wspierające takie programowanie 5 . Na sam koniec poniższy przykład prezentuje pakiet napisany w języku ADA, zawierający procedurę wyszukiwania binarnego w tablicy liczb zmiennoprzecinkowych. Przykład 6.5. Fragment programu (pakiet) w języku Ada

package Table is type Table is array (Integer range < >) of Float; procedure BinarySearch(T: Table, X: Float, Position: out Integer, Found: out Boolean) is subtype Index is Integer range T’First..T’Last; Lower : Index := T’First; Upper : Index := T’Last; Middle : Index := (Lower + Upper) / 2; begin
Zwróćmy uwagę, że nie ma to nic wspólnego z obiektowością Warto zwrócić uwagę, że to język dostarczał takie mechanizmy, np. w C czy C++ można pisać i w sposób współbieżny i rozproszony, jednak w przypadku tych języków programista musi sam zadbać o wiele rzeczy (ADA dbała o nie niejako za programistę).
5 c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004 4

6.2 Ewolucja języków programowania

167

loop if T(Middle) = X then Location := Middle; Found := True; return; elsif Upper < Lower then Found := False; return; elsif T(Middle) > X then Upper := Middle - 1; else Lower := Middle + 1; end if; Middle := (Lower + Upper) / 2; end loop; end BinarySearch; end Tables;

Jednak nie tylko ADA powstała jako język wspierający pracę nad złożonymi projektami — inną grupą języków były języki zorientowane obiektowo (ang. Object-Oriented Language (OOL)). Jako przyczynę do ich powstania można wskazać, podobnie jak przy programowaniu strukturalnym, kolejny wzrost wielkości tworzonych programów. Przede wszystkim skończyła się era dużych programów pisanych przez jedną osobę. Skoro nad programem pracowało kilka a nawet kilkanaście osób należało dostarczyć im narzędzi wspierających pracę grupową i języków wspomagających pisanie złożonych systemów. Z drugiej strony, coraz więcej powstających systemów, odzwierciedlało bardzo złożone procesy bądź zjawiska ze świata rzeczywistego. Zaistniała potrzeba odzwierciedlanie rzeczywistych obiektów jak statki czy samoloty w modelu języka programowania. Języki zorientowane obiektowo — czyli wspierające pisanie w zgodzie z metodologią obiektową — musiały posiadać kilka cech, które teraz omówimy. Powinny umożliwiać definicję nowych typów. Ten nowym typ definiowano za pomocą klasy, której konkretne instancje nazywano obiektami — stąd nazwa języki obiektowe. Co więcej należało zapewnić by te nowe typy (klasy) były traktowane jak typy wbudowane. Dzięki temu programista definiując typ (klasę) reprezentujący macierz mógł dla niej zdefiniować operację
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

168

Języki programowania

np. dodawania i operacja ta w sensie zapisu wyglądała jak dodawanie dwu zmiennych całkowitych. W rzeczywistości typ w językach obiektowych był czymś więcej niż typem w językach strukturalnych 6 . W nowym znaczeniu typ był zbiorem obiektów i operacji, których wykonanie na nim było zawsze poprawne. Dla podkreślenie pewnej odmienności pojęcia typu w językach obiektowych wprowadzona pojęcie klasy i tak zaczęto nazywać te nowe typy. Spójrzmy na poniższy fragment programu (zapisany w języku C++): // definicja dwu zmiennych typu całkowitego // jest to typ wbudowany w język int x = 1, y = 3; // zsumowanie dwu zmiennych i zapisanie wyniku w trzeciej int z = x + y; // Definicja zmiennych reprezentujacych macierze // a następnie ich zsumowanie Matrix a, b; c = a + b; Dzięki definicji typów była możliwą ścisła kontrola typów, to znaczy, że do zmiennej reprezentującej liczbę całkowitą nie można było przypisać samochodu. Jednak nie wszystkie języki wspierają ten mechanizm, mimo jego niepodważalnej siły. Przykładem języków o ścisłej typizacji — ścisłym sprawdzaniu poprawności typów — jest C++. Klasy miały przede wszystkim umożliwiać oddzielania interfejsu od implementacji. Interfejsem nazwiemy wszystko to co jest publiczne, czyli dające się wykorzystać przez użytkowników klasy. Implementacja jest sposobem realizacji funkcji klasy, jej jakby wnętrzem, które nie jest widzialne spoza niej samej a przez to nie jest narażone na przypadkową zmianę. Jako przykład rozważmy samochów, pojazd ten posiada kierownicę, pedały i drążek zmiany biegów7 . Wszystkie te elementy są składnikami jego interfejsu przez który kierujący jest w stanie sterować pojazdem. Jednak samochód posiada również silnik, skrzynie biegów i wiele innych, jednak większość użytkowników nie musi do nich zaglądać, a już z pewnością nie jest to zalecane w czasie jazdy.
6 7

W Pascalu również programista mógł zdefiniować własny typ danych. Oczywiście posiada znacznie więcej elementów, ale tyle wystarcza do tego przykładu.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

6.2 Ewolucja języków programowania

169

Następnie języki obiektowe powinny wspierać mechanizm dziedziczenia. Mechanizm ten zapewnia możliwość definiowania nowej klasy na podstawie istniejącej. W takiej sytuacji nowa klasa dziedziczy wszystko ze swojego przodka8 . Jest to bardzo silna cecha, która daje wsparcie do pisania programów „przez powtórne użycie” (ang. software reuse). Zatem mając wcześniej napisany program, możemy wykorzystać część jego klas, ewentualnie rozszerzając ich funkcjonalność poprzez dziedziczenie. Takie postępowanie oszczędza czas piszącego, ale co ważniejsze korzystamy z fragmentów kodu, który jest przetestowany, a zatem mamy mniej miejsc do popełnienia błędów. W chwili obecnej najczęściej kojarzonymi językami wspierającymi taki sposób programowania są C++ czy Java, jednak nie one były pierwsze. W 1971 roku w firmie Xerox rozpoczęto prace nad stworzeniem graficznego środowiska pracy o nazwie Dynabook. Do programowania tego środowiska wybrano język Smalltalk-72. Język ten został stworzony przez Alana Key-a we współpracy z wieloma osobami w tym, co ciekawe, psychologami. W tamtych czasach jeszcze nie dostrzegano wielu problemów, które znamy obecnie, jednak zdawano sobie sprawę, że wkrótce projekty informatyczne będą coraz większe, programów zaś mają używać zwykli użytkownicy nie zaś informatyczni guru. Stąd zaistniała potrzeba tworzenia graficznych interfejsów użytkownika i do takiego celu był projektowany Smalltalk. Został on oparty o różne wcześniejsze idee w tym o LOGO. Autorom Smalltalk-a zależało by był on jak najprostszy i intuicyjny w tworzeniu interfejsu. Zupełnie inny kierunek rozwoju został wyznaczony przez programowanie funkcjonalne (ang. functional programming) oraz programowanie w logice (ang. logic programming). Obie te metodologie powstały do wsparcia programowania sztucznej inteligencji (ang. artificial intelligence). Można najogólniej powiedzieć, że miały stanowić wsparcie do przetwarzania związków pomiędzy bardzo dużą ilością danych (patrz 1.4.8). Jako przykład rozwoju języka funkcjonalnego podamy krótką historię języka LISP (ang. List-Processing Language). Został on stworzony w końcu lat pięćdziesiątych ubiegłego wieku w głównej mierze przez John’a McCarthy’ego. Pierwsza implementacja tego języka pojawiła się na komputerze IBM 704, na nim to właśnie odbyła się demonstracja prototypu w 1960 roku. Język ten zdobył szerokie uznanie i stał się szybko najpowszechniej używa8 Choć słowo dziedziczenie kojarzy się w naturalny sposób z relacją rodzic-dziecko, to jednak tej analogii nie wolno posuwać aż tak daleko. Dziedzicząc pewne cechy od obojga rodziców jesteśmy niejako konglomeratem różnych cech i to nie wszystkich. Podczas gdy przy operacji tej w sensie języków obiektowych dziedziczone jest wszystko.

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

170

Języki programowania

nem językiem do programowanie sztucznej inteligencji posiadającym swoje implementacje również na mikrokomputery. Co więcej, zdarzały się implementacje nie związane ze sztuczną inteligencją, czego przykładem może być wykorzystanie LISP-u do programowanie komend użytkownika w programie AutoCAD. Rdzeniem języków tego typu jest funkcja. Co ciekawe wynikiem działania konkretnej funkcji jest inna funkcja. Nazwa tych języków wynika właśnie z faktu, że zajmują się one przetwarzaniem funkcji. Jest to zupełnie inne jakościowo podejście niż w przypadku wcześniej omawianych języków. W języku LISP, który omawiamy cecha ta jest wyraźnie widoczna podobnie jak to, że operuje on na listach. Dla przykładu funkcja posiada listę argumentów oraz w wyniku działania zwraca listę wartości. Można zapytać a co z funkcją, która wyświetla komunikat na ekranie? Przecież nie musi ona niczego zwracać. Nie szkodzi, zwróci ona listę wyników, która będzie pusta. Podobnie z funkcją, która nie potrzebuje argumentów, lista ich będzie również pusta. Zanim pokażemy przykład fragmentów programów w języku LISP musimy wyjaśnić pojęcie polskiej notacji. Notacja ta została zaproponowana przez polskiego logika Jana Łukasiewicza. Cechą charakterystyczną tej notacji jest umieszczanie operatora przed operandami, stąd też czasami jest ona zwana notacją prefiksową. Rozważmy prosty przykład, w matematyce działanie sumowanie dwu argumentów zapisujemy 2+3 podczas, gdy w polskiej notacji, to samo wyrażenie będzie miało postać (+ 2 3) Zatem co oznacza zapis (* (+ 2 3) 7) oczywiście (2 + 3) ∗ 7. I trochę bardziej skomplikowany przykład, znany jest nam wszystkim następujący wzór √ −b + b2 − 4ac 2a Jego zapis w notacji polskiej będzie miał postać (/ (+ (-b) (sqrt (- (exp b 2) (* 4 a c)))) (* 2 a ))
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

6.2 Ewolucja języków programowania

171

Być może na początku ta notacja wydaje się dziwna i nienaturalna jednak w wielu przypadkach jest znacznie wygodniejsza dla maszyny. Ponieważ naczelną własnością języka LISP jest przetwarzanie list za pomocą funkcji, stąd właśnie polska notacja wydaje się być naturalna do zapisywania jego poleceń. Rozważmy prosty warunek zapisany w poznanym dotychczas strukturalnym języku programowania 9 if x = y then f(x) else g(x) endif A teraz spróbujmy go zapisać w LISP-ie (cond ((eq x y) (f x)) (t (g x)) ) Jak widać już sam „wygląd” takiego programu jest zupełnie inny, być może mniej czytelny dla osób przyzwyczajonych do zapisu „klasycznego” 10 . W chwili obecnej LISP doczekał się wielu implementacji i tak np. w Unixowym edytorze Emacs LISP służy do definicji poleceń, a nawet istnieje moduł do popularnego serwera WWW jakim jest Apache, który pozwala na pisanie aplikacji Internetowych w tym właśnie języku. Przejdźmy teraz do programowania w logice. Jak to było powiedziane wcześniej motywacją do powstania tego typu języków były również badania nad sztuczna inteligencja. Jednak ten sposób programowania jest czymś więcej niż suchym narzędziem, jest wyznacznikiem pewnego nowego sposobu myślenia a mianowicie programowanie bezproceduralnego. Zauważmy, że programowanie proceduralne, czy to strukturalne czy funkcjonalne, skupiało się na tym jak daną rzecz należy wykonać. W programowaniu w logice skupiamy się na tym co należy zrobić. Być może jeszcze wydaje się to niejasne, wszak można powiedzieć: przecież zanim będę wiedział „ jak” muszę wpierw odpowiedzieć „co” będę robił. To prawda, ale w programowaniu w logice wystarczy odpowiedź na pierwsze pytanie. Spróbujemy to wytłumaczyć na przykładzie 6.6, który jest napisany w języku Prolog najpopularniejszym z tego typu języków.
Jest to przykład w pewnym „wymyślonym” języku. Słowo „klasyczny” zostało tu użyte wyłącznie jako przenośnia. Nie można mówić o językach strukturalnych jako klasycznych, gdyż jak to wynika z dat wcale nie były wcześniejsze od funkcjonalnych. Mamy w tym miejscu na myśli tylko fakt, że z reguły strukturalne języki są powszechniej znane.
10 c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004 9

172

Języki programowania

Przykład 6.6. Prosty program napisany w Prologu

rodzic(X, Y) :- ojciec(X, Y). rodzic(X, Y) :- matka(X, Y). malzenstwo(X, Y) :- ojciec(X, Z), matka(Y, T), Z = T. malzenstwo(X, Y) :- matka(X, Z), ojciec(Y, T), Z = T. przodek(X, Y) :- rodzic(X ,Y). rodzenstwo(X, Y) :- matka(M, X), matka(M, Y), ojciec(O, X), ojciec(O, Y), X \= Y. ojciec(marek, jurek). ojciec(marek, zosia). matka(jola, jurek). matka(jola, zosia).

W językach tych definiuje się relacje wiążące elementy, oraz określa pewien zasób wiedzy początkowej. Nie inaczej jest w powyższym przykładzie, określamy relację rodzic, mówiącą o tym, że X jest rodzicem Y, jeśli X jest ojcem Y (pierwsza linijka) lub X jest matką Y (druga linijka). W efekcie mamy powiedziane co to znaczy „być czyimś rodzicem”. Następnie w podobny sposób mamy powiedziane, co to jest relacja małżeństwa, bycia przodkiem, czy też rodzeństwa. Na koniec musimy wprowadzić pewien zbiór wiedzy, są to linie zaczynające się od słów matka i ojciec. Ostatecznie otrzymujemy pewną bazę wiedzy o niewielkiej rodzinie, teraz jeśli zapytamy system czy np. jola i marek są małżeństwem otrzymamy odpowiedź twierdzącą, mimo iż nie podaliśmy tego w sposób efektywny a jedynie na podstawie relacji z innymi elementami. Dzięki takiemu podejściu widać, że języki te nadają się do pisania systemów wspomagających wnioskowanie i w konsekwencji do tzw. baz wiedzy. Sam język Prolog, użyty w powyższym przykładzie został wymyślony w 1970 roku przez Alain-a Colmerauer-a oraz Philippe-a Roussel-a. Oni to wraz ze swoją grupą prowadzili badania na Uniwersytecie w Marsylii dotyczące sztucznej inteligencji. Stworzyli tam pierwszą specyfikację tego języka i rozwijali go dalej. Jak widać historia języków programowanie jest bardzo bogata nie tylko w różnego rodzaju dialekty tego samego języka, lecz przede wszystkim w
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

6.3 Klasyfikacja języków programowania

173

różne koncepcje, które miały służyć rozwiązywaniu konkretnych problemów zaczerpniętych z życia.

6.3

Klasyfikacja języków programowania

Klasyfikacji języków programowania można dokonać na różne sposoby, lub mówiąc inaczej patrząc z różnych poziomów, lub przez inne pryzmaty. Z jednej strony można dzielić języki ze względu na metodologię programowania jaką wspierają i wtedy wyróżnia się zasadniczo: programowanie sekwencyjne, programowanie strukturalne, programowanie obiektowe, programowanie funkcjonalne, programowanie w logice. Z drugiej strony można mówić o kolejnych generacjach języków programowania, i wtedy wyróżnia się pięć generacji, które omówione są w dalszej części punktu (zob. 6.3.2).

6.3.1

Podział według metodologii programowania

Szczegółowe omówienie języków pod tym kątem, na tle rozwoju myśli programistycznej, można znaleźć w punkcie (6.2), teraz dokonamy jedynie podsumowania wcześniejszych informacji. Programowanie sekwencyjne Konstrukcja tego typu języków odzwierciedlała prawie dokładnie język maszyny na jakiej one działały. Były to często programy zapisane w asemblerze danego procesora. Nawet jeśli były to języki wyższego poziomu, jak np. FORTRAN IV, to typy danych jakie w nim występowały również stanowiły odzwierciedlenie tego co mógł reprezentować procesor. Zatem języki te posiadały wyłącznie typy liczbowe ewentualnie znakowe, jednak nie było możliwości definicji własnych typów danych. Taka cecha została wprowadzona dopiero w poźniejszych językach np. Pascalu. Programowanie strukturalne W latach sześćdziesiątych ubiegłego wieku zaczęto zwracać większą uwagę na sposób pisania, czyli metodologię. Zauważono, że nie wystarczy jeden czy dwu zręcznych programistów. Coraz częściej programy były większe zatem trudniejsze do opanowania i w konsekwencji człowiek popełniał więcej błędów. Zaistniała potrzeba zaprojektowania takich języków programowania, który wprowadzałyby pewien porządek w programie, były wsparciem dla
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

174

Języki programowania

programisty a nie balastem. Konsekwencją tych spostrzeżeń było pojawienie się prac omawiających programowanie strukturalne (ang. structured programming). Sam termin programowanie strukturalne jest niejako wypaczony w odniesieniu do jego pierwotnego znaczenia, w oryginale autorom chodziło o podkreślenie, że programowanie to niejako wymusza prawidłową strukturę programu. Jednak z nazwy można by sądzić, że odnosi się to do pisania wyłącznie struktur. Należy podkreślić, że nazwa ta miała wyłącznie wskazywać na dobry styl pisania, co często prowadziło nawet do skrajności mówiącej o zakazie używania w takim programowania instrukcji skoku GOTO11 . Rzeczywiście przesadna ilość instrukcji GOTO w programie zaciemnia a często wręcz uniemożliwia analizę kodu, jednak są miejsca w których zastosowanie tej instrukcji jest czytelne i upraszające program. W związku z pojawieniem się nowej tendencji w programowaniu musiały pojawić się języki wspierające tan paradygmat postępowania. Do pierwszych języków strukturalnych należy zaliczyć Pascal, zaprojektowany przez Niklausa Wirtha [Wir71b]. Język Pascal był zaprojektowany by wspomagać dydaktyków programowania, tak by mogli nauczać dobrych nawyków przyszłych programistów. Zatem musiał spełniać następujące założenia: • struktura języka i jego konstrukcje powinny być przejrzyste i skłaniające do porządnego pisania, • powinien być językiem na tyle obszernym (uniwersalnym) by można w nim opisać szeroką klasę algorytmów, • opis języka powinien być ścisły, czytelny i prosty, • język ten powinien być łatwy do zaimplementowania, czyli stworzenia kompilatora dla niego. Język Pascal wspierał metodologię tworzenia programów zaproponowaną również przez Wirtha, znaną pod nazwą metody zstępującej (ang. stepwise refinement) [Wir71a]. W metodzie tej pracę rozpoczyna się od naszkicowania szkieletu całego programu, a następnie właśnie w sposób zstępujący rozpisuje się coraz to drobniejsze fragmenty, aż na końcu dochodzi się do funkcji i struktur danych. Oba te elementy implementuje się w języku strukturalnym, który właśnie wspierał zarówno tworzenie struktur danych definiowanych przez programistę, jak i definiowanie funkcji czy też procedur. Bardzo przystępny i zwięzły opis pierwszej wersji języka Pascal można znaleźć w [MI86].
Nawet utarło się swego czasu powiedzenie, że „umiejętności programisty są odwrotnie proporcjonalne do ilości użytych przez niego instrukcji GOTO w programie”.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004 11

6.3 Klasyfikacja języków programowania

175

Programowanie obiektowe Programowanie obiektowe zaczęto rozwijać, na dużą skalę, w latach osiemdziesiątych XX wieku. Języki zorientowane obiektowo — czyli wspierające pisanie w zgodzie z metodologią obiektową, musiały posiadać następujące cechy: definicję własnych typów, tworzenie klas, ukrywanie informacji, dziedziczenie. Szczególnie dziedziczenie było silnym mechanizmem, dzięki któremu można było wykorzystywać kod napisany wcześniej, ewentualnie rozbudowując go tylko o nowe cechy. W chwili obecnej istnieje bardzo dużo języków zorientowanych obiektowo, można tu wymienić: C++, Java, Python, PHP. Co ciekawe C++ jest językiem w którym można pisać zarówno strukturalnie jak i obiektowo, to znaczy nie ma konieczności używania klas. Dla kontrastu w Javie nie ma takiej możliwości i nawet najkrótszy program musi mieć co najmniej jeden obiekt.

Programowanie funkcjonalne Jest to rodzaj programowania, które jest skoncentrowane wokół funkcji. W większości tego typu języków programowania wszystko jest funkcją, wręcz cały program jest to prostu wywołanie funkcji i jej wykonanie. Zastosowanie tych języków to głównie sztuczna inteligencja, ale jak to było opisane wcześniej bynajmniej nie do tego zostały ograniczone. Okazuje się, że świetnie się sprawdzają do programowania komend użytkownika w złożonych programach. Innym zastosowaniem jest wspomaganie dowodzenia twierdzeń matematycznych i systemy wnioskujące. Z takiech źródeł wyrósł nie omawiany wcześniej język Standard ML, który również jest obecnie szeroko wykorzystywany.

Programowanie w logice Podobnie jak programowanie funkcjonalne tak i programowanie w logice zostało stworzone do wspomagania procesów wnioskowania i baz wiedzy. Paradygmat tego programowania różni się od poprzednich, gdyż koncentruje się na problemie jako taki — czyli pyta „co” jest do zrobienia, nie zaś na samym sposobie realizacji. Niewątpliwie najpopularniejszym środowiskiem do programowania w taki sposób jest Prolog, choć spotyka się też inne systemy, w tym systemy specjalistyczne czy też przemysłowe, pisane do realizacji specjalistycznych zastosowań.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

176

Języki programowania

6.3.2

Generacje języków programowania

W literaturze ([Mac99]) można spotkać podział języków programowania na pięć generacji.

Języki pierwszej generacji Struktura takich języków odpowiadała budowie komputerów lat 60-tych, co jest zrozumiałe o tyle, że w tamtych czasach istniały wyłącznie duże maszyny i było zalewie kilka firm je produkujących. Wszelkie instrukcje sterujące programem były ściśle związane z odpowiadającymi im instrukcjami w maszynie. Dla przykładu instrukcje warunkowe nie mogły być zagnieżdżone. Struktura języka była sekwencyjna, oparta wyłącznie na instrukcji skoku GOTO, przy pomocy której sterowano przebiegiem programu. Nawet w językach w których pojawiały się podprocedury (podprogramy), nie można było stosować rekurencyjnych ich wywołań. Podobnie typy danych były ściśle związane z maszyną, występowały w danym języku tylko takie, które miały odzwierciedlenie w implementacji w maszynie. Struktury danych nie mogły być zagnieżdżane. Nie przywiązywano uwagi do kwestii przenośności kodu na inne maszyny. Nie występowało dynamiczne zarządzanie pamięcią. Program zapisywany w tych językach do tego stopnia był silnie związany z maszyną, że często etykiety kolejnych linii były numerami komórek pamięci w których te rozkazy się znajdowały (patrz przykład 6.1). Przykładem języka programowanie pierwszej generacji jest FORTRAN IV.

Języki drugiej generacji Pojawiają się w tych językach pewne uogólnienia typów danych, między innymi możliwość definiowania tablic z dolnym indeksem innym niż 1, oraz tablic dynamicznych. Zwykle w tych językach pojawia się ścisła kontrola typów oraz typy wbudowane, wciąż nie ma typów definiowanych przez użytkownika. Struktura programu przypomina strukturę hierarchiczną i blokową, co powoduje mniejszą potrzebę użycia instrukcji GOTO. Pojawia się możliwość dynamicznego zarządzania pamięcią. Wiele z języków posiada słowa kluczowe i słowa zastrzeżone. Przykładem języka programowania drugiej generacji jest Algol-60.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

6.4 Kompilacja vs. interpretacja

177

Języki trzeciej generacji Struktura języka trzeciej generacji stała się bardziej elastyczna i wygodniejsza do korzystania, przykładem może być pętla for. Dodano również konstrukcje, które są wygodniejsze z punktu widzenia programisty, przykładem może być instrukcja wielokrotnego wyboru case. Pojawia się możliwość definiowania własnych struktur danych przez programistę. Wszystko to powoduje ogólną orientację języka w kierunku aplikacji a nie maszyny na której jest implementowany. Przykładem języka programowania trzeciej generacji jest Pascal. Języki czwartej generacji Jedną z głównych cech tych języków jest abstrakcja danych, wsparcie dla oddzielania informacji publicznej (interfejsu) od prywatnej (jej realizacji, implementacji). Języki te pozwalają często na tworzenie uogólnionych modułów (np. język ADA). Próbuje się pogodzić elastyczność z prostotą zapisu informacji. Drugą ważną cechą jest wsparcie dla programowania współbieżnego, zatem języki te posiadają mechanizmy synchronizacji procesów czy komunikacji pomiędzy nimi. Wprowadza się mechanizmy ochrony danych, oraz dodawany jest mechanizm obsługi wyjątków. Przykładami takich języków mogą być: C++, ADA, Java. Języki piątej generacji Nikt jeszcze nie wie dokładnie jak te języki będą wyglądały, ani jaka naczelna idea będzie im przyświecała. Wiele doświadczeń już zdobyto, ale wiele kierunków jeszcze nie jest rozpoznanych. Można powiedzieć tylko o pewnych kierunkach i trendach. Są nimi języki zorientowane obiektowo, języki zorientowane funkcyjnie, języki zorientowane logicznie. Słowo zorientowane mówi tylko o pewnym paradygmacie, wokół którego następuje rozbudowa języka.

6.4

Kompilacja vs. interpretacja

Program zapisany w języku programowania ma postać pliku tekstowego i w tej formie nazywany jest kodem źródłowym. Aby kod źródłowy mógł być wykonany musi zostać przetłumaczony na program wykonywalny, zapisany w języku maszynowym (jedyny język zrozumiały przez procesor zob. 3.4.5). Istnieją programy tłumaczące programy przez nas napisane
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

178

Języki programowania

na język danej maszyny. Ze względu na sposób i charakter pracy mówimy głównie o kompilatorach i interpreterach. Proces, który przetwarza program zapisany w języku programowania, zwany również programem źródłowym lub kodem źródłowym, na język maszynowy nazywamy kompilacją. Niestety kompilacją nazywany jest również jest proces tłumaczenia na język adresów symbolicznych lub inaczej asembler. Z kolei dla odróżnienia proces tłumaczenia programu zapisanego w asemblerze na kod maszynowy nazywany jest często dla wyróżnienia asemblacją. Otrzymany w wyniku kompilacji program w kodzie maszynowym nazywamy programem wykonywalnym. W przypadku kompilacji musimy posiadać program, który będzie tłumaczył nasz kod źródłowy na kod maszynowy, czy też na asembler. Program ten — kompilator — charakteryzuje się tym, że z reguły umie przetłumaczyć tylko jeden język programowania, oraz tylko na jedną platformę sprzętową i w ramach jednego systemu operacyjnego. Zatem jeśli posiadamy kompilator języka C na platformę Intel, pod kontrolą systemu Linux, to nie będziemy mogli przy jego pomocy skompilować programu napisanego w języku ADA. Co więcej program napisany w języku C i przetłumaczony na kod maszynowy za pomocą tego kompilatora nie będzie mógł być wykonany w systemie Windows, nawet jeśli będzie to ten sam komputer 12 . Co prawda cały proces tłumaczenia programu nazywamy kompilacją, jednak faktycznie składa się on z dwu czynności: kompilacji i łączenia. Kompilacja jest procesem zamiany programu źródłowego na plik binarny, który zawiera program w kodzie maszynowym — plik ten zwany jest plikiem obiektu13 (ang. object file). Jednak plik taki bardzo rzadko jest finalnym programem, wynika to z faktu, że często pisząc programy używa się odwołań do funkcji, które zawarte są w bibliotekach. Biblioteki są zbiorami skompilowanych funkcji, które mogą być używane wielokrotne. Zauważmy, że pisząc jakikolwiek program będziemy odwoływali się do funkcji (procedury) wypisującej komunikaty na ekranie. Zatem warto taką funkcje napisać raz, a potem wielokrotnie ją tylko wywoływać a nie pisać za każdym razem od nowa. Ponieważ nie jesteśmy jedynymi programistani na świecie, często to producent kompilatora dostarcza takich popularnych funkcji w postaci bibliotek — zwanych również bibliotekami systemowymi. Cały proces kompilacji jest zilustrowany na rysunku 6.4. Po zakończonym procesie kompilacji mamy zbiór plików obiektów oraz biblioteki. W tym momencie należy posklejać to wszystko w jeden wykony12 13

Oczywiście jest możliwość emulacji jednego systemu operacyjnego w ramach innego. Nie ma to nic wspólnego z programowaniem obiektowym!
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

6.4 Kompilacja vs. interpretacja

179

walny program. Proces wykonujący tę czynność nazywany jest łączeniem lub konsolidacją14 , zaś program wykonujący go zwany jest konsolidatorem. Wynikiem łączenia jest kompletny program zawierający wszystko to co jest niezbędne do jego wykonania w ramach konkretnej platformy sprzętowo-systemowej. Opisany proces łączenia zwany jest również łączeniem statycznym. Drugą możliwością łączenie jest łączenie dynamiczne. W tym wypadku, program końcowy jest wyłącznie informowany o obecności pewnych funkcji w bibliotekach, zaś wywoływanie tych funkcji spoczywa na systemie operacyjnym. Funkcje systemowe umieszczane są wtedy w bibliotekach dzielonych15 .
(dowolny edytor tekstowy)

Edycja

Pliki tekstowe {plik na dysku} {plik na dysku}

Kompilacja
plik obiektowy 1 plik obiektowy 2

kod maszynowy

{plik na dysku}

{plik na dysku}

plik obiektowy 1

plik obiektowy 2

pliki biblioteczne

plik wynikowy (program)

Rysunek 6.1: Ilustracja procesu kompilacji. Drugim sposobem uruchamiania programów zapisanych w pewnym ję14 Czasami określa się go spolszczając angielskie słowo linking i nazywa się go linkowaniem, zaś program go wykonujący linkerem. 15 Są one znane doskonale użytkownikom systemów Windows pod nazwą bibliotek DLL (ang. Dynamic Linking Library).

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

U Y X W T © U  T &R¦&V

r    q  p U S  ¡ i U © ¥  U £ Y T a ©  h £ a    S  V c T a U £ V   ¥   © T S £ P ¥ £ ¦¤`¤d¤¢&dg&¦¤&!f!ed¤¦Rd¦¤¡ I

H F 4 0 F E 7 D B@ 6 9 7 6 5 4 3 ( 1 0 ( 2G)2)8CA28))22))' 

  S  V c T a   ¥ U Y X W T © U  T V U S © T S £ P £ ¥ £ ¦¤&!b`&R¦&¨¤¦RQ¦¤¡ I

#       © % ¥ £ ¡ $¦&¨¦¤! 
plik biblioteczny plik biblioteczny Pliki binarne

#       ©§ ¥ £ ¡ $¦"¦¤! 

¨¦¤¨¦¤¢        ©§ ¥ £ ¡

¨¦&¨¦¤!        © % ¥ £ ¡

180

Języki programowania

zyku programowania, jest ich interpretacja. W celu uruchomienia programu interpretowanego, musimy posiadać dla niego środowisko uruchomieniowe zwane interpreterem. Interpreter jest podobnie jak kompilator programem, który umożliwia nam uruchomienie naszego programu. Różnicą jest to, że interpreter nie produkuje programu wykonywalnego w postaci pliku wykonywalnego, a tłumaczy nasz program „w locie” na program maszynowy i wykonuje go na bieżąco. Przypomina to wykonywanie skryptów powłoki (lub plików wsadowych w przypadku systemu MS-DOS), stąd też inna nazwa tego typu języków — języki skryptowe. Charakterystyką tych języków jest to, że posiadają środowisko do interpretacji na wiele platform sprzętowych i systemowych, a co za tym idzie przenośność programów w nich napisanych jest ogromna. Środowiska uruchomieniowe (interpretery) posiadają również zbiory bibliotek systemowych, które zawierają funkcje najczęściej stosowane lub napisane przez kogoś innego. W przypadku tych języków nie występuje proces łączenia w takiej formie jak przy kompilacji, funkcje biblioteczne są na bieżąco wyciągane z bibliotek i wywoływane. Ilustracja procesu interpretacji znajduje się na rysunku 6.4. Przykładem języka skryptowego jest PERL, który posiada swoje środowisko do interpretacji na ponad dwadzieścia platform. Zasadnicza różnica pomiędzy językami interpretowanymi a kompilowanymi polega na tym, że w jednym przypadku użytkownik końcowy otrzymuje program w postaci kodu źródłowego (języki interpretowane), a w drugim otrzymuje program w kodzie maszynowym (języki kompilowane). Ponieważ kod źródłowy należy interpretować w czasie wykonania zatem są one wolniejsze od programów skompilowanych. Również niekorzystny jest fakt, że przypadkowy użytkownik, często laik, otrzymuje postać źródłową naszego programu. Istnieje teoretyczna możliwość, że przez przypadek ją uszkodzi zaglądając do środka programu jakimkolwiek edytorem tekstowym i przez ciekawość zmieniając np. plus na minus 16 . Niepodważalną zaś zaletą języków interpretowanych jest ogromna przenośność kodu, co w przypadku programu wykonywalnego prawie nie występuje, za wyjątkiem sytuacji wykorzystywania emulatorów17 . Z uwagi na przenośność i uniwersalność języki skryptowe wykorzystuje się do pisane różnego rodzaju programów instalacyjnych, bądź skryptów administracyjnych. Cieszą się one dużym zainteresowaniem przy automatyzacji stron WWW i tu jako przykład można podać język PHP. Z kolei
Podkreślamy czysto teoretyczną możliwość, gdyż w praktyce użytkownik nie ma powodu tam zaglądać. 17 Programy „udające” inny procesor, bądź inny system operacyjny, bądź jedno i drugie.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004 16

6.4 Kompilacja vs. interpretacja

181

języki kompilowane stosuje się tam gdzie najważniejsza jest wydajność 18 . Rysunek 6.2: Ilustracja procesu interpretacji.
(dowolny edytor tekstowy) plik biblioteczny plik biblioteczny

Edycja

{plik na dysku}

wykonanie programu

Próbą pogodzenie obu powyższych rozwiązań było wprowadzenie języków prekompilowanych. W takiej sytuacji programista po napisaniu programu dokonuje procesu wstępnej kompilacji — prekompilacji swojego programu, efektem tej czynności jest plik w postaci kodu pośredniego, często zwanego Byte-code lub krócej B-Code. W efekcie użytkownik otrzymuje tę właśnie postać wstępnie skompilowaną, a następnie uruchamia program na pewnej maszynie wirtualnej (ang. virtual machine) lub krótko VM. Maszyna ta w dalszym ciągu interpretuje program i wykonuje go na konkretnej platformie sprzętowo-systemowej. Jak widać jest to połączenie procesów kompilacji i interpretacji, dzięki czemu oddajemy w ręce użytkownika niejako finalny program, a jednocześnie jest on przenośny wszędzie i będzie funkcjonował tam gdzie posiadamy VM dla tego języka. Wstępna kompilacja, przyśpiesza późniejszą interpretację, zatem języki te są szybsze od interpretowanych, oczywiście wolniejsze od kompilowanych. Funkcje z bibliotek podobnie jak w przypadku języków interpretowanych są dołączone
Faktem jest, że często komercyjne firmy programistyczne, mówią o zagrożeniu kradzieży kodu źródłowego, jeśli jest on dostępny. Jednak doświadczony programista do takiej argumentacji będzie podchodził ostrożnie. Ma on świadomość, że prześledzenie, zrozumienie a następnie przepisania na własne potrzeby dużego programu, który ma parę milonów linii kodu źródłowego jest bardzo trudne i czasochłonne. Osoba która to zrobi, zrozumie ideę tego programu, zatem może go napisać sama od nowa, i wcale nie musi go kraść.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004 18

#       © % ¥ £ ¡ $¦&¨¦¤! 

#       © ¥ £ ¡ $¦R&% ¤! 
{plik na dysku}

¨¦&¨¦¤!        © % ¥ £ ¡ ¨¦©      
¡

plik biblioteczny

¥ £ ¡ ¦¤! 

pliki biblioteczne

Interpretacja

182

Języki programowania

w trakcie wykonywania. Jednak w przypadku tych języków, dość często producent maszyny wirtualnej dostarcza te biblioteki nie w postaci kodu pośredniego, a w postaci skompilowanej na konkretną platformę. W takiej sytuacji funkcje te działają szybciej niż te napisane przez programistę. Przykładem takiego języka programowania jest JAVA. Co ciekawe w przypadku tego języka, firmy które tworzą maszynę wirtualną, wzbogacają ją coraz częściej w technologię JIT (ang. Just In Time compilation). Technologia ta zapewnia kompilację z kodu pośredniego do kodu wykonywalnego, dla danej platformy sprzętowo-systemowej, w trakcie pierwszego uruchomienia programu. Dzięki czemu każde następne wywołanie programu pobiera już tę skompilowaną, a zatem szybszą, wersję. Proces prekompilacji i wykonania na maszynie wirtualnej ilustruje rysunek 6.4. Rysunek 6.3: Ilustracja procesu prekompilacji i wykonania na maszynie wirtualnej.
(dowolny edytor tekstowy)

Edycja

Pliki tekstowe {plik na dysku} {plik na dysku}

Pre−kompilacja
plik obiektowy 1 plik obiektowy 2

{plik na dysku}

{plik na dysku}

plik obiektowy 1

plik obiektowy 2

pliki biblioteczne

Wykonanie na maszynie wirtualnej
Maszyna wirtualna (VM)

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

£ 0  2 © 6      ¥ A2 @ 9 ¤%)B$¦1 8

P H G 1  1 ©   2 F  A A  ©  D ¥ 1 0  0E 1 © 2    A2 D @ ¤I¦ 8 )(%)¢3%¤¤33¤¢ 8 ¢%¨%) C

!       © # ¥ £ ¡ "¦¤%$¦¤¢ 
plik biblioteczny plik biblioteczny

!       ©§ ¥ £ ¡ "¦¤¨¦¤¢  

7 2 £ 0  2 © 6    4   ¥ 2 0 © 1 0 £ ' £ ¥ £ ¤¤)¤%)53¤¦)(¦¤¡ &

¨¦¤¨¦¤¢        ©§ ¥ £ ¡

¨¦%$¦¢        © # ¥ £ ¡

Pliki binarne, zwykle kod maszynowy,

6.5 Elementy teorii języków formalnych

183

6.5

Elementy teorii języków formalnych

W tym punkcie podamy podstawowe pojęcie, którymi posługują się twórcy języków do ich opisu. Jednak spróbujemy przede wszystkim podać pewne intuicje związane z tymi pojęciami, celem lepszego zrozumienia złożoności zagadnienia. Szersze omówienie tych zagadnień wprowadzana jest z reguły na przedmiotach związanych z teorią automatów i języków formalnych.

6.5.1

Gramatyka

W celu formalnego zdefiniowania języka wprowadza się następujące pojęcia alfabetu — pewien zbiór symboli, musi być on skończony i niepusty, zwykle oznaczany przez symbol Σ; litery — dowolny element alfabetu; słowa — skończony ciąg liter alfabetu, spotyka się również nazwę słowo nad alfabetem; słownika — zbiór wszystkich słów utworzonych nad alfabetem, zwykle oznaczany poprzez dodanie symbolu gwiazdki do symbolu alfabetu, np. Σ∗ ; języka — dowolny podzbiór słownika. Zauważmy, że jeśli posługujemy się językiem naturalnym np. polskim, to słowa tego języka składają się z liter oraz pewnych dodatkowych znaków, zatem mamy alfabet. Na bazie tego alfabetu budowane są słowa danego języka. Jeśli na bazie danego alfabetu zbudujemy zbiór wszystkich możliwych słów to otrzymamy słownik. Zauważmy jednak, ze jest to zbiór wszystkich słów, czyli np. słowa „aalgifk”, które nie ma określonego znaczenia w języku polskim. Następnie językiem nazwiemy pewien podzbiór słów ze słownika, czyli odrzucimy te słowa, które nie posiadają znaczenia. W końcu lat pięćdziesiątych Noam Chomsky i John Backus wprowadzili pojęcie gramatyki formalnej. Co więcej, wprowadzili pojęcie gramatyki kontekstowej i gramatyki bezkontekstowej. Przekłada się to bezpośrednio na języki. Otóż jeśli dla danego języka istnieje gramatyka kontekstowa, która go generuje to nazwiemy go językiem kontekstowym, podobnie rzecz się ma dla języka bezkontekstowego. Znów odwołując się do pewnej intuicji można powiedzieć, że jeśli w danym języku mamy
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

184

Języki programowania

dowolne zdanie i znaczenie jego słów jest jasne w oderwaniu od reszty zdania, czyli od kontekstu, to taki język będzie bezkontekstowy. Analogicznie dla języka kontekstowego. Niestety trudno na pierwszy rzut oka wyobrazić sobie język naturalny19 bezkontekstowy i faktycznie nie ma takich języków.

6.5.2

Notacja BNF

Gramatyki formalne, zasygnalizowane powyżej, nie stanowią jedynej możliwej formy opisu składni, lecz również stanowią podstawę dla innych koncepcji. Do innych metod zliczamy specjalne języki opisu składni. Języki służące do opisu składni języków formalnych nazywamy metajęzykami. Najbardziej rozpowszechnionym metajęzykiem jest notacja BNF (Bachus-Naur-Form). W notacji BNF wprowadza się pewne oznaczenia i skróty do opisu składni. Przy użyciu tego metajęzyka, opis składni przedstawiony jest w postaci definicji, nazywanych formułami metajęzykowymi, postaci L ::= R, gdzie L — nazwa zmiennej metajęzykowej, R — zbiór wartości, które może przyjmować zmienna L, symbol ::= oznacza „równe z definicji”. Nazwa zmiennej metajęzykowej, symbol nieterminalny, składa się z pary nawiasów , między którymi występuje dowolny ciąg znaków. Ciąg R jest wyliczeniem jednej lub więcej wartości zwykle oddzielanych znakiem ”. Dodatkowo wprowadza się symbole {} oraz [], służą one do określenia odpowiednio powtórzenia oraz elementu opcjonalnego . . . i tu zwykle następuje drobna konsternacja. Przed czytaniem czegokolwiek w notacji BNF należy się upewnić co autor rozumie pod tymi pojęciami. Możliwe wartości to odpowiednio powtórzenie zero lub więcej razy, powtórzenie raz i więcej, ale może zdarzyć się coś innego. Dlatego zawsze na początku warto dobrze zinterpretować znaczenie tych symboli. Na przykład w [Fe98] w dodatku opisującym składnię jest napisane: {} [] \\ | ::= ... ------dowolny ciąg powtórzeń element opcjonalny wybór jednego z elementów składowych alternatywa reguła produkcji składnik identyczny

Przykład 6.7. Fragment składni Pascala zapisany przy pomocy notacji BNF.
19

Mamy tu na myśli polski, angielski ...
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

6.5 Elementy teorii języków formalnych

185

<litera>::=A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z| a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z <cyfra>::=0|1|2|3|4|5|6|7|8|9 <znak_liczby>::=+|<litera_lub_cyfra>::=<litera>|<cyfra> <identyfikator>::=<litera>{<litera_lub_cyfra>} <ciag_cyfr>::=<cyfra>{<cyfra>} <liczba_całkowita_bez_znaku>::=<ciąg_cyfr>

6.5.3

Notacja przy użyciu diagramów składni

Inną formą prezentacji gramatyki języka są diagramy składni. Notacja ta, zaproponowana przez Niklausa Wirtha, nosi także nazwę notacji skrzynkowej. W notacji tej każdy element składni jest prezentowany za pomocą pewnego symbolu graficznego, zaś połączenie pomiędzy nimi za pomocą strzałek. Rysunek 6.5.3 ilustruje fragment składni języka Pascal, podany w poprzednim punkcie za pomocą notacji BNF, zapisany w notacji skrzynkowej.

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

186

Języki programowania

Rysunek 6.4: Fragment składni Pascala zapisany przy pomocy diagramów składni.

litera a b z cyfra 0

A Y Z znak_liczby +

9

litera_lub_cyfra
litera

cyfra

cyfra

identyfikator

litera

litera_lub_cyfra

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004  

¥   

© ¨   ¦ ¥ £ ¡ ¤¤§¤¢¤¢ 

liczba_calkowita_bez_znaku

Rozdział 7

System operacyjny
Prawie każdy1 komputer posiada system operacyjny, jednak zwyczajny użytkownik często wcale nie zdaje sobie sprawy z jego funkcjonowania. Niewidoczny dla nie wtajemniczonych, nie zawsze doceniany przez tych, którzy coś o nim wiedzą, sprawia, że komputer staje się użytecznym i przyjaznym narzędziem pracy. Komputer jaki jest dziś znany, to „uniwersalna maszynka” do wszystkiego. Urządzenie, któremu przypisuje się wręcz jakąś magiczną moc. Można zauważyć tendencję dodawania wyrazu „komputerowy” w celu podnoszenia rangi danego przedmiotu; chyba tylko w ten sposób uzasadnić można hasła reklamowe typu „Komputerowe haftowanie”. Bezspornym faktem pozostaje jednak, iż komputery są obecne we wszystkich dziedzinach ludzkiej działalności. Stało się to głównie dzięki uczynieniu ich bardzo przyjaznymi dla zwykłego użytkownika i zapewnieniu zdolności do spełniania powierzanych im zadań w sposób efektywny (i nierzadko efektowny). Zwykle ta efektywność jest zapewniania przez dobry i sprawny system operacyjny. System operacyjny, to pierwszy program z jakim stykamy się zwykle zaraz po włączeniu komputera. Można usłyszeć nieraz: „Czekam, aż załadują się Windowsy”, ale czym są te „Windowsy”. „No jak to, czym? — pada odpowiedź — Windowsy to te ikonki, ta tapeta i kursor myszki. Jak klikniesz tu, to się uruchomi edytor tekstu, jak tu, to gra a jak tu, to . . . ”. Jest to wypowiedź użytkownika, który nie zamierza zagłębiać się w tajniki informatyki. Dla informatyka jest to oczywiście niedopuszczalne uproszczenie.
1

Patrz punkt 7.9

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

188

System operacyjny

7.1

Zadania realizowane przez system operacyjny

Zasadniczym celem systemów komputerowych jest wykonywanie programów użytkownika mających za zadanie ułatwić rozwiązanie pewnych zagadnień i problemów. Wraz z konstruowaniem odpowiedniego sprzętu, powstaje także oprogramowanie, które ma być na nim uruchamiane. Wiele z tworzonych programów wymagać będzie pewnych wspólnych operacji, jak na przykład dostęp do urządzeń wejścia/wyjścia, takich jak drukarka czy monitor. Jeśli komputer będzie docelowo posiadał wiele programów komunikujących się ze światem zewnętrznym przez wejścia/wyjścia (a tak jest w dzisiejszych komputerach), to ekonomicznie jest wyodrębnić funkcje realizujące komunikację w postaci pewnej wspólnej, dla nich wszystkich, „biblioteki” (jak się przekonamy dalej — zwykle jest to składnik systemu operacyjnego). Wspólne funkcje odpowiedzialne za sterowanie i przydzielanie zasobów, to oszczędność nie tylko miejsca w pamięci (wszak każdy program je zajmuje), ale również oszczędność czasu, gdyż nie trzeba dwa razy pisać tych samych operacji. Poniżej podano krótką charakteryzację głównych zadań stawianych systemom operacyjnym: Podsystem zarządzania procesami: • tworzenie i usuwanie procesów;

• wstrzymywanie i wznawianie procesów;

• dostarczanie mechanizmów synchronizacji i komunikacji procesów;

Podsystem zarządzania pamięcią operacyjną: • przydzielanie i zwalnianie obszarów pamięci w zależności od zapotrzebowania zgłaszanego przez procesy; • przechowywanie informacji o aktualnie zajętych częściach pamięci (ich rozmiar, położenie, proces, któremu są przypisane); • decydowanie o tym, który proces ma być załadowany do obszarów zwolnionych; Podsystem zarządzania pamięcią masową: • planowanie przydziałów obszarów pamięci dyskowej;

• zarządzanie obszarami wolnymi;

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

7.2 System operacyjny a architektura komputera

189

• dostarczanie logicznych jednostek składowania danych — plików i katalogów, oraz zarządzanie nimi; Podsystem wejścia/wyjścia: • dostarczenie programów obsługi poszczególnych urządzeń;

• izolowanie użytkownika od specyfiki urządzeń — dostęp do urządzenia odbywa się nie bezpośrednio, ale za pomocą dobrze zdefiniowanego interfejsu;

Podsystem ochrony: • identyfikacja użytkownika;

• ochrona zasobów systemowych przed nieautoryzowanym dostępem; • ustanowienie i kontrolowanie wzajemnych relacji pomiędzy użytkownikiem, czynnościami jakie wykonuje i programami jakie uruchamia a przyznanymi prawami dostępu; • zarządzanie rozliczeniem — sprawowanie kontroli nad stopniem wykorzystania systemu przez poszczególnych użytkowników;

Podsystem interpretacji poleceń: • dostarczenie mechanizmów interaktywnego dostępu do funkcji systemu operacyjnego dotyczącego między innymi nadzorowania wykonywania procesów, obsługi wejścia/wyjścia czy ochrony zasobów. Istnienie i działanie wszystkich tych podsystemów, to odpowiedź na główne zadanie stawiane przed systemem operacyjnym: umożliwić efektywną pracę pozwalającą w maksymalnym stopniu wykorzystać zasoby 2 jakimi dysponuje system komputerowy.

7.2

System operacyjny a architektura komputera

Własności systemu operacyjnego wpływają na obraz i możliwości komputera w podobnym, jeśli nie większym stopniu, co jego fizyczna architektura (procesor, pamięć, ...). Tym drugim czynnikiem świadczącym o możliwościach
Przez zasoby rozumiemy tutaj wszystkie urządzenia i podzespoły wchodzące w skład systemu komputerowego a więc procesor, pamięci, ale także drukarki, skanery, itp.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004 2

190

System operacyjny

komputera jest oczywiście architektura fizyczna komputera (procesor, pamięć, układy towarzyszące — patrz 3.3). Jednym z zadań stawianym dzisiejszym systemom operacyjnym jest zapewnienie w jak największym stopniu izolacji od fizycznych urządzeń przy jednoczesnym zachowaniu łatwości i intuicyjności ich obsługi — np. proces nagrywania danych na dysk, dyskietkę, dysk magnetooptyczny, czy CD powinien być maksymalnie ujednolicony, o prostocie nie wspominając. Dobry przykład ilustrujący poprzednie zdanie stanowi system operacyjny o nazwie Linux. Dostępny jest on na wiele różnych platform sprzętowych różniących się często istotnie pod względem ich budowy fizycznej. Użytkownik wcale jednak nie musi zdawać sobie z tego sprawy, gdyż wszystkie polecenia i czynności bez względu na rzeczywistą konfigurację sprzętową dają ten sam, lub możliwie najbardziej zbliżony, efekt. Dlatego też myśląc o systemie komputerowym z punktu widzenia zwykłego użytkownika wyodrębnia się zwykle trzy warstwy pojęciowe (rysunek 7.1): • Programy użytkowe i udostępniane przez nie możliwości manipulacji i przetwarzania danych. • System operacyjny, który pośredniczy pomiędzy elementami z warstwy poprzedniej i następnej. • Sprzęt komputerowy, który dzięki systemowi operacyjnemu wystarczy, jeśli użytkownik wie jakiego typu są zainstalowane urządzenia. Nie musimy nic wiedzieć na temat ich faktycznego sposobu pracy. Znajomość architektury komputera zaczyna być istotna dopiero, gdy ktoś samodzielnie zamierza napisać system operacyjny. Może się wówczas okazać, że w niektórych przypadkach realizacja pewnych postulatów stawianych przed systemami operacyjnymi jest szczególnie łatwa (lub trudna) właśnie ze względu na konkretną konstrukcję fizyczną danego komputera.

7.3

Klasyfikacja systemów operacyjnych

Systemy operacyjne można sklasyfikować ze względu na pewne cechy ich budowy, lub też ze względu na ich przeznaczenie. Poniżej przedstawiono podział ze względu na ich budowę: Jednoużytkownikowe, jednozadaniowe — Tego typu systemy umożliwiają efektywne wykonywanie tylko jednego programu przez jednego użytkownika. Przykładem systemu tej klasy jest DOS.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

7.3 Klasyfikacja systemów operacyjnych

191

U¯YTKOWNICY

Edytor tekstu Baza danych Arkusz kalkulacyjny OPROGRAMOWANIE

SYSTEM OPERACYJNY

SPRZÊT

Rysunek 7.1: Umiejscowienie Systemu Operacyjnego w systemie komputerowym.

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

192

System operacyjny

Jednoużytkownikowe, wielozadaniowe — Systemy operacyjne należące do tej grupy umożliwiają pojedyńczemu użytkownikowi wykonywanie równocześnie kilku programów. Na przykład pracując na komputerze z systemem Windows 95 mamy możliwość jednoczesnego pisania tekstu w edytorze, słuchania plików muzycznych oraz drukowania obrazka z programu graficznego. Wieloużytkownikowe — Systemy te pozwalają na równoczesną pracę wielu różnych użytkowników. Z punktu widzenia pojedynczej osoby, zachowują się jak wielozadaniowe systemy jednoużytkownikowe. Do tej grupy należą np. systemy wywodzące się z rodziny Unixa. Wśród systemów operacyjnych należy wyróżnić jeszcze dwie grupy, które ze względu na specyficzny charakter pracy trudno zakwalifikować do jednej z wyżej wymienionych grup, są to systemy czasu rzeczywistego i systemy rozproszone. Systemy czasu rzeczywistego są systemami o ściśle zdefiniowanych i określonych czasach realizacji konkretnych operacji. System gwarantuje, że zrealizuje pewną operację w założonym przedziale czasu w całości albo wcale. Jeśli z jakiś powodów nie uda się jej wykonać w tym terminie, zostanie wysłana wiadomość o tym fakcie, co daje szansę na podjęcie innych procedur postępowania. Można powiedzieć, że w tych systemach poprawność działań nie zależy tylko od ich poprawności w sensie prawidłowych wyników, ale także od czasu, w którym udało się je przeprowadzić (bądź nie). Przykładem z życia codziennego może być spóźnione przybycie na dworzec kolejowy — co prawda przybyliśmy, zatem zadanie zostało wykonane, ale niestety pociąg odjechał. Osiągnięcie terminowości realizacji zadań wymaga maksymalnego ograniczenia wszelkich możliwych opóźnień jakie powstają w systemie w związku z realizacją programów. Dlatego też systemy czasu rzeczywistego pozbawione są wielu rozwiązań spotykanych w innych nowoczesnych systemach operacyjnych. W szczególności, systemy rygorystycznego czasu rzeczywistego3 (ang. hard real-time system) pozostają w sprzeczności z koncepcją systemów wielozadaniowych. Jako przykład systemu czasu rzeczywistego posłużyć może QNX. Systemy rozproszone nadzorują pracę wielu różnych systemów komputerowych traktując je jak jeden duży komputer. Do najważniejszych powodów tworzenia takich systemów należą:
Inną odmianą systemów czasu rzeczywistego są systemy łagodne (ang. soft real-time system), w których to zakłada się jedynie, że zadanie mające priorytet zadania czasu rzeczywistego zostanie obsłużone przed innymi zadaniami.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004 3

7.4 Realizacja zadań

193

Podział zasobów — Daje możliwość wspólnego użytkowania pewnych zasobów w taki sposób jak byśmy byli w ich fizycznym posiadaniu. Dzięki temu możemy na przykład wydrukować pewne dane mimo, że drukarka stoi w innym pomieszczeniu. (Oczywiście można to również osiągnąć przy udziale klasycznej sieci komputerowej) Podział obciążenia — W sytuacji, gdy pewien system komputerowy w danej chwili jest mocno obciążony pracą, część zadań, bez ingerencji użytkownika, przenoszona jest na inny, gdzie wykonuje się jak w systemie pierwotnym. Zapewnienie niezawodności — W przypadku wystąpienia awarii, pewnego stanowiska zadania realizowane przez nie przejmują pozostałe. Dzieje się to w sposób niezauważalny dla użytkownika. Zapewnienie komunikacji — Wymiana informacji stanowi jedno z najistotniejszych zagadnień naszych czasów. Jednolity system daje użytkownikom dużą możliwość przesyłania plików, wiadomości czy prowadzenia videokonferencji. System Amoeba (zob. 7.10.1 opracowany przez Andrew Tanenbaum’a, to jeden z najczęściej podawanych, jako wzorzec, przykładów systemów tej rodziny.

7.4

Realizacja zadań

Przypomnijmy, że działanie komputera to nieprzerwane wykonywanie różnych instrukcji. Instrukcje te stanowią przetłumaczony na język maszyny sposób rozwiązania jakiegoś zagadnienia. Ciąg tych instrukcji realizujący zadanie nazywany jest programem. Gdy procesor rozpocznie wykonywanie instrukcji składających się na program, mówi się o procesie. Z każdym procesem wiążą się pewne dodatkowe informacje, które niezbędne są dla prawidłowego przebiegu wykonania programu. Między innymi są to: 1. Informacja o następnej instrukcji, która zostanie pobrana do wykonania przez procesor po obecnie przetwarzanej. Wskazuje ją licznik rozkazów będący komórką pamięci przechowującą adres następnej instrukcji. 2. Stan rejestrów procesora, zawierających najistotniejsze informacje dotyczące aktualnego przebiegu wykonywania instrukcji.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

194

System operacyjny

Tworzenie procesu

GOTOWY
Wywłaszczenie

AKTYWNY

Wywołanie funkcji O.S. Zamówienie zasobu

WSTRZYMANY

Rysunek 7.2: Stany procesu i przejścia pomiędzy nimi.

3. Stan pamięci i urządzeń wejścia/wyjścia. Przechowywane będą informacje o obszarach pamięci zajętych przez proces, a także realizowanych operacjach wejścia/wyjścia, przydzielonych urządzeń itp. 4. Stan procesu; każdy proces znajduje się w jednym z trzech stanów (rysunek 7.2)4 . • aktywny — są wykonywane instrukcje;

• gotowy — proces czeka na rozpoczęcie wykonania; ma przydzielone wszystkie zasoby oprócz procesora; • wstrzymany — proces czeka na wystąpienie jakiegoś zdarzenia, na przykład zakończenia operacji wejścia/wyjścia. Należy zwrócić uwagę na fakt, że w stanie aktywnym w każdej chwili może znajdować się co najwyżej tyle procesów ile procesorów jest w danym systemie. Nic jednak nie stoi na przeszkodzie, aby liczba procesów gotowych była większa. Również liczba procesów wstrzymanych nie jest (przynajmniej teoretycznie) ograniczona. Wymienione powyżej elementy jednoznacznie określają proces oraz stan w jakim się on w danym momencie znajduje i są niezbędne do jego prawidłowego wykonania. Dopóki system komputerowy realizuje tylko jeden konkretny program (przyjmijmy, że tylko jeden proces — ale pamiętajmy, że tak być nie musi!), dane te pozostają cały czas aktualne, bo tylko jeden proces jest aktywny. Jednakże w przypadku wielozadaniowych systemów wykonywanie tylko jednego jest sprzeczne z ich ideą. Co więcej założenie, że następny program możemy zacząć wykonywać dopiero z chwilą zakończenia poprzedniego to źródło potencjalnych przestojów i również w większości sytuacji przeczy to idei wielozadaniowości
W bardziej złożonych systemach może być więcej stanów, a także ich definicja może być inna.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004 4

¨¢ ¤¢ ¢¢¡   ¥  £  ¡
procesu

Zaszeregowanie

7.4 Realizacja zadań

195

a) CPU I/O b) CPU I/O c) CPU I/O d) CPU I/O

P1 P1 P2 P2 P1 P1 P1 P2 P1 P2

P1

P2 P1 P2 P2 P1 P2 P2 P2 P2 P2

Rysunek 7.3: Wykorzystanie procesora przez dwa procesy. Wykorzystanie procesora przez dwa procesy a) i b) w środowisku jednozadaniowym c) i wielozadaniowym d).

(mogłoby to sprawdzić się jedynie wtedy, kiedy działanie programu byłoby bardzo krótkie, co oczywiście jest nie realne). Ponadto prawie zawsze zachodzi konieczność odwołania się procesu do urządzeń wejścia/wyjścia w celu pobrania lub zapisania danych, i zwykle do zakończenia takiej operacji proces pozostaje wstrzymany; procesor w tym czasie nie robi nic. Rysunek 7.3 jest bardzo uproszczoną ilustracją sytuacji, gdy mamy dwa procesy A (rysunek 7.3 a) i B (rysunek 7.3 b) działające w środowisku systemu operacyjnego, gdzie mogą być one wykonywane jeden po drugim i tylko tak. Na proces A składają się następujące części: • obliczenia — trwające 3 jednostki czasu (TU, ang. Time Unit), • operacje we/wy — 6 TU, • ponownie obliczeń — 2 TU. Proces B składa się z: • obliczenia — 5 TU, • operacje we/wy — 2 TU, • obliczenia — 2 TU, • operacje we/wy — 2 TU.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

196

System operacyjny

Łączny czas wykonania obu procesów to 22 TU (rysunek 7.3 c)) co daje wykorzystanie procesora w 54%. Jeśli procesy te będą wykonywane w środowisku wielozadaniowym to w takiej sytuacji procesor nie będzie bezczynny w czasie realizacji zadań we/wy. Daje to dobre efekty — schematycznie przedstawiono to na rysunku 7.3 d (łączny czas wykonania 15 TU, wykorzystanie procesora 80%). Oczywiście należy pamiętać, że powyższa sytuacja jest wielkim uproszczeniem rzeczywistości, a pominięto w niej między innymi: • czas potrzebny na przełączanie się pomiędzy jednym a drugim procesem, • czas potrzebny dla systemu operacyjnego na nadzorowanie procesów, • założono, że operacje wejścia/wyjścia nie potrzebują w ogóle udziału procesora, co nigdy nie jest prawdą. W omawianej sytuacji rodzi się potrzeba funkcjonowania mechanizmów, które umożliwiałyby przełączanie miedzy procesami. Przełączanie, rozumiane tutaj jako ustawienie wszystkich niezbędnych struktur danych związanych z procesem na odpowiednie wartości i spowodowanie, że procesor rozpocznie realizację instrukcji związanych z innym programem, wybranym na podstawie pewnych założeń spośród oczekujących na wykonanie. Gdy tylko stanie się to możliwe, procesor powróci do poprzedniego programu. Zadanie to realizuje się właśnie poprzez system operacyjny. W swej najprostszej postaci odpowiada on za umieszczanie innych programów w pamięci, rozpoczęcie i nadzorowanie ich wykonania, przesyłanie i odbieranie danych.

7.5

W kierunku systemów wielozadaniowych

Przedstawiony na końcu poprzedniego punktu model pracy systemu operacyjnego, czyli „zdolność” przełączania się między programami nazywamy wieloprogramowością lub wielozadaniowością. Omawiając wielozadaniowość należy wspomnieć, że może ona być zrealizowana na dwa sposoby: bez wywłaszczania i z wywłaszczaniem. W obu przypadkach w rzeczywistości mamy do czynienia ze złudzeniem jednoczesności — ze złudzeniem, gdyż procesor nie może w tym samym momencie wykonywać kilku rozkazów realizujących różne programy. Jednoczesność polega na dzieleniu czasu pracy procesora pomiędzy wszystkie
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

7.6 Procesy, wątki, . . .

197

zadania (chwilowo zadanie utożsamiamy z procesem) i przydzielaniu każdemu z nich pewnego, bardzo krótkiego, odcinka czasu. W przypadku działania bez wywłaszczania system operacyjny mówi, który z procesów ma podjąć działanie, ale to ten proces musi oddać pałeczkę z powrotem do systemu operacyjnego. W tej sytuacji jak łatwo sobie wyobrazić, jeśli proces „nie odda” procesora to żaden inny proces (również system operacyjny) nie może działać. W konsekwencji komputer się „zawiesi”. Taka sytuacja miała miejsce w „duecie” Windows 3.11–DOS. Istotne jest to, że wówczas cały ciężar zapewnienia jednoczesności spoczywał na programiście. Każda aplikacja musiała być napisana w ten sposób, aby samodzielnie przekazać sterowanie do systemu. Innymi słowy, to program musiał wyrazić zgodę na wywłaszczenie i oddanie sterowania innemu programowi. Jeśli tego nie uczynił, system komputerowy cały czas wykonywał tylko jego kod, co użytkownik odbierał jako zawieszenie systemu, czyli utratę kontroli nad jego zachowaniem. W systemach z wywłaszczaniem, bez względu na to jak napiszemy program, czy tego chcemy, czy nie, zawsze po upływie przydzielonego fragmentu czasu zostanie on wstrzymany a rozpocznie się wykonanie następnego, wytypowanego przez system operacyjny. Dzięki temu realizowane są wszystkie zadania. Każdemu zadaniu przydzielany jest na tyle mały przedział czasu, że sprawia to wrażenie jednoczesności. W konsekwencji czas realizacji pojedyńczego zadania wzrośnie. Spowodowane jest to faktem przydzielenia tylko części mocy obliczeniowej. Jednakże w rzeczywistych zastosowaniach nie rzadko zdarza się, iż zadanie częściej oczekuje na wyniki operacji wejścia/wyjścia niż na wykonanie jego kodu (np. edytory tekstu), przez co w zupełności wystarczą mu te małe chwile czasowe jakie przydzieli system operacyjny.

7.6

Procesy, wątki, . . .

Na codzień terminy proces, wątek i zadanie używa się zwykle zamiennie, mając na myśli wykonywany ciąg instrukcji reprezentujący jakiś program. W pewnych sytuacjach rzeczywiście są to synonimy, jednak należy zdawać sobie sprawę, iż nie zawsze jest to prawdą. Co należy rozumieć pod pojęciami program i proces, powiedziane zostało na samym początku. Zdarzają się jednak sytuacje, w których jest porządane, aby wykonania pewnego programu było realizowane przez kilka procesów, które współużytkowałyby jakąś część zasobów. Takie procesy współdzielące kod, przestrzeń adresową i zasoby systemu operacyjnego nac 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

198

System operacyjny

zywa się wątkami, natomiast środowisko, w którym wątek działa — zadaniem. Pojedyńczy wątek, podobnie jak proces, powinien mieć własny stan rejestrów i stos. Proces równoważny jest zadaniu z jednym wątkiem. Konkretny wątek może być związany z dokładnie jednym zadaniem. Zadanie nic nie robi jeśli nie ma w nim ani jednego wątku.

7.7

Zarządzanie pamięcią

Podczas wykonywania programu jego kod oraz dane muszą znajdować się, przynajmniej częściowo, w pamięci głównej. Jeśli system jest jednozadaniowy, to całą wolną pamięć można przeznaczyć na potrzeby jednego, konkretnego programu (o ile oczywiście rozmiar programu nie przekracza rozmiaru wolnej pamięci). Sytuacja mocno komplikuje się w systemach wielozadaniowych, kiedy to w pamięci przechowuje się informacje związane z wieloma procesami. Zwykle wolny obszar pamięci głównej jest mniejszy niż łączne zapotrzebowanie programów. Wymaga to stworzenia mechanizmów zarządzających pamięcią i wbudowanie ich w system operacyjny. Istnieje wiele rozmaitych sposobów zarządzania pamięcią, a wybór konkretnych rozwiązań podyktowany jest w głównej mierze właściwościami sprzętowymi determinującymi ich efektywność.

7.8

System plików

Mechanizmy związane z zarządzaniem procesami i pamięcią pozostają zwykle niewidoczne dla użytkownika i raczej mało kto zdaje sobie sprawę z ich istnienia. Jednym z najlepiej widocznych przejawów działalności systemu operacyjnego jest system plików, a raczej jego obsługa. System plików to pewna logiczna hierarchia danych zdefiniowana niezależnie od fizycznych właściwości urządzenia na którym się znajduje, udostępnia ona logiczne jednostki magazynowania informacji, takie jak katalog czy plik. Informacje są gromadzone w plikach których zawartość określa ich twórca, nadając odpowiednie znaczenie ciągowi bajtów, którym faktycznie jest każdy plik. Pliki, dla łatwiejszego zarządzania nimi, pogrupowane są w katalogi tworzące hierarchiczną strukturę przypominającą drzewo. System plików porównać można z biblioteką, w której jest wiele różnych książek (plików). Oczywiście można je poukładać w kolejności alfabetycznej, łatwo będzie wówczas znaleźć jedną konkretną książkę. Jeśli jednak chcielibyśmy znaleźć wszystkie pozycje związane z bajkami, możemy natrafić na problemy. Dlac 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

7.9 Czy każdy komputer musi posiadać system operacyjny?

199

tego w wielu bibliotekach wyodrębnia się działy (odpowiednik katalogów) grupujące książki o wybranych tematykach (narzuconych przez bibliotekarza w celu łatwiejszego przeszukiwania zbiorów). Poniżej przedstawiono typowe informacje związane z każdym plikiem (pewne systemy operacyjne mogą nie mieć części z tych informacji, inne mogą dodawać odmienne)5 : nazwa pliku — służy do jednoznacznej identyfikacji pliku w danym katalogu. Nie mogą zatem w jednym katalogu istnieć dwa pliki o tej samej nazwie. Możliwe jest natomiast umieszczenie plików o identycznej nazwie w różnych katalogach. typ pliku — informacja dla systemu i/lub człowieka dotycząca sposobu interpretacji zawartości danego pliku; lokalizacja — określa urządzenie i umieszczenie pliku na tym urządzeniu; rozmiar — wyrażony w bajtach rozmiar pliku; atrybuty — informacje dodatkowe o pliku mówiące na przykład o tym, kto może go czytać, zmieniać, wykonywać itd. System plików stanie się faktycznie użyteczny, jeśli zostaną jeszcze zdefiniowane pewne, dostarczane przez system operacyjny, elementarne funkcje, które można na nim wykonać. Do podstawowych zaliczyć należy tworzenie pliku, zapis do pliku, odczyt z pliku, usuwanie pliku. Ponadto definiuje się pewną klasę operacji dodatkowych jak czytanie/pisanie od pewnej ściśle określonej pozycji w pliku czy zmiana jego atrybutów. W rzeczywistości wszystkie dane zapisane są, w jakiś sposób na fizycznym nośniku danych (użytkownik nie zna fizycznej organizacji tych danych). Gdyby nie system operacyjny, program musiałby pamiętać o ich położeniu i wzajemnych powiązaniach. To właśnie do systemu operacyjnego należy odwzorowywanie logicznej koncepcji pliku na fizyczne urządzenia pamięciowe.

7.9

Czy każdy komputer musi posiadać system operacyjny?

Z odpowiedzią na pytaniem postawione w tytule czekaliśmy na sam koniec, gdyż prowokuje ono do pewnej dyskusji, a ta możliwa jest gdy posiadamy
W systemie S/400 w ogóle nie ma hierarchii drzewiastej plików i katalogów, zamiast tego pliki są pamiętane w bazie danych.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004 5

200

System operacyjny

odpowiednią wiedzę, której zarys został przedstawiony w poprzednich punktach. Postaramy się przedstawić nasz punkt widzenia, mając nadzieję, iż każdy, opierając się na zdobytej wiedzy, wyrobi sobie swoje własne zdanie. W głównej mierze zależy to od charakteru pełnionych zadań. System operacyjny ma ułatwiać kontakty człowiek–maszyna i pozwolić na efektywne wykorzystanie sprzętu. Istotne jest to szczególnie w systemach wielozadaniowych, gdzie mamy do czynienia z mnóstwem dość złożonych zależności. Istnieją jednak systemy komputerowe wykonujące jeden konkretny program a interakcje z człowiekiem ogranicza się w ich przypadku do niezbędnego minimum. Taka sytuacja na przykład ma miejsce w przypadku mikrokontrolera sterującego pralką. Mikrokontroler, praktycznie rzecz biorąc, to bardzo mały system komputerowy — ma swoją jednostkę wykonawczą, różnego rodzaju pamięci, przetworniki i układy sterujące — wszystko to, co jego większy brat z biurka, tylko w mniejszej skali. Ze względu na sposób pracy, to znaczy stałe wykonywanie tego samego, pozostającego bez zmian, programu nie jest wymagana obecność systemu operacyjnego. Oczywiście w tym momencie, ktoś mógłby powiedzieć, iż program ten gospodarując zasobami mikrokontrolera w istocie stanowi sam dla siebie system operacyjny. Jednak z naszego punktu widzenia system operacyjny, to osobny, niezależny program (lub ich zbiór) wykonujący prace związane z zarządzaniem zasobami systemu na rzecz innych programów. Zatem w tym ujęciu program sterujący pralką wykonuje się bez udziału systemu operacyjnego. Ten typ oprogramowania nosi nazwę oprogramowanie wbudowane lub osadzone (ang. embedded system). Warto nadmienić, iż w chwili obecnej następuje dość dynamiczny rozwój układów mogących znaleźć zastosowanie w sterowaniu różnymi urządzeniami, jak choćby mikrofalówka czy pralka. Ich obecność w najnowszych produktach uzasadniana jest poszerzeniem zakresu funkcjonalności. Chodzi nam tutaj głównie o koncepcję „inteligentnego domu”, w którym to zainstalowane wyposażenie stara się dopomóc jego mieszkańcom. Tak więc, gdy skończy się mleko, lodówka automatycznie wysyła zamówienie do sklepu spożywczego; martwi się także o to, abyśmy mieli codziennie urozmaiconą dietę zestawiając różnorodne menu. Takie ”zachowanie” wymagać będzie rozbudowanego oprogramowania sterującego, a wówczas obecność systemu operacyjnego stanie się koniecznością. Sytuacja ta jest szczególnie dobrze widoczna na rynku telefonów komórkowych, które zaczynają pełnić coraz więcej różnorodnych funkcji, stając się malutkimi komputerami, aparatami cyfrowymi, organizatorami, salonami gier.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

7.10 Przykładowe systemy operacyjne

201

7.10

Przykładowe systemy operacyjne

W punkcie tym pokrótce zostaną wymienione pewne wybrane systemy operacyjne. Nie było naszym celem omówienie wszystkich znanych i mniej znanych, gdyż wymagałoby to osobnego opracowania. Staraliśmy się jednak wybrać te, które są znane i popularne oraz te, które może mniej znane, są wyznacznikami pewnego trendu lub typu systemów. Omawiane są one w kolejności alfabetycznej, by nie wyróżniać czy też sugerować ważności bądź jakości któregoś z nich.

7.10.1

Amoeba

Amoeba jest systemem znacznie różniącym się od pozostałych zaprezentowanych w tym opracowaniu. Koncepcje jakie legły u jego podstaw są jednak naturalną konsekwencją zmian zachodzących w świecie informatyki. W latach 70-tych i początkach 80-tych, królowały maszyny wieloużytkownikowe — na jednym komputerze mogło i często pracowało wiele osób. Postęp w takich gałęziach nauki i przemysłu jak chemia czy fizyka pozwolił na produkowanie coraz lepszych (bardziej wydajnych) i tańszych układów elektronicznych przy jednoczesnym spadku kosztów wytwarzania. Dzięki temu praktycznie każdy, kto tylko chciał, mógł posiadać komputer wyłacznie na swoje potrzeby; tak więc od połowy lat 80-tych do połowy lata 90-tych mamy do czynienia z sytuacją, gdy na jednym komputerze pracowała jedna osoba. Większą szybkość pracy komputera można osiągnąć na dwa sposoby: produkować wydajniejsze układy elektroniczne (co jest rozwiązaniem kosztownym i wymagającym czasu) lub łącząc wiele komputerów ze sobą w taki sposób aby „udawały” jeden. Drugie rozwiązanie jest znacznie tańsze a poza tym dostępne od zaraz. W drugim przypadku jest tylko jeden istotny szczegół — jak sprawić, aby wiele maszyn działało jak jedna? W poszukiwaniu odpowiedzi na to pytanie już w 1980 roku rozpoczyna pracę kierowany przez prof. Andrew S. Tanenbaum’a, z uczelni Vrije Universiteit (ang. Free University) z Amsterdamu, zespół badający rozproszone systemy komputerowe (ang. distributed computer system). Badania te, prowadzone także przy udziale Centrum voor Wiskunde en Informatica (ang. Centre for Mathematics and Computer Science), zaowocowały powstaniem nowego rozproszonego systemu operacyjnego nazwanego Amoeba (1983, V 1.0)6 . Z założenia Amoeba jest uniwersalnym (to znaczy nie przeznaczonym do wykonywania konkretnych zadań) systemem operacyjnym pozwalającym
6

Ostatnie oficjalne wydanie V 5.3 pochodzi z roku 1996.

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

202

System operacyjny

traktować zespół komputerów jak jeden dysponujący dużymi możliwościami przetwarzania7 . Pozwala to na wykonywanie programu na najlepiej do tego nadającej się maszynie lub wręcz równoległe wykonanie jego części, jeśli tylko istnieje taka możliwość8 . Użytkownik nie powinien (to znaczy nie musi) interesować się na jakim procesorze wykonywany jest jego program czy też gdzie faktycznie składowane są jego pliki; logując się do systemu 9 uzyskuje dostęp do wszystkich przydzielonych jemu zasobów 10 . Istotne jest, że maszyny pracujące pod kontrolą tego systemu nie muszą być oparte na tej samej architekturze i mogą być łączone za pomocą sieci LAN, a więc ich rozmieszczenie może być dosyć dowolne. Z punktu widzenia pojedynczej osoby całość zachowuje się jak jeden duży i bardzo wydajny system komputerowy.

7.10.2

Mac OS

Mac OS firmy Apple nierozłącznie kojarzony jest z graficznym interfejsem użytkownika — GUI (Graphical User Interface). Inspiracją do jego powstania, podobnie jak dla pozostałych systemów komunikujących się z otoczeniem głównie za pomocą obrazu i urządzenia wskazującego, była zbudowana w 1973 w laboratoriach firmy Xerox w Palo Alto maszyna Xerox Alto, która z czasem wyposażona została w pierwszy kompleksowy interfejs graficzny zrealizowany w komercyjnym produkcie Xerox Star (1981). Zaprezentowany w 1983 Apple Lisa znacznie rozszerzał powstałe tam koncepcje, czyniąc urządzenie atrakcyjnym dla potencjalanych nabywców. Niestety ze względu na cenę wynoszącą 10.000 USD (Xerox Alto 32.000 USD, Xerox Star 16.000 USD) oraz niewystarczającą wydajność nie osiągnął on większego sukcesu. Udało się to jednak jego następcy — Apple Macintosh (1984). Mac był od Lisy tańszy (2.500 USD), mniejszy, szybszy a przede wszystkim dużo bardziej przyjazny dla użytkownika. Wraz z komputerem wprowadzono system operacyjny dla niego przeznaczony — System 1. Zasoby komputera (pliki, programy itp) przedstawiano w nim pod postacią ikon, umieszczonych w oknach pozwalając tym samym na ich grupowanie.
7 Sama idea łączenia komputerów za pomocą sieci lub innego łącza jest jak pisaliśmy znana wcześniej. Jednak tradycyjne łączenie w tzw. cluster zapewniało współpracę jednak bez wspólnego systemu operacyjnego. Każdy z komputerów miał własny system i jedynie wykonywał zlecone zadania na rzecz kolektywu. 8 Mamy wówczas do czynienia z rozproszeniem (ang. distribution) i równoległością (ang. parallelism) przetwarzania co przekłada się na zwiększenie ogólnej wydajności. 9 Podkreślamy, do systemu a nie na konkretną maszynę! 10 Zachowanie takie określa się terminem przezroczystość (ang. transparency).

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

7.10 Przykładowe systemy operacyjne

203

Dodatkowe opcje udostępniane były przez system menu. Kontrolę nad tymi elementami (wybieranie, aktywacja, przemieszczanie itp.) sprawowano za pomocą jednoprzciskowego urządzenia wskazującego — myszy. Same zaś elementy rozmieszczono na wirtualnym biurku nazywanym pulpitem. Dążąc do maksymalnej intuicyjności pracy wprowadzono na przykład ikonę kosza — po przeciągnięciu na nią dowolnego pliku ulegał on skasowaniu. System operacyjny pozwalał na wykonywanie w tym samym czasie tylko jednego programu; nie było pamięci wirtualnej czy chronionej a także obsługi kolorów. Nie istniała możliwość zagnieżdżania katalogów (folderów ang. folders). Pliki przechowywane były na najwyższym poziomie hierarchii systemu plików (ang. root level of a disk); z każdym z nich skojarzona była jedynie nazwa folderu do którego należy plik. Mankament ten usunięto w wersji systemu znanej pod nazwą System 3, kiedy to hierarchiczny system plików HFS (Hierarchical File System), zastąpił używany do tej pory w wersji 1 i 2 MFS (Macintosh File System). Ponadto cały czas dodawano drobne zmiany w interfejsie czyniące pracę przyjemniejszą. Przykładem może być możliwość minimalizacji i maksymalizacji okienek. Kolejnym istotnym krokiem w rozwoju Mac OS był System 7 (1990). System ten w końcu stał się wielozadaniowy (we wcześniejszym (System 4) użytkownik decydował czy pracuje w trybie jedno czy wielozadaniowym). Wprowadzono 32 bitowe adresowanie oraz pamięć wirtualną. Razem z systemem zintegrowano obsługę sieci poprzez protokół AppleTalk oraz możliwość współdzielenia plików za pomocą AppleShare (wcześniej były to dodatkowe opcje). Pojawił się także mechanizm Drag and Drop oraz kolejne zmiany w interfejsie, jak choćby wykorzystanie możliwości oferowanych przez kolorowe monitory, w celu nadania bardziej przestrzennego wyglądu elementom ekranu. Coraz wyraźniej zarysowywała się jednak potrzeba opracowania nowego systemu operacyjnego i nie zmieniło tego faktu nawet pojawienie się w 1997 Mac OS 811 . Systemu wielowątkowego, z ulepszonym systemem plików HFS+, obsługą USB czy FireWire. . . ale wciąż za mocno związanego z poprzednimi wersjami, a więc co raz mniej odpowiadającego nowym trendom w rozwoju systemów operacyjnych. Nadzieją na zmiany, po upadku projektu Copland stał się zakup w 1997 firmy NeXT Steve’a Jobs’a (jednego z założycieli Apple Computer) 12 . W
11 Po raz pierwszy nazwy Mac OS użyto w stosunku do System 7.6 wydanego w styczniu 1997. 12 Pierwszy system NeXTSTEP wydany został w 1988 roku; bazował na mikrojądrze systemu Mach oraz elementach systemu BSD (4.3), a także oferował najbardziej zaawan-

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

204

System operacyjny

efekcie 13 września 2000 po raz pierwszy zaprezentowano Mac OS X Public Beta - zupełnie nowy system bazujący na koncepcjach NeXT 13 . Ostatnie lata to rozwijanie „drapieżnej” linii Mac OS: 2001 — Mac OS X 10.0 Cheetah, Mac OS X 10.1 Puma, 2002 — Mac OS X 10.2 Jaguar, 2003 — Mac OS X 10.3 Panther.

7.10.3

MS-DOS i Windows

Około roku 1980 rynkiem mikrokomputerów zainteresował się gigant przemysłu komputerowego firma IBM. Uwagę koncernu przyciągnęły nie tyle obroty firm zajmujących się mikrokomputerami (śmiesznie małe — 100 milionów dolarów w porównaniu do 28 miliardów IBM-a), ale bardzo dynamiczny rozwój tego rynku (roczny wzrost równy 70%). Szybko i w dość niekonwencjonalny sposób jak na ten koncern powstaje mikrokomputer oparty na 16-bitowym procesorze Intel 8088. Niestety tworzący go zespół nie miał doświadczenia w tworzeniu oprogramowania na tego typu urządzenia. Naturalną drogą stało się nawiązanie współpracy z twórcą bardzo popularnego już wówczas BASIC-a; tak IBM nawiązało pierwsze kontakty z Microsoftem — firmą Billa Gatesa. Po wstępnych rozmowach dotyczących stworzenia BASIC-a dla IBM, przyszedł czas na system operacyjny. Dla Microsoftu była to niesamowita szansa na zaistnienie na rynku. Terminy narzucone przez IBM były dość napięte i nie było mowy o tworzeniu systemu od podstaw. Gates zainteresował się wówczas stworzonym w Seattle Computer Products systemem QDOS o wielce obiecującym rozwinięciu tego skrótu Quick and Dirty Operating System (zmienionym później, grudzień 1980, na 86-DOS, (ang. Disk Operating System). W styczniu 1981 po raz pierwszy uruchomiono MS-DOS na prototypie komputera IBM. W czerwcu tegoż roku Microsoft stał się właścicielem wszystkich praw do DOS. W sierpniu 1981 światło dzienne ujrzał IBM 5150 PC wyposażony w procesor Intel 8088 taktowany zegarem 4.77 MHz, wyposażony w 65kB pamięci RAM, 40 kB ROM, napęd dysków elastycznych 5.25 cala pracujący pod kontrolą PC-DOS 1.0 (MS-DOS 1.0); cena całości — ok 3000 dolarów. Ponieważ praktycznie wszystkie programy pracujące do tej pory pod kontrolą rozpowszechnionego CP/M pracowały także pod kontrolą MS-DOS system bardzo szybko zdobył sobie zaufanie użytkowników. Ówczesny DOS zajmował 12kB pamięci i składał się z około 4000 linii kodu asemblerowego.
sowane GUI ze wszystkich dostępnych na rynku. 13 Jądro systemu znane pod nazwą Darwin faktycznie jest niezależnym, mogącym pracować samodzielnie, systemem operacyjnym zgodnym z rodziną systemów UNIX.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

7.10 Przykładowe systemy operacyjne

205

Nowością jak na tamte czasy była możliwość obsługi przez niego „minidyskietek” tj. 160kB jednostronnych dysków 5.25 cala (w odróżnieniu od 8-calowych ówczesnych „normalnych” dyskietek). Podobnie jak w CP/M także i tutaj nie było katalogów — wszystkie pliki znajdowały się w jednym miejscu. Zaczęto jednak przechowywać pewne dodatkowe informacje związane z plikami, jak choćby dokładny ich rozmiar, ulepszono algorytm przydziału miejsca na dysku a system jako całość działał szybciej niż pierwowzór. Istniała także możliwość wykonywania prostych skryptów powłoki. Kolejne lata to ciągłe usprawnianie i dodawanie nowych cech funkcjonalnych do systemu. O ile przez pierwsze lata swojego istnienia DOS okazał się całkiem udanym produktem, o tyle na początku lat 90-tych stało się jasne, że ze względu na ograniczenia — szczególnie dobrze widoczne przy porównaniu z systemami rodziny UNIX — należy zaprzestać dalszego jego rozwoju. Moment rozstania z DOS-em został nieznacznie przesunięty w czasie dzięki Windows 3.1 — nakładce na DOS pozwalającej na bardziej intuicyjną i przyjazną pracę, ale na horyzoncie widać było już Windows 95 — początek końca DOS-a. We wrześniu 1981 roku Microsoft rozpoczął prace nad czymś co nazwano Interface Manager. W projekcie tym wykorzystano efekty prac laboratorium Xerox-a z Palo Alto, gdzie po raz pierwszy pojawiła się idea interfejsu graficznego — GUI (ang. Graphical User Interface). Warto zauważyć, że w tamtych czasach GUI postrzegane było raczej w kategoriach ciekawostki niż przedsięwzięcia mającego przynieść poprawę w kontaktach człowiek – maszyna. Na wiosnę 1983 Microsoft ujawnił fakt prowadzenia prac nad swoją wersją GUI nazwaną Windows. W sierpniu 1985 pojawiła się Windows 1.0, który wówczas nie był niczym więcej niż nakładką na DOS-a (konkretnie MS-DOS 2.0). Operował jednak paletą 256 kolorów, pozwalał zmieniać rozmiar okien aplikacji (których poza dostarczonymi wraz z Windows takimi jak kalendarz, notatnik, zegarek, organizator nie powstało za wiele), minimalizować, maksymalizować i zmniejszać do ikony okna; nie było jednak możliwości nakładania się okien. Po prawie dwóch latach (kwiecień 1987) światło dzienne ujrzał Windows 2.0. Ta wersja zawierała wiele istotnych usprawnień: uwzględniono możliwości nowego procesora Intel 286, dodano wsparcie dla kart graficznych VGA, pamięci rozszerzonej, mechanizmów dynamicznej wymiany danych (DDE - ang. Dynamic Data Exchange); okna mogły być swobodnie przemieszczane po ekranie a użytkownik mógł korzystać z klawiatury w celu szybszego wykonywania pewnych operacji (tzw. skróty klawiszowe). Pojawiła się także wersja przeznaczona dla procesora Intel 386, oznaczona Windows 2.0/386, pozwalająca na jednoczesne uruc 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

206

System operacyjny

chamianie wielu aplikacji DOS-owych. Także ta wersja nie zyskała większej sympatii użytkowników aczkolwiek wielu programistów zaczęło tworzyć wówczas pierwsze aplikacje przeznaczone dla Windows (ewentualnie zaczęło poważnie rozważać taką możliwość). Wydany w maju 1990 roku Windows 3.0 mimo iż szybko zastąpiony wersją 3.1 przyniósł kolejne zmiany: wsparcie dla nowego procesora Intel 386 wykorzystujące jego 32-bitowe własności oraz zmienione środowisko tworzenia aplikacji (SDK ang. Software Development Kit) — teraz programista mógł skupić się wyłącznie na tworzeniu aplikacji w oparciu o dostarczone funkcje zamiast poświęcać część czasu na pisanie sterowników urządzeń. W kwietniu 1992 pojawił się Windows 3.1 dodające do cech znanych z wersji 3.0 obsługę multimediów — wsparcie dla urządzeń dźwiękowych, odtwarzania plików video oraz czcionek TrueType dzięki czemu edycja tekstu co raz bardziej zbliża się do idei WYSIWYG (ang. What You See Is What You Get), czyli założenia, że obraz na ekranie odzwierciedla dokładnie to, co otrzymujemy na wydruku. Windows w tej wersji (oraz wersji sieciowej Windows for Workgroups) sprzedał się w około 10 milionach egzemplarzy. W sierpniu 1993 na rynku pojawia się Windows NT 3.1, pomimo zbliżonej do Windows 3.1 nazwy i niewątpliwie wyglądu jest to zupełnie inny produkt. Stworzony z myślą o zastosowaniu w wysokowydajnych systemach serwerowych wyznaczał nowe kierunki, jeśli wziąć pod uwagę bezpieczeństwo, skalowalność czy wydajność. Sierpień 1995 przyniósł największe wydarzenie w historii Windows – wówczas ujrzała światło dzienne wersja nazywana Windows 95 (milion egzemplarzy w ciągu czterech pierwszych dni sprzedaży), która nie jest już jedynie nakładką na MS-DOS, ale pełnym, samodzielnym 32-bitowym systemem operacyjnym 14 . Z ważniejszych cech należy wymienić wsparcie dla obsługi stosu protokołów TCP/IP oraz Internetu, wprowadzenie obsługi mechanizmów Plug and Play oraz wiele zmian w wyglądzie i funkcjonalności interfejsu. Ważne jest także, że przesiadka z Windows 3.1 na Windows 95, czy później Windows 98, nie wiązała się z koniecznością drastycznej modernizacji sprzętu, co niestety w późniejszych wersjach staje się regułą. Wydany w lutym 2000 roku Windows 2000 to system mający zastąpić Windows 95, 98, NT na rynku zastosowań profesjonalnych. Październik 2001, wydany zostaje Windows XP, system mający w założeniu zatrzeć różnicę pomiędzy systemem profesjonalnym a jego domowym odpowiednikiem (biorąc pod uwagę najważniejsze cechy funkcjonalne).
Wprawdzie podczas ładowania systemu mamy do czynienia z DOS-em, konkretnie MS-DOS 7.0, to jednak Windows 95 po załadowaniu przejmuje całkowicie kontrolę nad systemem. Prawdą jest również, że zawierał on w sobie sporo kodu 16-bitowego.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004 14

7.10 Przykładowe systemy operacyjne

207

7.10.4

NetWare

We wczesnych latach 70-tych Novell Data Systems konstruuje wieloużytkownikowy system komputerowy złożony z jednostki głównej (zawierającej procesor, pamięci, napędy dyskowe, interfejsy do podłączenia urządzeń zewnętrznych takich jak na przykład drukarka) oraz terminali pozwalających na zdalną pracę na jednostce głównej. Całość pracowała pod kontrolą systemu operacyjnego bazującego na CP/M i Unix, i nosiła nazwę NOS (ang. Network Operating System). W tamtych czasach istniało wiele firm oferujących podobne rozwiązania. Na ich tle Novell wyróżnił się, i zdobył swoją pozycję, dokonując kilku nietypowych, jak na ówczesne czasy, wyborów dotyczących dalszego rozwoju: • Gdy tylko światło dzienne ujrzały pierwsze komputery IBM PC, Novell zaakceptował ich współistnienie w świecie komputerów, wychodząc z założenia, że system w którym „końcówki” nie są jedynie terminalami, ale pełnoprawnymi komputerami będzie działał sprawniej. • Nie traktował opracowanego NOS-a jako dodateku do sprzedawanego systemu, ale raczej skupia się na NOS-ie jako takim, oferując wsparcie dla wszystkich (oczywiście w ramach możliwości) istniejących systemów. W ten sposób omijały firmę „wojny sprzętowe”. Uniezależnienie się od sprzętu, a raczej wspieranie jego szerokiej gamy, powodowało, że bez względu na panujące w danym momencie trendy techniczne NOS Novell-a zawsze pozostawał do dyspozycji potencjalnych klientów. • Rozwinął ideę serwera plików zamiast powszechnej koncepcji serwera dysków, czyli przydziału użytkownikom partycji (określonych fragmentów przestrzeni dyskowej) do ich wyłącznej dyspozycji. Serwer plików w niedługim czasie zaczyna spełniać rolę nie tyle magazynu plików (ang. file repository), ale zdalnego systemu plików przez co serwer umożliwia sprawowanie kontroli nad tym kto i co robi z przechowywanymi plikami. Kolejne wersje NOS, począwszy od Advanced NetWare/286 powstałego dla komputerów PC/AT, przeznaczone są główne dla procesorów z rodziny Intel. Wraz z nadejściem w 1989 NetWare/386 (nazywanego też NetWare 3) rozwijana jest koncepcja modularności oprogramowania wchodzącego w skład systemu. Wydany w 1993 NetWare 4 oprócz dalszego rozwijania i podziału systemu na niezależne moduły, wprowadza NDS (ang. NetWare Directory Services, Novell Directory Services). NDS jest kompleksowym
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

208

System operacyjny

i wszechstronnym mechanizmem obsługi obiektowo zorientowanej bazy danych zawierającej informacje o wszelkich zasobach i obiektach w sieci. Teraz w ramach jednej sieci może funkcjonować wiele serwerów o czym użytkownik nie musi wiedzieć traktując system tak jak gdyby był tylko jeden serwer (centralne zarządzanie tożsamością i uprawnieniami). NetWare 4.10 i 4.11 oprócz kolejnych usprawnień i ułatwień, głównie natury administracyjnej (nowe i przyjaźniejsze oprogramowanie), stanowi zwrot w stronę technologi internetowych (obsługa stosu protokołów TCP/IP, mechanizm IPX/IP Gateway, NetWare/IP, Web Server). W NetWare 5 największą zmianą jest „prawdziwa” obsługa stosu protokołów TCP/IP. Owszem, wcześniej TCP/IP było obsługiwane, ale faktycznie cała komunikacja odbywała się w oparciu o stos IPX/SPX. Ponadto dodano wsparcie dla maszyn wieloprocesorowych a także umożliwiono wymianę podzespołów systemu podczas jego pracy (ang. hot-swap). W miejsce istniejącego do tej pory systemu plików pojawia się nowy, nazywany NSS (ang. Novell Storage Services). Trudności związane z nadzorowaniem indywidualnych stacji wchodzących w skład sieci ma pomagać rozwiązywać ZENworks (ang. Zero Effort Networking), a proces drukowanie dokumentów usprawniać NDPS (ang. Novell Distributed Print Services). Dla wygody administratora w końcu pojawia się pracujące na serwerze GUI. Oferowane oprogramowanie umożliwia zbudowanie kompletnego serwera sieciowego do zastosowań e-commerce. NetWare 6 to przede wszystkim większa otwartość na użytkownika, rozumiana tutaj jako umożliwienie dostępu do wszelkich zasobów sieci (pliki, drukarki, bazy danych). Wsparcie dla pracy grupowej poprzez: pocztę elektroniczną, terminarze, kalendarze itp. bez względu na rodzaj sieci, platformy na której działa serwer czy urządzenie klienta (wystarczy dowolne urządzenie umożliwiające dostęp do Internetu jak choćby telefon komórkowy czy palmtop).

7.10.5

OS/390

System operacyjny OS/390 firmy IBM stworzony został z myślą o produkowanych przez nią komputerach typu mainframe serii System/370 (1970 rok), który był następcą linii System/360 15 ). A docelowo miał być sercem
Liczba 360 w nazwie miała symbolizować cały zakres zapotrzebowań jakie była w stanie pokryć rodzina maszyn tej serii. Podkreślać miała uniwersalność zastosowań oraz dostępność dla szerokiego kręgu odbiorców o różnych potrzebach i możliwościach finansowych. Stąd 360 — czyli 360 stopni kątowych. Drugie źródło tłumaczy nazwę jako pochodzącą od lat 60-tych i następne od kolejnych dekad ubiegłego wieku.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004 15

7.10 Przykładowe systemy operacyjne

209

komputerów System/390. W prostej linii pochodzi on od systemu MVS, do którego dodano pewne usługi systemów typu Unix. Po raz pierwszy pojawił się w 1995 roku a obecnie, po wprowadzeniu wsparcia dla maszyn 64 bitowych, występuje pod nazwą z/OS i jest przeznaczony dla maszyn zSeries. MVS (ang. Multiple Virtual Storage) po raz pierwszy wydany został w 1974 roku. Stanowił on rozwinięcie długiej listy systemów operacyjnych przeznaczonych dla maszyn System/360. Pierwszy z nich to PCP (ang. Primary Control Program) — system, który nie wspierał wielozadaniowości; drugi to MVT (ang. Multitasking with a Variable number of Tasks) zawierał już wsparcie dla wielozadaniowości. Kolejny system — SVS (ang. Single Virtual Storage) wykorzystywał pojedynczą (jedną dla wszystkich zadań) wirtualną przestrzeń adresową. MVS, który był rozwinięciem cech poprzedników, pozwalał aby każde z wielu zadań posiadało swoją własną przestrzeń adresową. Zasadnicza interakcja z systemem odbywała się za pomocą języka JCL (ang. Job Control Language) oraz TSO (ang. Time Sharing Option). JCL jest językiem pozwalającym na uruchamianie programów w trybie wsadowym (ang. batch mode), czyli bez interakcji z użytkownikiem. Określano w nim jakie operacje były do wykonania, na jakich danych oraz jakie zasoby komputera dla nich przeznaczono. Od momentu uruchomienia takiego zadania, aż do zakończenia nie wymagało ono interwencji operatora systemu. Naturalnym uzupełnieniem języka JCL był TSO, który pozwalał na interaktywną pracę — na przykład operację na plikach czy nadzorowanie wykonania zadań wsadowych. Jeśli by chcieć porównywać systemy operacyjne tak jak samochody, to MVS byłby osiemnastokołową ciężarówką. Takiego pojazdu nie używa się ze względu na jego szybkość, ale po to aby mieć pewność, że uda się możliwie najbezpieczniej przewieźć bardzo duży ładunek 16 . Mówiąc o mocy komputerów typu mainframe, musimy zdawać sobie sprawę, że jest to coś zupełnie innego niż moc superkomputerów. Te drugie zoptymalizowane są pod kątem jak najszybszego wykonywania bardzo dużej liczby operacji arytmetycznych. Mainframe to z kolei maszyna zdolna do wykonania niewielkiej ilości prostych operacji na ogromnej ilości danych (liczby rzędu setek tysięcy nie są tu rzadkością). W tym przypadku nacisk kładziony jest na optymalizację operacji wejścia/wyjścia, które to w znacznym stopniu stanowić będą ramy określające czas potrzebny na przeprowadzenie operacji. Drugim ważnym aspektem, który brano pod uwagę przy projektowaniu systemów klasy mainframe i oczywiście systemów operacyjnych je wspierających, to niezawod16

Zaczerpnięte z [DuC01].

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

210

System operacyjny

ność. Projektowano je do profesjonalnych zastosowań, gdzie najmniejszy błąd mógł kosztować setki tysięcy dolarów, czy też ludzkie życie. Dlatego też systemy te do dzisiejszego dnia są wykorzystywane przez banki czy inne organizacje, dla których pewność rozwiązania jest najważniejsza.

7.10.6

OS/400

Historia OS/400 sięga swymi korzeniami daleko w przeszłość. Wprawdzie system ten pojawił się w 1988 jednak był on konsekwencją działań podejmowanych od końca lat 60-tych. W roku 1961 DEC (Digital Equipment Corporation) wprowadza na rynek komputer PDP-1 (Programmed Data Processor 1), postrzegany przez wielu jako pierwsze urządzenie nadające się do zastosowań komercyjnych. W dość krótkim czasie liczne grono powstałych na jego bazie konstrukcji rozwojowych udowodniło, że rynek niewielkich (i relatywnie tanich) komputerów (nazywanych minikomputerami dla odróżnienia od „prawdziwych” komputerów 17 ) może być atrakcyjny nawet dla takich gigantów jak IBM. IBM wkracza na rynek minikomputerów w roku 1969 przedstawiając System/3. W sześć lat później pojawia się pierwszy przedstawiciel rodziny System/3X — System/32. Niestety zarówno on, jak i wprowadzony w 1977 roku System/34 nie zdobyły większego zainteresowania ze strony potencjalnych klientów. Dopiero pochodzący z 1978 System/38 a w szczególności wprowadzony w 1983 System/36 zyskują uznanie w oczach odbiorców — każdy z innych powodów. System/36 – głównie za sprawą przyjaznego interfejsu i łatwości obsługi. System/38 był natomiast zaawansowanym technicznie urządzeniem, w którym wcielono w życie tak rewolucyjne pojęcia jak obiekt czy enkapsulacja danych a w szczególności maszyna wirtualna. Pomysł polegał na uniezależnieniu oprogramowania od fizycznie istniejących komponentów. Osiągnięto to wprowadzając interfejs programistyczny znany niegdyś pod nazwą MI (ang. Machine Interface), a obecnie jako TIMI (ang. Technology Independent Machine Interface). Użytkownik/programista mógł komunikować się ze sprzętem tylko i wyłącznie przez ten interfejs; nie miał żadnej możliwości wglądu w to co dzieje się pod nim. Tworzone oprogramowanie w pierwszej fazie przekształcane było do kodu mogącego wykonywać się właśnie na takiej abstrakcyjnej maszynie. Następnie za pośrednictwem interfejsu, bez ingerencji człowieka, przekształcane było do postaci nadającej się do wykonania na maszynie rzeczywistej. Połączenie technicznych koncepcji System/38 z łatwością użytkowania System/36 zaowocowało powstaniem w 1988 komputera AS/400 (ang. AS
17

Które w tamtych czasach zajmowały pokaźne powierzchnie.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

7.10 Przykładowe systemy operacyjne

211

— Application System, pierwotnie Advanced System). Wraz z komputerem powstał też i system operacyjny pozwalający na pracę z nim — OS/400. OS/400 traktowany jest w AS/400 jak zwykły program użytkownika w tym sensie, że również on musi uzyskiwać dostęp do systemu za pośrednictwem MI. Jednakże część usług przypisywanych systemowi operacyjnemu jak choćby zarządzanie procesami czy pamięcią musi mieć dostęp do fizycznie istniejących urządzeń. Tą część OS-a zdefiniowano poniżej warstwy MI udostępniając, jeśli to konieczne, jedynie odpowiedni interfejs do komunikacji z częścią OS-a leżącą powyżej MI. Jako, że wszystko co jest poniżej MI ukryte jest przed światem zewnętrznym, praktycznie nie ma możliwości zastąpienia oryginalnego OS/400 innym systemem operacyjnym lub wykorzystania go na innej platformie sprzętowej. Stąd też nazwy OS/400 i AS/400 używane są przez wielu jak synonimy, choć nimi nie są.

7.10.7

Unix i rodzina

Jak to napisano we wstępie w 1965 roku trzy organizacje Bell Telephone Laboratories, General Electric Company oraz MIT, w ramach projektu MAC. próbowały stworzyć system Multics. Ponieważ jednak projekt nie przynosił spodziewanych efektów Bell odstąpił od udziału w projekcie i podjął własne wysiłki skonstruowania systemu wspierającego pracę wieloużytkownikową, zapewniającego współdzielenie zasobów i udostępnianie mocy obliczeniowej. W 1971 roku w wydziale patentowym Bell Laboratories zainstalowano pierwszą wersję systemu Unix18 . System ten cechował się małymi wymogami, zaledwie 16KB dla samego systemu i 8KB dla programów użytkowych, obsługiwał dysk o pojemności 512KB oraz pozwalał na utworzenie pliku nie przekraczającego 64KB. W krótkim czasie zostaje wymyślony i zaimplementowany w tym systemie nowy język programowania C, zaś w 1973 roku w tym języku właśnie przepisany zostaje cały system operacyjny. Ponieważ firma w której powstawał Unix ze względu na pewne zobowiązania wobec rządu federalnego nie miała prawa sprzedawać komputerów oddała Unix Uniwersytetom, które zaczęły wykorzystywać go do celów dydaktycznych. W 1977 roku liczba instalacji Unixa wynosi 500, z czego 125 było na uniwersytetach. Przez lata system ten był rozwijany zarówno wewnątrz AT&T jak i na Uniwersytecie Kalifornijskim w Berkeley. Stąd powstały wtedy niejako dwie gałęzie: jedna nazwana System V i wspierana przez AT&T oraz druga 4.3 BSD wspierana przez uniwersytety. W chwili obecnej istnieje roŹródła podają, że autorem nazwy Unix, która była kalamburem słowa Multics, był Brian Kernighan.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004 18

212

System operacyjny

dzina trzech systemów z serii BSD: FreeBSD, OpenBSD i NetBSD, które są dostępne za darmo. Na początku 1984 roku na świecie odnotowano około 100000 instalacji systemu Unix, co więcej były to instalacje na przeróżnych komputerach i procesorach, od małych jak na tamte czasy, do dużych maszyn. W tamtym okresie żaden system nie mógł poszczycić się taką przenoszalnością, którą Unix posiadał dzięki kilku cechom. Po pierwsze, był napisany w języku wysokiego poziomu C, dzięki czemu przenoszenie kodu nie sprawiało tylu trudności co przenoszenie w asemblerze na inny mikroprocesor. Co prawda nie wystarczyło na nowym komputerze posiadającym kompilator języka C przekompilować cały system. Wymagało to często sporego wysiłku dostosowywania do innej architektury, jednak nie był to proces tak żmudny jak przepisywanie ręczne programów w asemblerze. Po drugie, system Unix posiadał prosty interfejs, oraz zawierał wiele narzędzi do budowy programów. Po trzecie, posiadał spójny i hierarchiczny system plików, oraz dostarczał prostych funkcji do operowania na nich, nie zależnych od fizycznego położenia pliku. Po czwarte był systemem wielodostępnym i wieloprocesorowym, co więcej izolował poprzez warstwę abstrakcji użytkownika od architektury sprzętowej. W tamtym czasach żaden inny system nie mógł poszczycić się powyższymi cechami oraz taką popularnością. W chwili obecnej trudno mówić o systemie Unix jako takim, istnieje cała rodzina systemów Unixo podobnych, kompatybilnych z nim w mniejszym lub większym stopniu. Aby trochę ogarnąć ten chaos zaproponowano wiele standardów, jednym z nich jest POSIX i obecnie większość szanujących się systemów Unixowych jest zgodna z tym właśnie standardem wprost lub też przez emulację. Jedną z najpopularniejszych obecnie „odmian” Unixa jest Linux. System ten został napisany w zgodzie ze standardem POSIX, a tam gdzie POSIX nie precyzuje rozwiązań, Linux naśladuje System V. Co więcej w celu zwiększenia uniwersalności system ten zawiera biblioteki pozwalające emulować działania BSD. Istnieją co prawda wyjątki, gdyż system podsieci został zupełnie przeniesiony z BSD i jest oparty na idei gniazd. System Linux został zaprojektowany w 1991 roku przez młodego studenta Uniwersytetu w Helsinkach Linusa Torvaldsa. Linus pisząc swój system oparł się na innym Unixo podonym systemie Minix, jednak napisał go od nowa i udostępnił na prawach licencji GPL. Licencja GPL jest tak skonstruowana, że kod przez nią udostępniany jest wolny w sensie wolności dystrybucji i otwartości źródeł19 . Spowodowało to, że cały świat internetu miał dostęp do źródeł systemu Unixo podobnego, co spotkało się z dużym zainteresowaniem. W ciągu kilku zaledwie lat system ten obrósł dodatkowymi programami i bibliote19

Nie oznacza to automatycznie, że jest on za darmo
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

7.11 Zadania

213

kami, by w chwili obecnej stać się jednym z najpopularniejszych na świecie, zwłaszcza w kręgach ludzi młodych oraz środowiskach akademickich. W dniu dzisiejszym jest on jednym z najchętniej wybieranych systemów serwerowych, oraz coraz częściej wykorzystywany przez programistów z racji bardzo dużej stabilności pracy i ogromnej ilości narzędzi programistycznych właśnie.

7.11

Zadania

1. Wymień główne zadania stawiane systemowi operacyjnemu. 2. Porównaj systemy jedno i wielozadaniowe. 3. Porównaj systemy jedno i wieloużytkownikowe (wielodostępowe). 4. Wymień co najmniej dwa współczesne systemy operacyjne i spróbuj je scharakteryzować.

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

214

System operacyjny

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

Rozdział 8

Sieci komputerowe
A wtedy z chaosu odezwał się głos i powiedział: „Bądź cicho, mogło być gorzej.” Byłem cicho i pogorszyło się. Nieznany administrator sieci

8.1

Po co mi ona?

Jedną z najcenniejszych rzeczy w dzisiejszym świecie jest informacja. Skonstruowanie pierwszych komputerów w połowie XX wieku pozwoliło na niespotykane przyspieszenie przetwarzania danych. Komputery najpierw powoli, głównie za sprawą swojej ceny i skomplikowanej obsługi, z czasem coraz szybciej znajdowały zastosowanie w najprzeróżniejszych dziedzinach naszej egzystencji. Wraz ze wzrostem ich ilości, problemy zaczęła sprawiać wymiana danych pomiędzy nimi. Coraz więcej urządzeń musiało dysponować bądź takimi samymi danymi, bądź pojawiały się komputery, które posiadały informację użyteczną dla innych. W tej sytuacji potrzebne dane trzeba było wciąż kopiować na taśmę lub dyskietkę, przenosić i wgrywać w nowym miejscu. Rozwiązanie takie nie należało do najbezpieczniejszych, głównie za sprawą podatności na utratę zapisanych danych i przypadkowe zniszczenia fizyczne a także potencjalną możliwość uzyskania do nich dostępu przez osoby do tego nieuprawnione. Ponieważ zarówno szybkość przetwarzania jak i możliwe pola zastosowań ulegały ciągłemu zwiększaniu, aktualizacje zgromadzonych i przetwarzanych informacji musiały być coraz częstsze. W tej sytuacji najefektywniejszą metodą, zarówno pod względem szybkości jak i niezawodności, przenoszenia danych stało się połączenie komputerów za pomocą przewodu. Z czasem, mimo związanych z tym kosztów i wzrostu
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

216

Sieci komputerowe

stopnia komplikacji, łączono ze sobą coraz więcej systemów, gdyż korzyści jakie dzięki temu osiągano, przeważały poniesione nakłady. Dzięki takiemu rozwiązaniu stało się możliwe współużytkowanie a raczej współdzielenie danych; wystarczyło, że składowane są one na jednym komputerze a pozostałe, jeśli ich potrzebują, mają możliwość szybkiego ich otrzymania, dzięki istnieniu bezpośredniego, pracującego bez ingerencji człowieka, systemu ich przekazywania. Ponieważ w takiej sytuacji informacje przechowywane są w jednym miejscu, zatem ekonomicznie uzasadnione staje się konstruowanie wyspecjalizowanych systemów przeznaczonych do ich zarządzania, kontrolowania i ochrony — powstają bazy danych, serwery FTP, serwery WWW, serwery poczty itd. Kolejne ważne zagadnienie to zwiększenie niezawodności i funkcjonalnej dostępności. W sytuacji, gdy wiele komputerów połączonych jest ze sobą i nastąpi awaria jednego lub nawet kilku z nich, ich funkcje przejmują automatycznie jednostki sprawne. Jeśli z kolei użytkownik prosi o pewne dane znajdujące się na kilku komputerach, to otrzyma je od tego, który w danym momencie może zrobić to najszybciej skracając tym samym czas dostępu do danych.

8.2

Struktura fizyczna sieci

Charakteryzując fizyczną strukturę jakiekolwiek sieć komputerowej podajemy zwykle jej zasięg i topologię.

8.2.1

Zasięg sieci

Pod względem zasięgu zasadniczo wyróżniamy sieci typu LAN, WAN i MAN, które pokrótce można scharakteryzować w następujący sposób: LAN — (ang. local area network) sieć lokalna łączy ze sobą urządzenia sieciowe znajdujące się w „niewielkiej” odległości. Termin „niewielkiej” równie dobrze może oznaczać 1 metr — gdy łączymy się z komputerem kolegi stojącym na biurku obok, jak i 200 metrów — gdy łączymy się z komputerem znajdującym się w sąsiednim budynku. Zawsze pozostanie to jednak ograniczony obszar, niewielki (wręcz punktowy) w skali kraju czy świata. WAN — (ang. wide area network) sieć rozległa to, oprócz dużo większego zasięgu niż sieci LAN, skalowalność rozwiązania. Sieci tego typu muszą umożliwiać rozbudowę w miarę potrzeb, przy założeniu łączenia wielu
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

8.2 Struktura fizyczna sieci

217

węzłów rozmieszczonych w znacznych odległościach od siebie. Węzeł to zarówno jeden komputer, jak i równie dobrze cała sieć LAN. Sieci WAN integrują wiele różnorodnych technologii sieciowych w jeden system umożliwiający wymianę informacji bez względu na występujące różnice sprzętowe, programowe i logiczne. MAN — (ang. metropolitan area network) sieć miejska jest czymś pośrednim między siecią lokalną a rozległą. Jeszcze 10 lat temu wyraźnie można było powiedzieć, która sieć jest siecią miejską. Obecnie jest to trudne, gdyż sieci miejskie są włączane w ogólny szkielet Internetu, czyli sieć rozległą, co powoduje, że stają się jego częścią.

8.2.2

Topologia sieci

Mówiąc o topologii sieci mamy na myśli jej niejako ułożenie fizyczne, określające sposób połączenia wchodzących w jej skład urządzeń. I tak zasadniczo wyróżnia się trzy topologie występujące w sieciach LAN oraz jedną dodatkową stosowaną w MAN i WAN. Topologia magistrali — uzyskuje się ją łącząc wszystkie urządzenia za pomocą pojedynczego kabla (rysunek 8.1). W chwili obecnej realizowane jest to jedynie za pomocą kabla koncentrycznego (zob. 8.5.2). Obecnie jest to technologia wymierająca, z racji dużej awaryjności oraz kłopotliwości w serwisowaniu. Topologia pierścienia — charakteryzuje się tworzeniem zamkniętego połączenia (rysunek 8.2). Każdy komputer łączy się ze swoimi dwoma najbliższymi sąsiadami i tylko z nimi wymienia on bezpośrednio informacje. Topologia gwiazdy wszystkie węzły łączą się w jednym punkcie (rysunek 8.3). Topologia oczek pełnych — tutaj każdy węzeł połączony jest ze wszystkimi pozostałymi (rysunek 8.4). Otrzymujemy w ten sposób najbardziej niezawodną i odporną na uszkodzenia fizyczne konfigurację. Oczywistą wadę takiego rozwiązania stanowi szybki wzrost liczby połączeń, pociągający za sobą wzrost stopnia komplikacji i kosztów, wraz ze wzrostem ilości obsługiwanych węzłów. Topologia mieszana — stanowi dowolne połączenie powyższych rozwiązań, wbrew pozorom jest dość często stosowana. Przykładowo w firmie
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

218

Sieci komputerowe

Rysunek 8.1: Topologia magistrali.

istnieje topologia magistrali a zarząd nie chce podjąć decyzji o przebudowie istniejącej infrastruktury a jednocześnie chce podłączyć kolejne komputery, wtedy nowe będą np. w topologii gwiazdy. Mówiąc o topologii pamiętajmy, aby nie mylić organizacji fizycznej i logicznej sieci. Fizyczna organizacja to właśnie powyższe modele, które można zobaczyć gołym okiem albo dotknąć, jeśli ktoś będzie miał takie życzenie. Organizacja logiczna wykorzystuje natomiast fizycznie istniejące połączenia do zorganizowania przesyłania danych. Organizacja ta jest całkiem niezależna od organizacji fizycznej. Dlatego nic nie stoi na przeszkodzie, aby istniała sieć o fizycznej strukturze gwiazdy działająca na (logicznej) zasadzie pierścienia.

8.3

Architektura sieci

Ostatnią z cech, jaką należy zasygnalizować jest architektura sieci. Niesie ona ze sobą informację o konfiguracji urządzeń podłączanych do jednej z wcześniej omawianych topologii. Tu wymienia się głównie architekturę klient-serwer (zob. 8.3.2) oraz równorzędną (zob. 8.3.1). W celu lepszego przybliżenia idei powyższych, wprowadzimy wpierw terminy klient i
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

©§ © §§§¨ §¦ ¨ §¨ §¨ §¦ ¦ ¦ ¦    ¡ §  § § §      §§§ § ¢ ¢£ £ §  § § §      §§§ § ¤ ¤¥ ¥

¡

8.3 Architektura sieci

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

$ §$ §$ §$ § # # # # & §§§§$ # % $ §$ §$ §$ § § % §§§§$ # # # # #

Rysunek 8.2: Topologia pierścienia.

Rysunek 8.3: Topologia gwiazdy.

)§ ( §( §( §' ' ' ' 0 ) §§§( §' ! "!" 

§  §§§ §  § § §    ¤¤ ¥¥

¨ §¨ §¨ §¦ ¦ ¦ ¦  §§§¨ §¦ ©§ ¡  © §§§¨ §¦ ¨ §¨ §¨ §¦ ¦ ¦ ¦ ¡ §  §§§ §  § § §    ¢ ¢£ £

219

220

Sieci komputerowe

Rysunek 8.4: Topologia oczek pełnych.

serwer. Serwerem nazywany jest dowolny komputer przyłączony do sieci i udostępniający swoje zasoby innym urządzeniom. Poprzez zasób rozumiemy tutaj zarówno dysk, pamięć, czy też moc obliczeniową. Na ogół sposób pracy i konstrukcja fizyczna serwera zoptymalizowane są pod kątem wykonywania określonej funkcji. Nie zapominajmy jednak, iż „serwer” nie musi oznaczać osobnego komputera; kilka serwerów może funkcjonować w ramach jednej fizycznej maszyny, wtedy też mówi się nie o serwerze a usłudze lub usługach jakie udostępnia. Ze względu na sposób pracy wyróżnia się zwykle: Serwer plików służy do składowania plików i ich udostępniania użytkownikom sieci. Mechanizm centralnego przechowywania plików daje łatwiejszą kontrolę nad tworzonymi dokumentami. Teraz każdy zamiast „osobistej” wersji pewnych plików przechowywanych na swoim komputerze korzysta z tych zapisanych na serwerze. Gdy nastąpi jego zmiana, każdy przy następnym jego pobraniu automatycznie otrzyma uaktualnioną wersję. Uwalnia nas to od konieczności przeglądania wszystkich potencjalnych miejsc przechowywania i ciągłego kontrolowania zachodzących zmian. Kolejną niezaprzeczalna zaletą jest uproszczenie tworzenia kopii zapasowych, które są niezbędne do zapewnienia
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004 

¥ ¥ ¥ ¥ © © © ©  ¥¥¥¥ ©   ¥ ¥ ¥ ¥ ¥¢£¢  ¥¥¥¥ © © © © © £

¦ ¥¦ ¥¦ ¥¦ ¥ ¤ ¤ ¤ ¤ ¨ ¥¥¥¥¦ ¤ § ¦ ¥¦ ¥¦ ¥¦ ¥ ¥ ¡  § ¥¥¥¥¦ ¤ ¤ ¤ ¤ ¤ ¡

8.3 Architektura sieci

221

bezpieczeństwa w razie awarii. Dość łatwo można wykonać kopię zasobów jednego komputera (tego na którym wszyscy przechowują pliki) podczas gdy dla większej ich ilości wymagany jest większy nakład pracy, sprawna organizacja i co najważniejsze pilnowanie użytkowników, by w ogóle wykonywali te kopie. Serwer aplikacji jest miejscem wykonywania (uruchamiania) programów wykonywalnych. Jego istotą jest właśnie to, że programy uruchamiane są na tymże serwerze a nie na komputerze użytkownika, co miałoby miejsce gdyby były przechowywane na serwerze plików. Innymi słowy serwer aplikacji udostępnia swoje zasoby poprzez bezpośrednie wykonywanie zainstalowanych na nim programów na rzecz odległego klienta. Stosowanie serwerów aplikacji daje sposobność wygodnego kontrolowania używanego oprogramowania. Serwer wydruków. Pomimo, iż żyjemy w epoce komputerów i cyfrowej informacji, nadal często zachodzi potrzeba sporządzenia dokumentów w postaci zadrukowanej kartki papieru. W tym celu należy zapewnić użytkownikom dostęp do odpowiednich urządzeń. Indywidualna drukarka dla każdego, to rozwiązanie nieuzasadnione ekonomicznie. Zwykle ilość drukowanych stron to kilka, kilkadziesiąt miesięcznie. W takiej sytuacji dużo lepiej zainwestować w serwer wydruków, który wszystkim użytkownikom sieci udostępnia podłączone do niego drukarki, których jest znacznie mniej niż potencjalnych użytkowników. Przyjmuje on zlecenia druku od wszystkich urządzeń w sieci, ustawia je w kolejkę i kieruje do odpowiedniej drukarki. Dzięki temu 50 komputerów może korzystać z 2 drukarek, praktycznie w taki sam sposób jakby stały one tuż obok każdego stanowiska. Jedyna niedogodność, to konieczność „przespacerowania się” po gotowe wydruki do miejsca gdzie stoją drukarki. Serwer bazy danych jest miejscem przechowywania danych. Istotnym czynnikiem odróżniającym go od serwera plików, na którym również można składować dane, jest zdolność do zarządzania, kontroli i przetwarzania przechowywanych informacji. Korzystając z serwera plików wysyłamy do niego lub otrzymujemy plik, którego zawartości nadajemy znaczenie dopiero my sami. Natomiast między użytkownikiem a serwerem baz danych przesyłana jest tylko konkretna informacja. Załóżmy, że zapisujemy codziennie wielkość naszego majątku oraz ile i na co wydaliśmy naszych zasobów pieniężnych. Po pewnym czasie chcąc
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

222

Sieci komputerowe

dowiedzieć się kiedy wydaliśmy więcej niż 100 złotych w przypadku serwera plików pobieramy plik z wydatkami i przeglądamy go ręcznie w poszukiwaniu żądanych pozycji. Korzystając natomiast z usług serwera baz danych, wysyłamy do niego zapytanie, które nieformalnie brzmieć może: „Podaj mi te dane, w których dziennywydatek > 100”. Serwer odpowiada na nasze zapytanie przesyłając nam tylko poszukiwane informacje. Serwer poczty umożliwia użytkownikom wymianę informacji na zasadach zbliżonych do funkcjonowania poczty tradycyjnej. Serwer udostępnia swoje usługi w postaci wysyłania lub odbierania wiadomości z określonego miejsca sieci oraz jej przechowywania. Wysyłając do kogoś list elektroniczny podajemy adres według wzoru: nazwa_użytkownika@serwer.poczty Część po znaku @ określa miejsce w sieci gdzie list ten zostanie przesłany (jest to nazwa fizycznego komputera — hosta), natomiast pozostała informuje, do której „skrzynki” należy go przekazać, czyli kto jest adresatem. Serwer WWW udostępniając swoje usługi pozwala na tworzenie ogólnodostępnych, interaktywnych, niezależnych od platformy sprzętowej, hipertekstowych dokumentów. Utożsamiany często z samym Internetem faktycznie, wraz z językiem HTML (HyperText Markup Language), przyczynił się znacznie do jego rozwoju. Serwer . . . Wspominamy tylko o najczęściej spotykanych serwerach, gdyż ich ilość jest dość znaczna. W zasadzie każde urządzenie przyłączone do sieci i wykonujące jakąś usługę na rzecz innych maszyn nazywamy serwerem. Klientem nazywamy urządzenie korzystające z usług serwera za pomocą sieci. Ważne jest to, że każdy komputer może być zarówno klientem jakiejś usługi jak i serwerem innej, wszystko to zależy od konfiguracji oprogramowania.

8.3.1

Architektura równorzędna

W sieciach równorzędnych (ang. Peer-to-Peer — P2P) zwanych również sieciami każdy-z-każdym, nie występuje tzw. dedykowany serwer. Oznac 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

8.3 Architektura sieci

223

cza to, iż każde z urządzeń może pełnić funkcję zarówno klienta jak i serwera. Sieci tego typu zbudować można w oparciu o dowolną topologię, co więcej nie ma konieczności stosowania specjalnych środowisk pracy czy systemów operacyjnych. Dzięki temu ich koszt i niezbędny nakład pracy związany z instalacją zredukowane są do minimum. Praktycznie w oparciu o każdy dzisiejszy komputer (a raczej ich zbiór) można zbudować sieci tego typu bez większego nakładu pracy i środków. Trzeba jednak przy tym pamiętać o pewnych zagrożeniach i ograniczeniach wynikających z takiej struktury. Bezpieczeństwo w tego typu systemach zależy od każdego z użytkowników. Wszyscy muszą przestrzegać pewnych zasad i procedur postępowania. Niewiele warte staną się nawet najwymyślniejsze hasła użytkowników, gdy jeden z nich ujawni (celowo lub przez przypadek) swoje. Ponadto współpraca kilkunastu osób wymagać może ciągłej wymiany pewnych dokumentów. Problemem staje się wówczas zapewnienie dostępu do ich najaktualniejszej wersji. Zwykle poszukiwany plik będzie znajdował się na kilku maszynach, co zmusi nas do ich przejrzenia i wybrania tego właściwego. Co więcej, może się okazać, że komputer z najaktualniejszą wersją właśnie został wyłączony, gdyż jego użytkownik postanowił właśnie zakończyć pracę i zapomniał poinformować o tym fakcie pozostałych. Ze względu na wspomniane cechy sieci typu każdy-z-każdym idealnie nadają się dla małych firm, czy grup roboczych wchodzących w skład większych organizacji. Doskonale powinny zdać także egzamin w przypadku „sąsiedzkiego” połączenia w jednym bloku mieszkalnym czy odcinku ulicy.

8.3.2

Architektura klient-serwer

Ograniczenia i problemy związane z bezpieczeństwem i administracją w sieciach równorzędnych obejść można stosując architekturę klient-serwer. Jedną z kluczowych cech w tej architekturze jest centralne zarządzanie, dzięki któremu ulega znacznej poprawie bezpieczeństwo danych. Jeśli zachodzi potrzeba stworzenia np. kopii zapasowej, to dotyczy to tylko serwera i zadanie to można powierzyć jednej osobie lub wręcz zautomatyzować. Nie trzeba obarczać indywidualnych użytkowników, którzy najczęściej nie rozumieją potrzeby tworzenia kopii bezpieczeństwa dopóki nie nastąpi jakaś awaria i utrata danych. Konsekwencją centralnej weryfikacji tożsamości jest znacznie sprawniej działający mechanizm kontroli uprawnień nadawanych osobom korzystającym z sieci. W sieciach klient-serwer jedynie serwer odpowiada za przetwarzanie zapytań klientów. Odciąża to znacznie poszczególne komputery klienckie. W takiej sytuacji wydajność sieci zależy często
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

224

Sieci komputerowe

od wydajności serwera. Zwykle jest to wyspecjalizowany system komputerowy zoptymalizowany pod kątem pełnienia pewnej określonej funkcji i jego wydajność zapewnia właściwe działanie sieci. Trzeba wyraźnie zaznaczyć, że serwer pozostaje jednak najbardziej newralgicznym ogniwem sieci. Jego awaria powoduje zatrzymanie pracy wszystkich użytkowników. Dlatego też przykłada się wielką wagę do zapewnienia ciągłej i nieprzerwanej pracy serwerów czyniąc je tym samym urządzeniami skomplikowanymi i niestety drogimi. Koszty podnosi konieczność stosowania specjalnego oprogramowania nadzorującego pracę sieci a także samo jej wdrażanie i codzienna obsługa. Właśnie ze względu na czynnik ekonomiczny stosowanie sieci klient-serwer wydaje się ekonomicznie uzasadnione w przypadku dużych instytucji, gdzie wymaga się zwiększonego bezpieczeństwa oraz jednolitego zarządzania i korzystania z zasobów sieci.

8.4
8.4.1

Kilka przydatnych pojęć
Pakiety i ramki

Zazwyczaj dane przesyłane przez sieć nie stanowią ciągłego strumienia bitów. Dane przeznaczone do wysłania dzielone są na pewne „porcje” zwane pakietami i dopiero wtedy transmitowane. Są dwa główne powody stosowania pakietów. Po pierwsze, wspierają obsługę błędów transmisji, gdyż dużo łatwiej jest powtórzyć transmisję niewielkiego bloku danych niż na przykład kilku megowego pliku. Po drugie, umożliwiają prawie równoległą transmisję, za pomocą jednego ośrodka, danych pochodzących od wielu nadawców. Łatwo wyobrazić sobie sytuację, gdy jeden komputer przesyłający 100 MB plik wstrzymałby pracę całej sieci. Dzięki stosowaniu pakietów, każdy w pewnym fragmencie przydzielonego czasu może nadać małą porcję danych (właśnie pakiet), co chroni przed zablokowaniem dostępu dla pozostałych komputerów (rysunek 8.5). Ramką będziemy nazywali ciąg bitów, o ustalonym znaczeniu i kolejności w pakiecie. Ogólny schemat ramki przedstawia rysunek 8.6. Jak widać oprócz danych użytkownika przesyłane są także pewne dodatkowe informacje, są nimi np. SOH (ang. Start Of Header) — początek ramki, oraz EOT (End Of Transmission) — koniec ramki. Wadą takiego rozwiązania jest „marnowanie” części pakietu na dodatkowe dane, które nie niosą ze sobą faktycznej informacji. Jednak patrząc na zagadnienie z drugiej strony, stosowanie ramek i większych nagłówków jest konieczne. Zauważmy, że musimy przecież gdzieś umieścić choćby informacje o adresacie przesyłki. Przyda się
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

8.4 Kilka przydatnych pojęć

225

(b)

(a)

(c)

Rysunek 8.5: Porównanie transmisji pakietowej (c) z niepakietową (b). Trzy różne dane przeznaczone do transmisji pokazuje rysunek (a).

SOH Nagłówek

Dane

EOT

Rysunek 8.6: Ogólny schemat ramki.

także numer kolejny przesyłki, pozwalający złożyć z danych transmitowanych w pakiecie oryginalną daną przesłaną przez użytkownika jeśli zaszłaby konieczność jej podziału. Jeśli zamierzamy potwierdzać otrzymanie przesyłki, to ramka powinna zwierać także adres nadawcy. Należy ponadto wziąć pod uwagę fakt, iż rzadko kiedy transmisja w sieci jest bezproblemowa. To właśnie dzięki nadmiarowym informacjom znajdującym się w nagłówkach możliwe jest zidentyfikowanie i odtworzenie lub retransmisja błędnej informacji.

8.4.2

Protokół

Mianem protokołu określamy zbiór pewnych zasad i sposobów postępowania prowadzących do osiągnięcia pewnego celu. Zauważmy, że na codzień spotykamy się z protokołami nie zdając sobie nawet z tego sprawy. Telefonując do kogoś realizujemy „protokół nawiązania połączenia głosowego z odległym klientem”. Gdy nasz rozmówca podniesie słuchawkę, nie mówimy od razu tego co mamy do przekazania, lecz wpierw „inicjujemy wymianę danych”. Zwykle następuje wymiana informacji niezbędnych do nawiązania połączenia w rodzaju:
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

226

Sieci komputerowe

Przykład 8.1. Prosty protokół nawiązywania połączenia zakończony sukcesem. Odbiorca: Nadawca: Odbiorca: Nadawca: Halo. Dzień dobry, mówi X. Witam serdecznie, w czym mogę pomóc. Dzwonię w sprawie...

połączenie zostało nawiązane

Przykład 8.2. Prosty protokół nawiązywania połączenia zakończony porażką. Nadawca: Mówi X. Czy mogę prosić Y? Odbiorca: XyCVFR!* ?GHJAA;p[];, Nadawca: Bardzo przepraszam.

połączenie nie zostało nawiązane z powodu niezgodności protokołów

Savoir-vivre to nic innego jak zbiór zasad i ogólnie przyjętych norm postępowania w różnych sytuacjach życiowych. Nie obcy jest nam przecież termin protokół dyplomatyczny. Ustalenia takie chronią przed niezręczną lub kłopotliwą sytuacją o co nie trudno w przypadku dyplomatów mających kontakty z ludźmi wychowanymi w ramach różnych kultur. Podobnie korzystanie z elektronicznych form przekazu, wymaga ustalenia znaczenia pewnych sygnałów i kolejności ich wymiany w celu zainicjowania pewnego działania. Fizyczne połączenie jest warunkiem koniecznym, ale nie wystarczającym dla pomyślnej realizacji komunikacji sieciowej. Jako przykład szeroko rozpowszechnionych i obecnie najczęściej wykorzystywanych protokołów sieciowych posłużyć może stos protokołów TCP/IP.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

8.4 Kilka przydatnych pojęć

227

8.4.3

TCP/IP

Stos protokołów TCP/IP1 jest obecnie najczęściej stosowanym protokołem w sieciach komputerowych w ogóle i jedynym w Internecie w szczególności. W celu lepszego zrozumienia idei omawianego protokołu posłużymy się przykładem Internetu. Jest to sieć sieci — „twór” integrujący w sobie różne sieci zapewniając im mechanizm jednolitych usług. Oznacza to, iż wymagane jest aby informacje z dowolnego komputera w jednej sieci mogły być przekazywane do innego komputera w każdej z pozostałych. Dla realizacji tego postulatu niezbędne okazuje się wsparcie zarówno od strony programowej jak i technicznej. Aspekt techniczny jest częściowo zapewniony poprzez odpowiednią organizację sieci rozległej (zob. 8.2.1), gdyż Internet jest właśnie ogromną, bo otaczającą całą kulę ziemską siecią WAN. Dlaczego tylko częściowo? Ponieważ, sama topologia sieci i jej konfiguracja zapewnia tylko i wyłącznie możliwość przesyłania w niej bitów informacji, ale jak to było powiedziane wcześniej (zob. 8.4.1), istnieje potrzeba by tę informację pokawałkować, a następnie wysłać do miejsca docelowego. I właśnie adresowanie jest zapewniane poprzez jeden z dwu protokołów wchodzących w skład protokołu TCP/IP, a mianowicie protokół IP. Można zastosować tu pewną analogię do wysyłania zwykłego listu. List ten aby dotarł na miejsce musi posiadać adres, również w dobrym tonie jest podać informację o nadawcy. Protokół IP zapewnia, że dane dotrą pod wskazany adres2 , zatem można przyjąć, że jest on rodzajem koperty w którą zostaje włożony list. Samo adresowanie komputerów w sieci, odbywa się poprzez nadawanie im adresów cyfrowych. Jest to tak zwany numer IP, w formacie czterech kolejnych bajtów oddzielonych kropkami, przykładowy adres może mieć postać: 212.191.65.2. Administrator komputera, podłączając go do sieci globalnej musi mu przypisać adres i od tego momentu komputer ten będzie widziany ze świata pod tą cyfrową reprezentacją (zob. również 8.4.4). Można zapytać, czy każdy numer składający się z czterech kolejnych liczb z zakresu 0 . . . 255 jest prawidłowym adresem. Oczywiście nie, po pierwsze dlatego, że taki numer już gdzieś w sieci może istnieć, czyli może być nadany jakiemuś komputerowi, a przecież nie można wmeldować
W dalszej części będziemy mówili po prostu protokół TCP/IP, chociaż słowo stos jest tutaj bardzo adekwatne, gdyż podkreśla fakt, że nie jest to pojedynczy protokół, lecz dwa: TCP i IP. 2 Tak naprawdę, to jest to tylko częściowe wsparcie, resztę musi dostarczyć sprzęt i odpowiednie oprogramowanie, jednak to protokół IP jest nośnikiem adresu i oczywiście reszty informacji.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004 1

228

Sieci komputerowe

sublokatora komuś na siłę, po drugie dlatego, że pewne numery są zastrzeżone. Ogólnie rzecz biorąc, w celu podłączenia komputera do sieci globalnej, należy zgłosić się do swojego dostawcy usługi (ang. Internet Provider), aby nadał nam numer, bądź więcej jeśli istnieje taka potrzeba. Pozostaje pytanie do czego służy drugi z protokołów, czyli TCP (ang. Transmission Control Protocol). Dzięki niemu zapewniono niezawodne dostarczanie danych. Realizuje on pozornie niemożliwe zadanie, używa oferowanej przez IP zawodnej usługi do wysyłania danych, gwarantując przy tym ich poprawne przesłanie. Każdy komunikat TCP jest kapsułkowany w datagram IP3 i wysyłany siecią. Gdy datagram dociera do węzła docelowego, protokół IP przekazuje jego zawartość do TCP. Zauważmy, że chociaż TCP używa IP do przenoszenia komunikatów, jednak oprogramowanie IP ich nie czyta ani nie interpretuje traktując je po prostu jako dane. W ten sposób TCP traktuje IP jak system komunikacyjny oparty na pakietach, który łączy węzły na dwóch końcach połączenia. Natomiast protokół IP traktuje każdy komunikat TCP jak zwykłe dane do przesłania. Protokół TCP nazywa się protokołem transportowym, gdyż zapewnia połączenie bezpośrednio między programem na jednym komputerze a programem na drugim. Drugim jego zadaniem jest poszatkowanie zbyt długich danych, oraz zapewnienie odtworzenia ich po drugiej stronie w odpowiedniej kolejności. Wracając do analogii z listami, możemy wyobrazić sobie, że mamy do dyspozycji wyłącznie zwykłe koperty w których nie mieści się więcej niż kilka kartek, jednak pragniemy przesłać więcej informacji np. maszynopis książki. W tym celu pakujemy po kilka kartek do koperty, na niej umieszczamy kolejne numery (protokół TCP), a następnie umieszczamy ją w innej kopercie z adresem odbiorcy (protokół IP). Dzięki temu nawet jeśli któryś list zginie to odbiorca, będzie wiedział który, a po otrzymaniu wszystkich będzie znał kolejność składania kartek.

8.4.4

Usługa DNS

W chwili obecnej, gdy w sieci istnieją miliony podsieci i komputerów, trudno posługiwać się tak niewygodnym do zapamiętania adresem jak numer. Dla większości użytkowników dużo łatwiej zapamiętać nazwę www.math.uni.lodz.pl, niż jej cyfrową postać 212.191.65.2. Stąd też w pewnym momencie powstało zapotrzebowanie na usługę DNS (ang. Domain Name Service), która zamienia nazwy symboliczne – dużo łatwiejsze do zapamiętania, na fizyczny
„Kapsułkowany w datagram IP” oznacza „otoczony ramką IP i traktowany jako pakiet IP”.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004 3

8.5 Urządzenia sieci komputerowych

229

adres komputera w sieci. W chwili obecnej usługa ta realizowana jest poprzez różnego rodzaju serwery4 , do których można zaliczyć np. program bind dostępny na wszystkich odmianach systemów UNIX.

8.5

Urządzenia sieci komputerowych

Sieć to nie tylko idee i koncepcje związane z działaniem, ale także fizyczne urządzenia bezpośrednio tworzące jej strukturę oraz sprzęt z niej korzystający.

8.5.1

Urządzenia transmisji

Urządzenia transmisji służą do przekazywania sygnałów przez sieć od nadawcy do odbiorcy, są nimi wszelkiego rodzaju nośniki sygnału elektrycznego i tak możemy wyróżnić: Kabel koncentryczny (zwany również kablem BNC) Składa się z dwóch współosiowych przewodów rozdzielonych izolatorem. Całość osłonięta jest koszulką ochronną (rysunek 8.7). W swoim wyglądzie jest on podobny do kabla antenowego od telewizora, lecz posiada inną impedancję falową5 . W chwili obecnej jest już technologią zanikającą, wspominamy go tutaj z racji istniejących wciąż instalacji opartych o ten kabel. Skrętka Zbudowana jest z czterech par cienkich przewodów splecionych razem parami. Skręcenie przewodów ze sobą ma na celu znoszenie zakłóceń elektromagnetycznych (rysunek 8.8). Jest to obecnie najpowszechniejsza technologii budowy sieci w firmach czy nawet małych sieci osiedlowych, głównie ze względu na niewielkie koszty kabla i urządzeń dostępowych jak karty sieciowe. Światłowód Wewnątrz osłonki ochronnej znajduje się nośnik, którym nie jest jak poprzednio miedź lecz szkło lub plastik o odpowiednio dobranej strukturze (rysunek 8.9). Jest to nośnik o świetnych parametrach przewodzenia, co za tym idzie o niskim tłumieniu, jednak jest on drogi i trudny do instalacji, przez co stosowany wyłącznie przy profesjonalnych instalacjach.
Chodzi w tym miejscu o serwer jako usługę nie zaś konkretne fizyczne urządzenie. Impedancja falowa jest technicznym pojęciem elektrotechnicznym, które można, w pewnym uproszczeniu, opisać jako oporność przewodnika, w którym płynie prąd zmienny.
5 c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004 4

230

Sieci komputerowe

3

1

2

Rysunek 8.7: Kabel koncentryczny. Kabel koncentryczny. 1 — osłona, 2 — izolator, 3 — przewodnik.

Rysunek 8.8: Skrętka. Skrętka.

„Powietrze” Oczywiście powietrze jako takie nie jest żadnym nośnikiem niemniej jednak zastało przywołane w tym miejscu jako pewne hasło. Poprzez „powietrze” niejako6 podróżują fale elektromagnetyczne o różnej częstotliwości, są to między innymi fale radiowe, podczerwone czy laserowe7 . Te trzy rodzaje fal są coraz częściej spotykane w sieciach komputerowych, podczerwień w biurach, gdzie nie ma konieczności bądź technicznej możliwości założenia okablowania. Radio w biurach i sieciach osiedlowych, co prawda początkowy koszt instalacji jest wyższy niż przy okablowaniu, jednak zwraca się on szybko ze względu na elastyczność rozwiązania i brak konieczności konserwacji kabli. Laser zaś jest stosowany na większe odległości, jednak jest on podatny na zakłócenia takie jak deszcz, mgła i inne tego typu czynniki, przez co bywa zawodny.

8.5.2

Urządzenia dostępowe

Urządzenia dostępowe są to wszelkiego typu składniki sieci, których zadaniem jest aktywny udział w jej działaniu. Zauważmy, że urządzenia transJeszcze raz podkreślamy przenośnię tego stwierdzenia, w rzeczywistości tylko fale dźwiękowe mogą rozchodzić się w powietrzu. 7 Lasery stosowane do transmisji danych pracują w zakresie fal widzialnych.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004 6

8.5 Urządzenia sieci komputerowych

231

1

2

3

Rysunek 8.9: Światłowód. Światłowód. 1 — osłona, 2 — wypełnienie ochronne, 3 — przewodnik światłowodowy.

misji miały charakter pasywny. Można wymienić następujące zadania realizowane przez urządzenia dostępowe: 1. Obróbka danych, w celu dalszego wysłania ich w sieci. Zatem będzie to porcjowanie na pakiety, ramki nadawanie adresów docelowych i nadawczych itp. 2. Fizyczne wysyłanie tak sformatowanych danych poprzez konkretne urządzenie transmisji. 3. Odbieranie danych, które są przeznaczone dla tego urządzenia. Ze względu na sposób działania i niejako umiejscowienie w sieci wyróżnia się następujące podstawowe urządzenia dostępowe: Karta sieciowa Jest to element charakterystyczny dla sieci LAN, którego zadaniem jest umożliwienie komputerowi wysyłania i odbioru sygnałów w tej sieci. Można mówić o kartach posiadających możliwość wysyłania sygnałów przez jedno lub kilka wymienionych urządzeń transmisji. Zatem można mówić o karcie przeznaczonej do skrętki, wyposażonej w tym celu w złącze RJ-40, czy o karcie radiowej. Routery spełniają podobną rolę jak karty sieciowe ale w sieciach WAN, tam właśnie są podstawowymi urządzeniami dostępowymi. Jednak analogii tej nie można posuwać zbyt daleko, gdyż routery są urządzeniami znacznie bardziej inteligentnymi od kart sieciowych, a ich zadanie nie sprowadza się wyłącznie do przesłania dalej danych. Wymaga się od nich stwierdzenia którą drogą najlepiej (najszybciej) te dane można posłać. Z powyższego wynika, że router nie musi był połączony wyłącznie z jednym sąsiadem i rzeczywiście tak jest, na ogół jeden router ma kilku a nawet kilkunastu sąsiadów i przez kilku z nich może wysłać informację. Przypomina to trochę sytuację z jaką mamy
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

232

Sieci komputerowe

do czynienia w mieście: jest wiele takich punktów pomiędzy którymi można się poruszać na różne sposoby. Nie jest jednak obojętne którędy pójdziemy, co więcej często jest to bardzo zmienne i zależne np. od natężenia korków ulicznych. Wzmacniaki Ograniczony zasięg sieci lokalnych wynika miedzy innymi ze zjawiska tłumienia (słabnięcia) sygnałów elektrycznych w przewodniku wraz z przebywaną odległością. W celu redukcji tego ograniczenia stosuje się wzmacniaki, czyli urządzenia monitorujące sygnał w przyłączonych do niego kablach, inna ich nazwa to koncentratory (ang. hub). Po wykryciu sygnału w którymś z przewodników, przekazuje jego wzmocnioną „kopię” do pozostałych. Wzmacniaki nie interpretują formatów ramek ani adresów fizycznych, po prostu przesyłają sygnały elektryczne z jednego miejsca do drugiego, dokonując przedtym jego wzmocnienia. Ponieważ koncentratory są obecnie bardzo często wykorzystywane jako centralny punkt sieci wykonanej w topologii gwiazdy (zob. 8.2.2), zapomina się o ich podstawowej cesze jaką jest wzmacnianie. Mosty (ang. bridge) umożliwiają połączenie dwóch osobnych segmentów sieci, jednak w przeciwieństwie do wzmacniaka, przekazuje tylko pewne ramki, a zatem przekazywanie to jest inteligentne. Przykładowo jeśli komputer A z segmentu 1 sieci przesyła informacje do komputera B to wzmacniak przekaże sygnały z tego segmentu do drugiego bez względu na to, w którym z nich jest B. Jeśli natomiast segmenty połączymy mostem, wówczas sygnały zostaną przekazane z jednego segmentu do drugiego tylko wtedy, gdy komputery A i B będą w różnych segmentach. Dzięki temu generowany jest dużo mniejszy ruch w obu segmentach, podnosząc tym samym wydajność sieci. Przełączniki (ang. switch) stanowią rozwinięcie koncepcji mostów. Zasadnicza różnica pojawia się w sposobie pracy. Otóż przełącznik potrafi zestawiać indywidualne i niezależne połączenia dwóch komputerów. Mówiąc obrazowo, składa się on z wielu „minimostów” łączących komputery na zasadzie każdy-z-każdym i wybierających w danej chwili tylko potrzebne połączenie. Rysunek 8.10 przedstawia obrazowo koncepcję WAN-u wraz z zaadresowanymi routerami, oraz stacjami klienckimi podłączonymi do sieci rozległej na różne sposoby.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

8.6 Zadania

233

Rysunek 8.10: Ogólny schemat komputerów w sieci.

8.6

Zadania

1. Omów główne rodzaje topologii sieci komputerowych. 2. Omów różnicę pomiędzy architekturą klient-serwer a architekturą Peerto-Peer. 3. Wymień i omów cechy znanych ci urządzeń transmisji. 4. Co to jest adres IP i usługa DNS?

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

234

Sieci komputerowe

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

Dodatek A

Podstawowe jednostki i pojęcia w informatyce
Informatyka podobnie jak każda współczesna wiedza rozwijająca się dynamicznie posiada własne słownictwo i swoje symbole, którymi się posługuje. W dodatku tym zawarliśmy, naszym zdaniem, najważniejsze z nich, najczęściej używane, które stanowią tylko wybór z bogactwa słownictwa informatycznego. Poniższy wykaz został sporządzony w porządku alfabetycznym. bajt — (ang. byte) jest zbiorem 8-miu bitów, zatem bajt informacji to osiem dowolnych wartości z dwuelementowego zbioru wartości {0, 1}. Dla przykładu następujące dwa ciągi zero-jedynkowe są bajtami informacji 01101101, 11101100, należy zauważyć, że bajty zawsze są zapisywane w postaci ośmiu bitów, niezależnie od tego czy na początku są zera czy też nie. Jako oznaczenie dla jednostki bajtu przyjęto dużą literę „B”. bit — (ang. bit) jest najmniejszą jednostką miary informacji, bitem może przyjmować jedną z dwu wartości ze zbioru {0, 1}. Należy zwrócić uwagę, że zero w tym wypadku również niesie ze sobą informację, podobnie jak jedynka. Jako oznaczenie dla jednostki bitu przyjęto małą literę „b”. host — słowo oznaczające komputer, zwykle w odniesieniu do komputera podłączonego do sieci Internet.

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

236

Podstawowe jednostki i pojęcia w informatyce

interfejs — (ang. interface) określa urządzenie, które jest pewnego rodzaju pośrednikiem umożliwiającym komunikację dwu lub więcej urządzeń. Np. drukarka komunikuje się z komputerem poprzez port równoległy, można powiedzieć, że jest to interfejs równoległy. Kb, KB — są to jednostki wyrażające wielokrotności odpowiednio bitu i bajtu, przy czym należy je czytać k-bit bądź bajt (tj. najpierw samo „k” potem bit bądź bajt), nie należy czytać ich „kilo”. Słowo kilo zgodnie z układem jednostek i miar SI określa wielokrotność 1000, zaś literka „K” (zwróćmy uwagę, że jest to duże K a nie małe k — oznaczające kilo) oznacza wielokrotność 1024 co jest równe 2 10 . Jednak bardzo często mówi się kilo, gdyż jest to znacznie łatwiejsza forma do wypowiedzenia. kompatybilny — (ang. compatible) oznacza zgodny, czyli mówiąc, że jakieś urządzenie jest kompatybilne z innym mamy na myśli, że jest zgodne w jakimś sensie. Dla przykładu mówimy że procesory firmy AMD są kompatybilne na poziomie listy z procesorami firmy INTEL, co oznacza, że z powodzeniem możemy używać oprogramowania przeznaczonego do platformy INTEL na platformie AMD 1 . najmłodszy bit — jest to bit pierwszy od prawej strony bajtu. najstarszy bit — jest to bit znajdujący się najdalej po lewej stronie bajtu.

Jednak zgodność ta występuje w ramach pewnego podzbioru rozkazów, i tak zarówno np. Pentium 4 ma pewną klasę rozkazów, które nie występują w procesorach AMD, i procesory AMD mają rozkazy klasy SSE, które nie występują w procesorach INTEL. Jeśli program będzie wykorzystywał te specyficzne rozkazy to oczywiście nie może zostać uruchomiony na obu tych procesorach.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

1

Dodatek B

Kody ASCII
Tabele zawiera wykaz podstawowych znaków i przypisanych im kodów ASCII.
Dec Hex Znak Dec Hex Znak Dec Hex Znak Dec Hex Znak

32 36 40 44 48 52 56 60 64 68 72 76 80 84 88 92 96 100 104 108 112 116 120

20 24 28 2C 30 34 38 3C 40 44 48 4C 50 54 58 5C 60 64 68 6C 70 74 78

SPC $ ( , 0 4 8 < @ D H L P T X \ ‘ d h l p t x

33 37 41 45 49 53 57 61 65 69 73 77 81 85 89 93 97 101 105 109 113 117 121

21 25 29 2D 31 35 39 3D 41 45 49 4D 51 55 59 5D 61 65 69 6D 71 75 79

! % ) 1 5 9 = A E I M Q U Y ] a e i m q u y

34 38 42 46 50 54 58 62 66 70 74 78 82 86 90 94 98 102 106 110 114 118 122

22 26 2A 2E 32 36 3A 3E 42 46 4A 4E 52 56 5A 5E 62 66 6A 6E 72 76 7A

” & * . 2 6 : > B F J N R V Z ^ b f j n r v z

35 39 43 47 51 55 59 63 67 71 75 79 83 87 91 95 99 103 107 111 115 119 123

23 27 2B 2F 33 37 3B 3F 43 47 4B 4F 53 57 5B 5F 63 67 6B 6F 73 77 7B

# ’ + / 3 7 ; ? C G K O S W [ _ c g k o s w {

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

238 Dec Hex Znak Dec Hex Znak Dec Hex Znak

Kody ASCII Dec Hex Znak

124

7C

125

7D

}

126

7E

127

7F

Del

Tabela zawiera wykaz niedrukowlanych znaków ASCII oraz ich kody. Dec 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 Hex 0 1 2 3 4 5 6 7 8 9 A B C D E F 10 11 12 13 14 15 16 17 18 Znak Ctrl-@ (NUL) Ctrl-A (SOH) Ctrl-B (STX) Ctrl-C (ETX) Ctrl-D (EOT) Ctrl-E (ENQ) Ctrl-F (ACK) Ctrl-G (BEL) Ctrl-H (BS) Ctrl-I (HT) Ctrl-J (LF) Ctrl-K (VT) Ctrl-L (FF) Ctrl-M (CR) Ctrl-N (SO) Ctrl-O (SI) Ctrl-P (DLE) Ctrl-Q (DC1) Ctrl-R (DC2) Ctrl-S (DC3) Ctrl-T (DC4) Ctrl-U (NAK) Ctrl-V (SYN) Ctrl-W (ETB) Ctrl-X (CAN) znaczenie (ang.) null start of heading start of text end of text end of transmission enquiry ackonowledge bell backspace horizontal tab line feed vertical tab form feed carriage return shift out shift in data link escape device controls znaczenie (pol.) brak znaku początek nagłówka początek tekstu koniec tekstu koniec transmisji zapytanie potwierdzenie sygnał akustyczny cofnięcie tabulacja pozioma przesunięcie o wiersz tabulacja pionowa wysunięcie strony powrót karetki przełączenie na zestaw niestandardowy przełączenie na zestaw standardowy znak sterujący transmisją znak sterowania urządzeniami

negative acknowledgment synchronous end of transmission block cancel

negatywne potwierdzenie synchronizacja koniec bloku transmisji kasowanie

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

239

Dec 25 26 27 28 29 30 31

Hex 19 1A 1B 1C 1D 1E 1F

Znak Ctrl-Y (EM) Ctrl-Z (SUB) Ctrl-[ (ESC) Ctrl- (FS) Ctrl-] (GS) Ctrl-ˆ (RS) Ctrl- (US)

znaczenie (ang.) end of medium substitute escape file separator grou separator record separator united separator

znaczenie (pol.) fizyczne zakończenie nośnika zastąpienie znak unikowy separator plików separator grup separator rekordów separator połączony

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

240

Kody ASCII

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

242

Kodowanie polskich znaków

Dodatek C

Kodowanie polskich znaków
Standard ą ć ę ł ń ó ś ź ż 1 ISO 8859-2 B1 E6 EA B3 F1 F3 B6 BC BF 2 CP 1250 B9 E6 EA B3 F1 F3 9C 9F BF CP 8523 A5 86 A9 88 E4 A2 98 AB BE Mazowia 86 8D 91 92 A4 A2 9E A6 A7 Cyfromat 90 91 92 93 94 95 96 98 97 IEA-Swierk A0 9B 82 9F A4 A2 87 A8 91 Ventura 96 94 A4 A7 91 A2 84 82 87 ELWRO-Junior E1 E3 E5 EC EE EF F3 FA F9 AmigaPL E2 EA EB EE EF F3 F4 FA FB TeXPL A1 A2 A6 AA AB F3 B1 B9 BB Atari-Calamus D1 D2 D3 D4 D5 D6 D7 D8 D9 CorelDraw! E5 EC E6 C6 F1 F3 A5 AA BA Unicode 105 107 119 142 144 F3 15B 17A 17C Standard Ą Ć Ę Ł Ń Ó Ś Ź Ż 4 ISO 8859-2 A1 C6 CA A3 D1 D3 A6 AC AF 5 CP 1250 A5 C6 CA A3 D1 D3 8C 8F AF CP 8526 A4 8F A8 9D E3 E0 97 8D BD Mazowia 8F 95 90 9C A5 A3 98 A0 A1 Cyfromat 80 81 82 83 84 85 86 88 87 IEA-Swierk 8F 80 90 9C A5 99 EB 9D 92 Ventura 97 99 A5 A6 92 8F 8E 90 80 ELWRO-Junior C1 C3 C5 CC CE CF D3 DA D9 AmigaPL C2 CA CB CE CF D3 D4 DA DB TeXPL 81 82 86 8A 8B D3 91 99 9B Atari-Calamus C1 C2 C3 C4 C5 C6 C7 C8 C9 c 2001-2003 CorelDraw!by P. Fulmański F2 Sobieski, UniwersytetD1 D3 RC1 z dnia: 4 stycznia 2004 C5 & Ś. C9 A3 E1 ED Łódzki. Wersja FF Unicode 104 106 118 141 143 D3 15A 179 17B

Dodatek D

Organizacje standaryzujące
Dodatek ten zawiera listę z krótkim opisem organizacji zajmujących się ustanawianiem standardów związanych z różnymi technologiami informatycznymi. Odwołania do tych organizacji można znaleźć w wielu miejscach niniejszego opracowania. ANSI (ang. The American National Standards Institute) — Amerykański Narodowy Instytut Normalizacji jest to prywatna organizacja niekomercyjna, której celem jest ułatwienie rozwoju różnych nowych technologii. Osiągane jest to poprzez koordynację i publikowanie nieobligatoryjnych standardów. Zauważmy, że użyte zostało słowo nieobligatoryjnych, co oznacza, że to co opublikuje ta organizacja nie jest dla nikogo obowiązkowe i nie ma konieczności posługiwania się tymi a nie innymi normami. Niemniej jednak bardzo często standardy te stają się później obowiązującymi na mocy ustaleń innych organizacji, dzieje się tak głównie z uwagi na fakt, że ANSI w Stanach Zjednoczonych reprezentuje organizacje ISO i IEC. IEEE (ang. The Institute of Electrical and Electronic Engineers) — Instytut Elektryków i Elektroników jest organizacją zajmującą się definiowaniem i publikowaniem standardów telekomunikacyjnych. Dla przykładu jednym z jej produktów jest seria standardów 802, która określa normy dla sieci LAN i MAN. ISO (ang. International Organization for Standarization) 1 — MiędzynaroFormalnie poprawnym skrótem jest IOS jednak, zarówno świat jak i sama organizacja woli łatwiejszy do zapamiętania akronim ISO. Skrót ISO pochodzi od greckiego słowa isos oznaczającego równy lub standardowy.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004 1

244

Organizacje standaryzujące

dowa Organizacja Normalizacyjna została utworzona w 1946 roku w Genewie, gdzie do dziś znajduje się jej siedziba. Celem tej organizacji jest ustalanie wszelkich norm międzynarodowych, do tego celu została wynajęta przez ONZ. ISO jest potężną instytucją składającą się obecnie z ponad dziewięćdziesięciu różnych mniejszych organizacji, zakres jej działania jest również imponujący, gdyż zawiera w sobie wszystkie dziedziny ludzkiej nauki za wyjątkiem elektryki i elektroniki. IEC (ang. International Electrotechnical Commission) — Międzynarodowa Komisja Elektrotechniczna została utworzona w 1909 roku i mieści się w Genewie. Jej celem jest ustalanie międzynarodowych norm w zakresie elektryki i elektroniki, zatem uzupełnia zakres działalności organizacji ISO. IAB (ang. The Internet Architecture Board) — Komisja Architektury Internetu, znana poprzednio jako Komisja Działań Internetu (ang. Internet Activities Board) jest organizacją, której celem jest zarządzanie techniczną stroną rozwoju Internetu. W jej skład wchodzą dwie grupy robocze: Grupa Robocza ds. Technicznych Internetu (ang. Internet Engineering Task Force (IETF)) oraz Grupa Robocza ds. Naukowych Internetu (ang. Internet Research Task Force (IRTF)). Zadaniem grupy naukowej jest badanie nowych technologii w zakresie ich przydatności do rozwoju bądź poprawienia jakości Internetu. Zadaniem grupy technicznej jest wdrażanie i testowanie nowych technologii jak i opieka nad istniejącą infrastrukturą.

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

Dodatek E

Rozwiązania zadań
W dodatku tym znajdują się odpowiedzi lub rozwiązania do zadań zamieszczonych w publikacji. Zamieszczamu odpwiedzi i rozwiązania wyłącznie do zadań obliczeniowych, bądź problemowych. Wszystkie odpowiedzi, które zawarte są wprost w treści rozdziału, są pomijane.

E.1

Rozwiązania do rozdziału 2

1. Nad znakami równości umieszczono numery praw z których korzystano. 2. W każdym podpunkcie pierwsza liczba zapisana jest w systemie dziesiętnym, druga — dwójkowym, trzecia — ósemkowym, czwarta — szesnastkowym. a) b) c) d) e) f) g) h) i) j) 172 517 778 13 76 107 300 201 472 802 → → → → → → → → → → 10101100 1000000101 1100001010 1101 1001100 1101011 100101100 11001000 111011000 1100100010 → → → → → → → → → → 254 1005 1412 15 114 153 454 310 730 1442 → → → → → → → → → → AC 205 30A D 4C 6B 12C C8 1D8 322

3. W każdym podpunkcie pierwsza liczba zapisana jest w systemie dwójkowym, druga — ósemkowym, trzecia — szesnastkowym, czwarta —
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

246

Rozwiązania zadań

dziesiętnym. a) b) c) d) e) f) g) h) i) j) 11100 1011110001 10001010001 100100100 1110011100 101110001 1001001100 101010000 10100001 1000101 → → → → → → → → → → 34 1361 2121 444 1634 561 1114 520 241 105 → → → → → → → → → → 1C 2F1 451 124 39C 171 24C 150 A1 45 → → → → → → → → → → 28 544 1105 292 805 369 578 336 161 69

4. W każdym podpunkcie pierwsza liczba zapisana jest w systemie dwójkowym, druga — ósemkowym, trzecia — szesnastkowym, czwarta — dziesiętnym. a) b) c) d) e) f) g) h) i) j) F2 11F AAA 100 1AB 123 FF F0 BAB 307 → → → → → → → → → → 11110010 100011111 101010101010 100000000 110101011 100100011 11111111 11110000 101110101011 110111 → → → → → → → → → → 362 437 5252 400 653 443 377 360 5653 67 → → → → → → → → → → 242 287 2730 256 427 291 255 240 2987 55

5. W każdym podpunkcie pierwsza liczba zapisana jest w systemie ósemkowym, druga — dwójkowym, trzecia — szesnastkowym, czwarta — dziesiętnym. a) b) c) d) e) f) g) h) i) j) 123 457 177 65 22 10 27 55 222 512 → → → → → → → → → → 1010011 100101111 1111111 110101 10010 1000 10111 101101 10010010 101001010 → → → → → → → → → → 53 12F 7F 25 12 8 17 2D 92 14A → → → → → → → → → → 83 303 127 37 18 8 23 45 146 330

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

E.1 Rozwiązania do rozdziału 2

247

6. W każdym podpunkcie pierwsza liczba zapisana jest w systemie siódemkowym, druga zaś w piątkowym. a) b) c) d) e) f) g) h) i) j) 565 100 62 12 166 306 255 32 836 56 → → → → → → → → → → 2023 144 134 14 342 1103 1023 143 3113 131

7. W każdym podpunkcie pierwsza liczba jest zapisana w systemie piątkowym, druga zaś w jedenastkowym. a) b) c) d) e) f) g) h) i) j) 1234 4222 2131 1421 3123 1121 2041 4131 3211 3114 → → → → → → → → → → 167 45 245 140 346 137 227 452 362 342

8. W każdym podpunkcie pierwsza liczba jest zapisana w systemie trzynastkowym, druga zaś w dziewiątkowym. a) b) c) d) e) f) g) h) i) j) C99 2A5 91 65 3BC 910 B7 18 301 40C → → → → → → → → → → 2710 553 141 102 772 1874 176 23 624 800

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

248

Rozwiązania zadań

E.2

Rozwiązania do rozdziału 3

1. W każdym podpunkcie pierwsza liczba jest całkowitą liczbą zapisaną w systemie dziesiętnym, druga zaś odpowiadającą jej liczbę dwójkową w notacji znak-moduł. a) b) c) d) e) f) g) h) i) j) -20 +20 +47 -18 -37 -128 +372 -420 -42 -15 → → → → → → → → → → 10010100 00010100 00101111 10010010 00100101 1000000010000000 0000000101110100 1000000110100100 10101010 10001111

2. W każdym podpunkcie pierwsza liczba jest całkowitą liczbą zapisaną w systemie dziesiętnym, druga zaś odpowiadającą jej liczbę dwójkową w notacji uzupełnień do dwu. a) b) c) d) e) f) g) h) i) j) -20 +87 +65 -18 +27 -99 -300 +77 -77 +503 → → → → → → → → → → 11101100 01010111 01000001 11101110 00011011 10011101 11010100 01001101 10110011 0000000111110111

3. W każdym podpunkcie pierwsza liczba jest rzeczywistą liczbą dziesiętną, druga zaś rzeczywistą dwójkową.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

E.2 Rozwiązania do rozdziału 3

249

a) b) c) d) e) f) g) h) i) j) k) l)

0.5625 7.4375 11.0625 4.6875 2.8125 13.875 9.625 14.75 15.375 5.0625 3.3125 8.1875

= = = = = = = = = = = =

0.1001 111.0111 1011.0001 100.1011 10.1101 1101.111 1001.1010 1110.11 1111.011 101.0001 11.0101 1000.0011

4. W każdym podpunkcie pierwsza liczba jest rzeczywistą liczbą dwójkową, druga zaś rzeczywistą dziesiętną.

a) b) c) d) e) f) g) h) i) j) k) l)

1111.01 1011.101 1000.111 1.1011 0.1111 1100.0001 100.0111 1001.001 101.1101 10.1001 1101.011 1010.1

= = = = = = = = = = = =

15.25 11.625 8.875 1.6875 0.9375 12.0625 4.4375 9.125 5.8125 2.5625 13.375 10.5

5. W każdym podpunkcie pierwsza liczba jest rzeczywistą liczbą dwójkową zapisaną zgodnie z formatem opisanym w poleceniu ćwiczenia, druga zaś rzeczywistą dziesiętną.
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

250

Rozwiązania zadań

a) b) c) d) e) f) g) h) i) j) k) l)

01000000 10100101 00010010 10101001 00000100 10100010 01111101 10001110 00101011 10011111 01010110 10000011

= = = = = = = = = = = =

+ 0.01171875 - 0.3125 + 0.033203125 - 5.0 + 0.125 - 0.0390625 + 120.0 - 128.0 + 20.0 - 288.0 + 3.25 - 0.0625

6. Tabelki ruchów. a)

Stan s s s s s s s s s s s s k b b b b b b b b b b b b b a b b b b b b b b b b b b b b a a a a a a a a a a a a a a b b a a a a a a a a

a a b b b b b b b

Zawartość taśmy b a b a b a b a b a b a b a a a a b a b a b a b a b

a a a a a a a a a b b b b

b b b b b b b b b b a a a

b b b b b b b b b b b a a

b)
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

E.3 Rozwiązania do rozdziału 4

251

Stan s s s 1 1 1 1 1 s s k c) Stan s s s 1 1 1 1 1 2 2 2 2 k a a a a a a a a a a a a a a a a a a a a a a a a a a b b b b a a a a a a a a a b b b b b b b b b b b b b b b b b b b b b b a a a a a a a a a a a a a a a a a a a a a a

Zawartość taśmy a b a b a b a b a b a b a a a a a a a a a a

b b b b b b b a a a a

a a a a a a a a a a a

a a a a a a a a a a

Zawartość taśmy a b a b a b a b a b a b a b a a a a a b a b a b a b

a b b b b

b b b b b b b b b b b b b

E.3
1.

Rozwiązania do rozdziału 4
a) 1100 b) 0111 c) 1111 d) 0010

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

252

Rozwiązania zadań

2.

a) 0011 b) 1011 c) 0111 d) 1000

3.

a) Błąd podczas transmisji. b) Transmisj poprawna. c) Błąd podczas transmisji. d) Błąd podczas transmisji.

E.4

Rozwiązania do rozdziału 5

1. Przykład E.1 prezentuje wersję iteracyjną, zaś E.2 wersję rekurencyjną. Przykład E.1. Funkcja obliczająca potęgę — podejście iteracyjne.

potega_i(x, y) begin w := 1; while (y > 0) begin w := w * x; y := y - 1; end potega_i := w; end

Przykład E.2. Funkcja obliczająca potęgę — podejście rekurencyjne.

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

E.4 Rozwiązania do rozdziału 5

253

potega_r(x, y) begin if (y=0) w := 1; else w := x * potega_r(x, y-1); potega_r := w; end

2. Schemat blokowy dla iteracyjnego algorytmu obliczania potęgi z poprzedniego zadania pokazany jest na rysunku 2.

Rysunek E.1: Schamat blokowy algorytmu obliczającego potęgę.

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

254

Rozwiązania zadań

3. Algorytm zapisany w postacji pseudo-jezyka podany jest w przykładzie E.3. Algorytm ten realizuje wyznaczanie Największego Wspólnego Dzielnika dwu liczb naturalnych. Przykład E.3. Algorytm wyznaczania NWD.

nwd(x, y) begin while (a != b) begin if (a > b) then a := a - b else b := b - a end nwd := a end

4. Funkcja wyszukiwania połówkowego zrealizowany rekurencyjnie jest przedstawiony w przykładzie E.4. Pierwsze wywołanie takiej funkcji dla tablicy 10-cio elementowej o nazwie tablica, gdzie chcemy znaleźć element o wartości 45, będzie miało postać szukaj(45, 1, 10, tablica). Funkcja ta zwróci indeks znalezionego elementu w przypadku sukcesu oraz wartość −1 w przypadku braku poszukiwanego elementu w tablicy. Przykład E.4. Rekurencyjna funkcja wyszukiwania połówkowego.

szukaj(co, start, koniec, tablica) begin if (start > koniec) szukaj := -1
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

E.4 Rozwiązania do rozdziału 5

255

else begin polowa := (start + koniec) / 2 if (tablica[polowa] = co) szukaj := polowa else if (tablica[polowa] < co) szukaj := szukaj(co, start, polowa-1, tablica) else szukaj := szukaj(co, start+1, koniec, tablica) end end

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

256

Rozwiązania zadań

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

Bibliografia
[AD89] [B+ 99] [Bab89] [Bac95] [Bie96] M. Adiba and C. Delobel. Relacyjne bazy danych. WNT, Warszawa, 1989. M. Beck et al. Linux kernel jądro systemu. MIKOM, Warszawa, 1999. R. L. Baber. O oprogramowaniu inaczej. Biblioteka inżynierii oprogramowania. WNT, Warszawa, 1989. M. J. Bach. Budowa systemu operacyjnego UNIX. WNT, Warszawa, 1995. Janusz Biernat. Arytmetyka komputerów. PWN, Warszawa, 1996.

[Chu41] A. Church. The calculi of lambda-conversion. Ann. of Math. Studies, 6, 1941. [Com00] D. E. Comer. Sieci komputerowe i intersieci. WNT, 2000. [Coo70] E. F. Cood. A relational model of data for large shared data banks. Comm. ACM, 13(6):377–387, 1970.

[DuC01] B. DuCharme. Operating Systems Handbook. McGraw-Hill, 2001. [Fe98] Z. Fryźlewicz and Z. Huzar etc. ADA 95. Helion, 1998.

[FLB77] G. Goos F. L. Bauer. Informatyka. WNT, Warszawa, 1977. [Hal96] F. Halsall. Data Communications, Computer Networks and Open Systems. Electronic Systems Engineering Systems. AddisonWesley, 1996. D. Harel. Rzecz o istocie informatyki, algorytmika. Klasyka informatyki. WNT, 2000.

[Har00]

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

258

BIBLIOGRAFIA

[Hed87] [Kle36] [Kąc86] [LE94] [Lig92]

R. Hedtke. Systemy mikroprocesorowe. WNT, Warszawa, 1987. S. C. Kleene. General recursive functions of natural numbers. Mathematische Annalen, 112:727–742, 1936. E. Kącki. Elektroniczna technika obliczeniowa. Matematyka dla politechnik. PWN, Warszawa, 1986. A. M. Lister and R. D. Eager. Wprowadzenie do systemów operacyjnych. WNT, Warszawa, 1994. R. Ligonni´re. Prehistoria i historia komputerów. Ossolineum, e Wrocław, 1992.

[Mac99] B. J. MacLennan. Principles of Programming Languages. Oxford University Press, New York, 1999. [Mar54] A. Markow. Theory of algorithms. Trudy Mat. Inst. Steklov., 42, 1954. [MI86] S. Matwin M. Iglewski, J. Madey. Pascal. WNT, Warszawa, 1986.

[Nau79] P. Naur. Zarys metod informatyki. WNT, Warszawa, 1979. [Ove01] [RS87] [S+ 94] [Sha48] [Ste98] [Str00] [Tur36] M. L. Overton. Numerical Computing with IEEE Floating Point Arithmetic. SIAM, Philadelphia, 2001. A. Rydzewski and K. Sacha. Mikrokomputer elementy, budowa, działanie. NOT SIGMA, Warszawa, 1987. M. M. Sysła et al. Elementy informatyki. Wydawnictwo Naukowe PWN, Warszawa, 1994. C. Shannon. A mathematical theory of communication. The Bell System Technical Journal, 27:379–423, July 1948. W. R. Stevens. Biblia TCP/IP, Protokoły, volume Tom 1. Wydawnictwo RM, Warszawa, 1998. B. Stroustrup. Język C++. Klasyka informatyki. WNT, Warszawa, 2000. A. M. Turing. On computable numbers, with an application to the entscheidungsproblem. Proc. London Math. Soc., 2(42):230–265, 1936.

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

BIBLIOGRAFIA

259

[Wir71a] N. Wirth. Program development by step-wise refinement. Comm. ACM, 14(4):221–227, 1971. [Wir71b] N. Wirth. The programming language pascal. Acta Informat., 1(1):35–63, 1971. [Wir01] N. Wirth. Algorytmy + struktury danych = programy. Klasyka informatyki. WNT, 2001.

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

Indeks
przez rejestr indeksowy i offset, 92 układów wejścia/wyjścia, 92 Aiken, Howard, 23 al-Chorezmi, Muhammad, 18 alfabet, 71, 183 Algebra Boole’a, 100 algebra Boole’a, 39 algorytm, 15, 17, 18, 129 Abakus, 17 Euklidesa, 18, 129 ADA, 22, 178 obliczeniowy, 143 Ada Augusta hrabina Lovelace, 21 sortowania, 144 Address Unit, 84 algorytmika, 29 addytywny system liczbowy, 46 algorytmy, 29 adres genetyczne, 35 efektywny, 84, 91 Altair, 24 adresowanie, 90 ALU, 83 bezpośrednie analiza z pamięci, 91 algorytmów, 131 z rejestru, 91 analiza złożoności, 153 natychmiastowe, 91 ANSI, 243 pośrednie architektura przez rejestr bazowy, 91 fizyczna komputera, 190 przez rejestr bazowy i indekvon neumannowska, 24 sowy, 92 architektura klient-serwer, 223 przez rejestr bazowy i offset, architektura sieci, 218 92 ARPA, 32 przez rejestr bazowy, indeksowy ARPANET, 32 i offset, 92 Arythmetic Logic Unit, 83 przez rejestr indeksowy, 92 arytmometr, 24
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

ścisła typizacja, 168 światłowód, 229 Łukasiewicz, Jan, 170 łączenie dynamiczne, 179 statyczne, 179 4.3 BSD, 211

INDEKS

261

asemblacja, 178 asembler, 96, 178 AU, 84 AX (AH/AL), 86 B-code, 181 Babbage, Charles, 19 Backus, John, 160, 183 bajt, 235 bazy danych, 30 hierarchiczne, 30 obiektowe, 30 relacyjne, 30 sieciowe, 30 Bell, 22 Berners-Lee, Tim, 33 biblioteki, 178 dzielone, 179 systemowe, 178 bit, 235 bit parzystości, 121 Boole, George, 39 BP, 87 bramka AND, 75 NAND, 75 NOR, 75 NOT, 75 OR, 75 bramka logiczna, 75 bridge, 232 Bull, 22 BX (BH/BL), 86 Byte-Code, 181 cache, 82 CD-R, 82 CD-R/W, 82 cecha, 110 Cerf, Vinton, 33

Chomsky, Noam, 183 Church, A., 130 Church, Alonzo, 29 Codd, E. F., 30 Colmerauer, Alain, 172 Colos I, 23 Colos II, 23 Computer Science, 16 CRC, 125 CS, 86 CX (CH/CL), 86 Cyclic Redundancy Code, 125 cyfra, 46 cyfrowe układy scalone, 75 cykl, 84 odczytu, 84 pobrania, 84 wykonania, 85 zapisu, 85 cykl pracy procesora, 84 cykliczny kod nadmiarowy, 125 część wspólna zbiorów, 136 czas dostępu, 81 datagram IP, 228 DEC, 210 deklaracja, 166 Delphi, 163 DI, 87 diagram przejść, 71 diagramy składni, 185 DNS, 228 dopisanie elementu do pliku, 137 dostęp swobodny, 133 DS, 86 dwuelementowa algebra Boole’a, 44 DX (DH/DL), 86

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

262

INDEKS

Hollerith, Henrich, 30 Hollerith, Herman, 22 EDSAC, 24 host, 235 EDVAC, 24 HTML, 222 efektywność algorytmów, 153 elementy języka programowania, 159 hub, 232 ENIAC, 23 I/O, 78 enkapsulacja danych, 210 IAB, 244 EOT, 224 IBM, 22, 23, 210 ES, 87 IEC, 244 etykieta, 98 IEEE, 243 Euklides, 18 implementacja klasy, 168 inżynieria oprogramowania, 32, 164 flaga indeksowanie tablic, 133 kierunku, 88 informacja, 16, 215 parzystości, 88 Input/Output, 78 pomocnicza, 88 Instruction Unit, 83 pracy krokowej, 88 instrukcja, 166 przeniesienia, 88 do–while, 141 przepełnienia, 89 pętli, 141 zera, 88 podstawienia, 140 zezwolenia na przerwanie, 88 warunkowa, 140 znaku, 88 while, 141 FLAGS, 87 interfejs, 235 Floating Point Unit, 83 interfejs klasy, 168 FPU, 83 International Business Machine, 22 funkcja, 148 interpretacja, 180 rekurencyjna, 148 interpreter, 178, 180 funkcja następnego ruchu, 71 IP, 87 Gates, Bill, 25, 204 IPX/SPX, 208 gliniane tabliczki, 17 ISO, 243 GNU Prolog (gprolog) jest wersją na ISO 8859-2, 101 licencji Open Source., 175 iteracja, 148 grafika komputerowa, 31 IU, 83 gramatyka język, 183 bezkontekstowa, 183 ADA, 177 formalna, 183 Algol-60, 161, 176 kontekstowa, 183
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

działania arytmetyczno–logiczne, 24 dziedziczenie, 169

GUI, 202, 205

INDEKS

263

Algol-68, 162 asembler, 96 BASIC, 25 bezkontekstowy, 183 C, 212 C++, 177 FORTRAN, 160 FORTRAN 77, 161 FORTRAN 90, 161 FORTRAN IV, 161, 176 JAVA, 182 Java, 177 JCL, 209 kontekstowy, 183 LISP, 169 LOGO, 169 maszynowy, 177 mnemoniczny, 96 Pascal, 162, 174, 177 PERL, 180 PHP, 180 Prolog, 171 Smalltalk, 169 zorientowany obiektowo, 167 język adresów symbolicznych, 178 język prekompilowany, 181 język programowania, 157 języki skryptowe, 180 języki programowania, 32 Jacquard, 21 jednoczesność zadań, 196 jednostka adresująca, 84 arytmetyczno-logiczna, 83 dekodowania rozkazów, 83 sterująca, 24 zarządzająca pamięcią, 84 zmienno-przecinkowa, 83

JIT, 182 kabel BNC, 229 kabel koncentryczny, 229 Kahn, Robert, 33 kalkulator elektroniczny, 23 karta sieciowa, 231 karty perforowane, 21, 22 katalog, 198 KB, 236 Kb, 236 Kernighan, Brian, 211 Key, Alan, 169 klasa, 167 Kleene, Stephen, 29 klient, 222 kod BCD, 62 CRC, 125 Morsa, 119 kod źródłowy, 177, 178 kod ASCII, 100 kod pośredni, 181 kod szesnastkowy, 56 kodowanie, 100 kompatybilny, 236 kompilacja, 178 kompilator, 178 komprocesor, 83 komputer Apple II, 25 AS/400, 210 biologiczny, 28 klasy PC, 25 PC, 25 PDP-1, 210 komunikat TCP, 228 koncentratory, 232 koniec ramki, 224

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

264

INDEKS

koprocesor numeryczny, 78 kryptografia, 31 lampy elektronowe, 23 LAN, 216 Leibniz, Gottfried Wilhelm, 19 licencja GPL, 212 liczba całkowita, 103 liczba naturalna, 103 liczby Fibonacciego, 151 licznik rozkazów, 193 liczydło, 18 litera, 183 logika rozmyta, 35 ludy sumeryjskie, 17 młyn, 20 magazyn, 20 magistrala adresowa, 80 danych, 80 sterująca, 80 systemowa, 78 Mainframe, 34 MAN, 216 mantysa, 110 MARK I, 23 maszyna analityczna, 20 licząca, 19 różnicowa, 19 Turinga, 23 wirtualna, 210 Z1, 23 Z2, 23 Z3, 23 Z4, 23 Maszyna Turinga, 29, 70 maszyna Turinga, 69 maszyna wirtualna, 181

mechaniczna maszyna licząca, 19 mechanizm sterujący, 21 Memory Managment Unit, 84 metoda różnicowa, 20 zerojedynkowa, 44 zstępująca, 174 metodologia obiektowa, 167 mikroprocesor jednoukładowy, 78 MMU, 84 moc obliczeniowa, 16 Mors, Samuel, 119 most, 232 MVS, 209 MVT, 209 najmłodszy bit, 236 najstarszy bit, 236 NCR, 22 NDS, 207 Neper, John, 19 Network Operating System, 207 Newmann, John von, 24 NFSNET, 33 niedomiar dodatni, 112 ujemny, 112 nośniki informacji, 16 NOS, 207 notacja BNF, 184 numer IP, 227 Nyquist, Harry, 119 obiekt, 166, 210 objekt, 167 odczyt z pliku, 199 odczytanie następnego elementu pliku, 137

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

INDEKS

265

polska notacja, 170 POSIX, 212 Post, E., 130 postać kanoniczna, 45 pozycyjny system liczbowy, 46 pałeczki Nepera, 19 prawo pakiet, 166 łączności, 40 pakiety, 224 De Morgana, 42 pamięć, 24, 78 dopełnienia, 40 o dostępie swobodnym, 80 idempotentności, 41 RAM, 80 identyczności, 40 pamięć o dostępie swobodnym, 78 identyczności drugie, 41 pamięć podręczna, 82 pochłaniania, 41 Pascal, Blaise, 19 przemienności, 40 Pascaliny, 19 rozdzielności, 40 PCP, 209 prekompilacja, 181 plik, 133, 136, 198 problem dziesiątkowania, 18 obiektu, 178 problem stopu, 149 sekwencyjny, 137 proces, 193 pobranie wstępne, 85 procesor, 78 początek ramki, 224 INTEL, 34 Mororola, 25 podejście iteracyjne, 148 PowerPC, 25, 34 program, 131, 193 rekurencyjne, 148 podprogramy, 166 wykonywalny, 177 program źródłowy, 178 podstawa systemu liczbowego, 46 program wykonywalny, 178 podsystem programowanie interpretacji poleceń, 189 bezproceduralne, 171 ochrony, 189 funkcjonalne, 169 wejścia/wyjścia, 189 obiektowe, 175 zarządzania pamięcią masową, 188 sekwencyjne, 173 zarządzania pamięcią operacyjną, 188 strukturalne, 174 zarządzania procesami, 188 w logice, 169, 171 protokół, 225 podział obciążenia, 193 https, 31 NCP, 32 zasobów, 193 pojemność, 82 SSL, 31 TCP/IP, 227 pola, 135 oprogramowanie osadzone, 200 wbudowane, 200 otwarcie pliku, 137
c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

266

INDEKS

protokół IP, 227 przełącznik, 232 przecięcie zbiorów, 136 przepełnienie dodatnie, 112 ujemne, 112 przynależność do zbioru, 136 pseudo-język programowania, 139 punkty decyzyjne, 137 zbiegu, 138 różnica zbiorów, 136 RAD, 163 ramka, 224 redundancja, 120 reguły gramatyczne, 159 znaczeniowe, 159 reguły składniowe, 159 rejestr źródła, 87 akumulatora, 86 bazowy, 86 danych, 86 dodatkowy, 87 flagowy, 87 kodu, 86 licznika, 86 przeznaczenia, 87 stosu, 86 wskaźnika bazy, 87 wskaźnika rozkazów, 87 wskaźnika stosu, 87 rejestry, 83 ogólnego przeznaczenia, 86 segmentowe, 86

stanu, 87 stosu, 87 wskaźnikowe, 87 rejestry procesora, 193 rekord, 133, 135 z wariantami, 136 rekurencja, 148 rekursja, 148 reprezentacja zmiennoprzecinkowa, 23 reprezentacja uzupełnień do dwu, 105 reprezentacja zero–jedynkowa, 100 reprezentacja znak–moduł, 104 Roussel, Philippe, 172 router, 231 rozgałęzienia, 137 rozkazy arytmetyczne i logiczne, 97 przerwań, 99 przesunięć, 97 skoków, 98 sterujące stanem procesora, 99 rozproszony system komputerowy, 201 rząd systemu liczbowego, 46 S/360, 34 słownik, 183 słowo, 183 schemat blokowy, 137 Schickard, Wilhelm, 19 semantyka, 159 serwer, 220 aplikacji, 221 bazy danych, 221 plików, 220 poczty, 222 WWW, 222 wydruków, 221

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

INDEKS

267

Shannon, Claude, 36, 39, 118 SI, 87 sieć każdy-z-każdym, 222 Peer-to-Peer, 222 równorzędna, 222 sieć działań, 138 sieć klient-serwer, 223 sieć komputerowa, 215 lokalna, 216 miejska, 217 rozległa, 216 zasięg, 216 sieci komputerowe, 32 sieci neuronowe, 35 składnia, 159 skrętka, 229 SOH, 224 sortowanie, 144 sortowanie przez wstawianie, 145 SP, 87 SS, 86 stan końcowy Maszyny Turinga, 71 Maszyny Turinga, 71 odrzucający Maszyny Turinga, 71 procesu, 194 aktywny, 194 gotowy, 194 wstrzymanie, 194 stan akceptujący Maszyny Turinga, 71 stos protokołów TCP/IP, 227 strona kodowa, 101 struktura fizyczna sieci, 216 struktury danych, 132 suma zbiorów, 136 suma kontrolna, 122

sumator arytmetyczny, 19 SVS, 209 switch, 232 symbol końcowy Maszyny Turinga, 71 symbol pusty Maszyny Turinga, 71 symbol taśmowy, 71 system Amoeba, 193 czasu rzeczywistego, 192 rygorystyczny, 192 dwójkowy, 19, 23, 47 dziesiętny, 45 liczbowy addytywny, 46 liczbowy pozycyjny, 46 operacyjny, 187 Amoeba, 201 BSD, 34 CP/M, 25, 207 FreeBSD, 212 jednoużytkownikowy, 190, 192 jednozadaniowy, 190 Linux, 34 Mac OS, 202 MS-DOS, 197, 204 NetBSD, 212 NetWare, 207 OpenBSD, 212 OS/390, 208 OS/400, 210 QDOS, 204 QNX, 192 rozproszony, 192 UNIX, 34 Unix, 192, 207, 211 wieloużytkownikowy, 192 wielozadaniowy, 192 Windows, 204 Windows 2000, 206

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

268

INDEKS

Windows NT, 206 Windows XP, 206 plików, 198 rzymski, 46 szesnastkowy, 56 Windows 95, 192 System V, 211 systemy osadzone, 165 szerokość magistrali, 80 sztuczna inteligencja, 35, 169 sztuczne sieci neuronowe, 35 szyfr Cezara, 31 tablica, 133 tablica przejść, 71 Tabulating Machine Company, 22 Tanenbaum, Andrew, 193, 201 TCP/IP, 33, 208, 227 teoria obliczalności, 29 teoria informacji, 36 teza Churcha, 75 Tomilnson, Roy, 33 topologia gwiazdy, 217 magistrali, 217 mieszana, 217 oczek pełnych, 217 pierścienia, 217 topologia sieci, 217 Torvalds, Linus, 212 tryb pracy krokowej, 88 TSO, 209 Turbo Pascal, 163 Turing, Alan, 22, 29, 69, 70, 130 tworzenie pliku, 199 typ, 166 liczbowy, 133

logiczny, 133 plikowy, 133 prosty, 133 rekord, 133, 135 tablicowy, 133 wyliczeniowy, 163 złożony, 133 zbiorowy, 133 znakowy, 133 układ sterowania magistrali, 83 wykonawczy, 83 ukrywanie informacji, 165 Unicode, 102 UNIVAC, 24 upraszczanie wyrażeń booleowskich, 45 urządzenia dostępowe, 230 transmisji, 229 usługa DNS, 228 usuwanie pliku, 199 utworzenie pliku, 137 uzupełnienie dwójkowe, 106 virtual machine, 181 wątek, 198 WAN, 216 warunek dyskretności, 130 efektywności, 130 jednoznaczności, 130 uniwersalności, 130 warunek stopu, 149 Wejście/Wyjście, 78 wiadomość, 118 wiadomość elektroniczna, 33 wielomian generacyjny, 125

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

INDEKS

269

wieloprogramowość, 196 wielozadaniowość, 196 bez wywłaszczania, 196 z wywłaszczaniem, 196 Windows 3.1, 205 Windows 3.11, 197 Windows 95, 205 Wirth, Niklaus, 162 wyrażenie, 166 WYSIWYG, 206 wyszukiwanie binarne, 147 liniowe, 147 połówkowe, 147 wzmacniaki, 232 XML, 30 złożoność obliczeniowa, 153 zadanie, 166, 198 zapewnienie komunikacji, 193 niezawodności, 193 zapis do pliku, 199 zapis stałoprzecinkowy, 109 zarządzanie pamięcią, 198 zasada dualności, 40 zbiór, 133, 136 zbiory funkcjonalnie pełne, 76 zegar systemowy, 78 znaki narodowe, 101 semigraficzne, 101 znaki alfanumeryczne, 100 Zuse, Konrad, 23

c 2001-2003 by P. Fulmański & Ś. Sobieski, Uniwersytet Łódzki. Wersja RC1 z dnia: 4 stycznia 2004

Sign up to vote on this title
UsefulNot useful