Datorer och programspråk
En dator är en maskin som kan utföra instruktioner av aritmetiskt/logisk karaktär. Utmärkande för datorer är att de är snabba och lätt kan fås att upprepa sekvenser av instruktioner. Datorer är också utrustade med minnen för att lagra temporära resultat, men de har även minnen för långtidsförvaring.
En dators fysiska komponenter, "elektroniken", kallas hårdvara (hardware). Instruktionerna samlas i program. Program, i största allmänhet, kallas mjukvara (software).
Det finns hundratals programspråk, några som jag själv använder är Fortran, C, C++ och Java. Matlab är inget programspråk , utan snarare en hel miljö för beräkningar. Man kan dock skriva program i Matlab med Matlabs eget programspråk.
Datorn "förstår" bara ett språk, som man brukar kallas datorns maskinspråk. Olika datorfabrikat (Sun, Intel etc.) har olika maskinspråk. Detta gör att program skrivna i maskinspråk inte går att flytta mellan olika typer av system (de är inte portabla, som man säger). Utmärkande för maskinspråket är att det är väldigt enkelt och är nära knutet till hårdvaran och att allt uttrycks med hjälp av binära tal (ettor och nollor). En del av programmet kanske ser ut så här (här skrivet i hexadecimal form):
74657874
002e7379
6d746162
002e7374
72746162
002e636f
Människor brukar aldrig programmera i maskinspråk eftersom det är så arbetskrävande. Det är också svårt att ändra i ett program. I bland programmerar man i det så kallade assemblerspråket, som ligger strax "ovanför" maskinspråket. Man kan då använda namn på operationer i stället för att ange hexadecimala tal.
Dessa assembler-rader adderar två tal, ungefär summa
= a
+ b
:
ldd [%o0], %f2
ldd [%o1], %f4
faddd %f2, %f4, %f2
std %f2, [%o2]
faddd
beräknar summan. Vi kommer att
återvända
till detta exempel och då förstå lite bättre vad
som
händer.
Det är tämligen jobbigt att programmera i assembler också, en liten summaberäkning gav ju upphov till fyra assembler-rader. Så, assemblerprogrammering används normalt t.ex. vid vissa elektroniktillämpningar där det är stora krav på snabbhet och kontroll.
För att det skall vara bekvämt att programmera och för att programmen skall bli portabla programmerar man normalt i så kallade högnivåspråk, som Fortran C och C++. Program skrivna i dessa språk måste översättas till maskinspråket. Översättningen görs av speciella program, så kallade kompilatorer (man säger att man kompilerar). När översättningen är gjort kan man så "köra" programmet (man säger normalt att man exekverar programmet).
När det gäller vissa språk, som t.ex. en del av Matlabprogrammen, så interpreteras koden i stället. Detta görs av en så kallad interpretator. Programmet översätts då lite eftersom, och inte som när man kompilerar där hela programmet görs om till maskinkod på en gång. En vanlig analogi är den mellan en simultantolk (interpretatorn) som översätter mening för mening, och en översättare (kompilatorn) som översätter en hel bok.
Java intar lite av en mellanställning. Man kompilerar först Javaprogrammet till så kallad bytekod (en enkel kod som inte är bunden till någon speciell dator; bytekoden är portabel med andra ord). Programmet exekveras sedan genom att bytekoden interpreteras (det kan också vara så att bytekoden kompileras till maskinkod som sedan exekveras).
Här kommer nu några rader om datorer varefter vi fortsätter med programspråken.
En enkel modell av en dator visas i följande bild (jag har inte ritat ut bildskärm, tangentbord osv.).
Sekundärminnet består i allmänhet av en eller flera hård-diskar. En sådan, med avtaget lock, kan se ut som på bilden nedan (längden är 10-15 cm i verkligheten):
Disken består av en eller flera metallskivor som är belagda med magnetiserbart material. Skivorna roterar med några tusen varv per minut. Det går att läsa data från och skriva data på skivan. Detta arbete utförs med hjälp av armen (se bilden) som kan röra sig över skivans yta. I armens ände finns ett litet läs- och skrivhuvud. Dina filer lagras på diskar och data finns kvar även om Du loggar ut från datorn. Det går att skriva över data som redan finns på skivan så om Du ändrar i en fil så finns inte den gamla versionen kvar. En typisk hård-disk kan lagra 20 - 100 Gbyte.
Primärminnet, ofta säger man bara minnet, består av elektroniska kretsar (inga rörliga delar) och det kan vara uppbyggt av kretsar som den på bilden nedan (längden är drygt 5 cm i verkligheten):
Primärminnet används för temporär lagring och minnesinnehållet byts normalt ut många gånger per sekund. Om man slår av datorn försvinner minnesinnehållet. Vanlig minnesstorlek är 128 Mbyte - 1 Gbyte.
CPUn (Central Processing Unit), processorn, är "hjärnan" i datorn. Det är här alla beräkningar sker, till exempel. Följande bild visar ovansidan och undersidan (med alla anslutningsstiften) av en CPU till en Sundator (sidlängden är drygt 5 cm i verkligheten):
Det mesta av bilden ovan är förpackning. Själva chipytan (kiselskivan) är mindre (10 mm sida kanske). Här är en uppförstorad bild av kiselskivan från en av Intels Pentiumprocessorer:
Bilden visar också funktionen hos olika delar av CPU-chipet. Vi återkommer strax till en del av dessa. En modern CPU innehåller flera tiotals miljoner transistorer.
De olika delarna av datorn, CPU, primär- och sekundärminne är ihopkopplade av så kallade bussar (bus på engelska). Du kan tänka Dig en buss som ett knippe elektriska ledningar, 32 stycken kanske. Eftersom vi har parallella ledningar kan vi överföra data (bitar) snabbare än om vi har en enda ledningen där vi måste skicka bitarna efter varandra (seriellt, som man säger).
En dator är tämligen oanvändbar utan det
stora programsystem
som kallas operativsystem (brukar förkortas OS). Det OS vi
använder, Red Hat Enterprise Linux, är en variant av unix.
PC-datorer kör ofta
någon form av Microsofts OS, t.ex. Windows XP eller Vista.
Ett operativsystem har många uppgifter, t.ex.:
Det "förmedlar" kontakten mellan användaren och hårdvaran.
Det sköter filhanteringen och ser till att filer lagras på och hämtas från disken.
Det fördelar resurser (tid och minne) mellan olika program som går i datorn.
Det ser till att vi kan ansluta till Internet.
Låt oss nu väldigt stora drag studera vad som händer
när
vi vill köra ett program i ett unix-system. Vi antar att
programmet
är kompilerat och att den så kallade exekverbara filen
(maskinkoden)
ligger i en fil på disken. I unix kör man program genom att
skriva
namnet på den exekverbara filen. När Du, till exempel, ger
kommandot
ls
för att lista namnen på Dina filer så
körs
ett kompilerat C-program. Den exekverbara filen heter ls
och
ligger i katalogen /bin
i filsystemet. Så, när
Du
skriver namnet på den exekverbara filen ser operativsystemet till
att
läsa in (eventuellt i omgångar) den exekverbara filen och
placera
koden (instruktionerna) i primärminnet. Därefter
låter
operativsystemet CPUn börja exekvera de instruktioner som ligger i
minnet.
CPUn kommer då att hämta instruktioner från minnet.
Det
görs av "Instruction Fetch"-enheten i processorn (se bilden ovan).
När
instruktionen har hämtats avkodas den (av Instruction Decode),
dvs.
CPUn tar reda på vilken typ av instruktion det är (addition,
logiskt
val etc.). Om det är en additionsoperation av decimaltal som skall
utföras kommer operationen att exekveras av en FPU (Floating Point
Unit).
Om det är heltal som skall adderas tar instruktionen hand om av
"integer
execution units" (som det står i bilden). De övriga delarna
i
bilden väntar vi med till kursens sista föreläsning.
"Floating
point numbers" är tal med decimalpunkt och eventuell exponentdel.
Du
kommer att höra mer om sådana tal på
föreläsningen.
Man kan nu undra hur CPUn får tag i operanderna (de tal som skall adderas). Jo, primärminnet lagrar inte bara instruktionerna utan det kan även lagra data (tal, tecken etc.) som programmet skall arbeta med. Tänk Dig minnet som en uppsättning numrerade fack. Man brukar dock säga adress i stället för nummer. Varje fack svarar normalt mot en byte (minnet kan adresseras på byte-nivå). För att lagra ett heltal tar vi normalt fyra bytes och ett decimaltal kräver oftast åtta bytes. CPUn kan nu hämta ett tal från primärminnet och lagra det i ett så kallat register (minnesutrymme) i CPUn. Det finns inte speciellt många register, kanske 32-64 stycken. När så summan är beräknad kan resultatet lagras tillbaks till primärminnet. Vi är nu mogna att se på assembler-raderna igen:
ldd [%o0], %f2
ldd [%o1], %f4
faddd %f2, %f4, %f2
std %f2, [%o2]
ldd
(load double) är en instruktion som hämtar
åtta
bytes från minnet. Minnesadressen som talet skall hämtas
från
anges, i exemplet, av innehållet i det register som heter
%o0
(konstigt namn) och talet placeras i register
%f2
som finns i FPUn. Därefter hämtas den andra
termen
och läggs i register %f4
. FPUn adderar sedan ihop
talen
(faddd
står för floating point add double) och
summan
läggs i %f2
. Den sista instruktionen, std
(store double) lagrar så värdet i den adress som anges av
register
%o2
.
Här följer slutligen ett kort C++-program som
läser in två
tal och sedan beräknar och skriver ut summan. Jag har också
visat
hur programmet kompileras (den kompilator jag använt heter
g++
, det finns flera olika) och jag exekverar så
slutligen
programmet genom att skriva namnet på den exekverbara filen,
a.out
. I unix-världen brukar en kompilator alltid
döpa
den exekverbara filen till a.out
(om man inte säger
något
annat).
% cat my_program.cc
#include <iostream>
using namespace std;
int main()
{
double a, b, summa;
cout << "Ange två tal: ";
cin >> a >> b;
summa = a + b;
cout << "Summan = " << summa << endl;
return 0;
}
% g++ my_program.cc
% a.out
Ange två tal: 23.56 18.7
Summan = 42.26
cat my_program.cc
listar programmet i fönstret
(%
är den så kallade prompten som talar om att
systemet
är redo att ta emot kommandon). De två första raderna i
programmet (include
-, och using
-raden)
ordnar diverse
definitioner som behövs för in- och utmatning.
double
-raden deklarerar tre åttabyes flyttal.
cout
-raden skriver ut lite text och på
cin
-raden läser vi in a och b. Därefter
beräknas
summan varefter lite text och summan skrivs ut. De kvarvarande raderna
struntar
jag i.
g++ my_program.cc
kompilerar programmet och lägger
den
exekverbara filen på a.out
. När jag så
skriver
a.out
startar programmet. Jag skriver in talen 23.56 och
18.7
varefter summan beräknas och skrivs ut.
Så här ser motsvarande sekvens ut för ett
Fortran90-program
(kompilatorn heter nu f90
):
% cat my_program.f90
program my_program
double precision :: a, b, summa
print*, "Ange två tal: "
read*, a, b
summa = a + b
print*, "Summan = ", summa
end
% f90 my_program.f90
% a.out
Ange två tal:
23.56 18.7
Summan = 42.26