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.).

En enkel dator

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):

Disk

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 50 - 500 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):

Minne

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 512 Mbyte - 4 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):

CPU

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:

Pentium

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.:
 

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


Back