Ohjelmointikielten periaatteet Syksy Antti-Juhani Kaijanaho

Samankaltaiset tiedostot
Konekielinen ohjelmointi

Ohjelmointikielten periaatteet Syksy Antti-Juhani Kaijanaho

Ohjelmointikielten periaatteet Syksy Antti-Juhani Kaijanaho

Johdanto Meta Kielten jaotteluja Historia. Aloitusluento. TIES542 Ohjelmointikielten periaatteet, kevät Antti-Juhani Kaijanaho

Ohjelmointikielten periaatteiden taustaa

TIEA255 Tietotekniikan teemaseminaari ohjelmointikielet ja kehitysalustat. Antti-Juhani Kaijanaho. 16. helmikuuta 2011

11/20: Konepelti auki

4. Lausekielinen ohjelmointi 4.1

Ongelma(t): Miten mikro-ohjelmoitavaa tietokonetta voisi ohjelmoida kirjoittamatta binääristä (mikro)koodia? Voisiko samalla algoritmin esitystavalla

815338A Ohjelmointikielten periaatteet Harjoitus 3 vastaukset

Koodi. Antti-Juhani Kaijanaho. 11. marraskuuta Sisältö

Luento 1 Tietokonejärjestelmän rakenne

Luento 1 Tietokonejärjestelmän rakenne. Järjestelmän eri tasot Laitteiston nopeus

ELM GROUP 04. Teemu Laakso Henrik Talarmo

Luento 1 Tietokonejärjestelmän rakenne

Java-kielen perusteet

Luento 1 Tietokonejärjestelmän rakenne. Järjestelmän eri tasot Laitteiston nopeus

Tällä kurssilla tarkastellaan ohjelmointikieliä. Lienee tarpeen yrittää rajata, mitä

Concurrency - Rinnakkaisuus. Group: 9 Joni Laine Juho Vähätalo

TIEP114 Tietokoneen rakenne ja arkkitehtuuri, 3 op. Assembly ja konekieli

1. Olio-ohjelmointi 1.1

Tietotekniikan valintakoe

Tiedon esitysmuodot. Luento 6 (verkkoluento 6) Lukujärjestelmät Kokonaisluvut, liukuluvut Merkit, merkkijonot Äänet, kuvat, muu tieto

815338A Ohjelmointikielten periaatteet Harjoitus 2 vastaukset

tään painetussa ja käsin kirjoitetussa materiaalissa usein pienillä kreikkalaisilla

TIEP114 Tietokoneen rakenne ja arkkitehtuuri, 3 op. Assembly ja konekieli

Pythonin alkeet Syksy 2010 Pythonin perusteet: Ohjelmointi, skriptaus ja Python

.NET ajoympäristö. Juha Järvensivu 2007

LOAD R1, =2 Sijoitetaan rekisteriin R1 arvo 2. LOAD R1, 100

Prolog kielenä Periaatteet Yhteenveto. Prolog. Toni ja Laura Fadjukoff. 9. joulukuuta 2010

2 Konekieli, aliohjelmat, keskeytykset

Algoritmit 1. Luento 3 Ti Timo Männikkö

815338A Ohjelmointikielten periaatteet

13. Loogiset operaatiot 13.1

815338A Ohjelmointikielten periaatteet Harjoitus 6 Vastaukset

Ongelma(t): Miten jollakin korkeamman tason ohjelmointikielellä esitetty algoritmi saadaan suoritettua mikro-ohjelmoitavalla tietokoneella ja siinä

TIEA241 Automaatit ja kieliopit, syksy Antti-Juhani Kaijanaho. 30. marraskuuta 2015

Ruby. Tampere University of Technology Department of Pervasive Computing TIE Principles of Programming Languages

Imperatiivisten ohjelmien organisointiparadigmojen. historia

Imperatiivisten ohjelmien organisointiparadigmojen historia

Kertausluento luennoista 1-3 1

Tähtitieteen käytännön menetelmiä Kevät 2009 Luento 4: Ohjelmointi, skriptaus ja Python

AS C-ohjelmoinnin peruskurssi 2013: C-kieli käytännössä ja erot Pythoniin

ITKP102 Ohjelmointi 1 (6 op)

Ohjelmoinnin perusteet Y Python

Ohjelmointi 1 / syksy /20: IDE

TIEA241 Automaatit ja kieliopit, syksy Antti-Juhani Kaijanaho. 5. marraskuuta 2015

TIE PRINCIPLES OF PROGRAMMING LANGUAGES Eiffel-ohjelmointikieli

Java-kielen perusteet

Tietokoneen toiminta, Kevät Copyright Teemu Kerola Järjestelmän eri tasot Laitteiston nopeus

4. Lausekielinen ohjelmointi 4.1

Kertausluento 1 (lu01, lu02, lu03) Tietokonejärjestelmän rakenne ttk-91 ja sillä ohjelmointi

Tutoriaaliläsnäoloista

Luento 1 (verkkoluento 1) Tietokonejärjestelmä

Alkuarvot ja tyyppimuunnokset (1/5) Alkuarvot ja tyyppimuunnokset (2/5) Alkuarvot ja tyyppimuunnokset (3/5)

Java-kielen perusteita

TIEA241 Automaatit ja kieliopit, syksy Antti-Juhani Kaijanaho. 8. syyskuuta 2016

Python-ohjelmointi Harjoitus 2

Luento 3 (verkkoluento 3) Ttk-91 konekielinen ohjelmointi. Ohjelman esitysmuoto Konekielinen ohjelmointi ttk-91:llä (Titokone, TitoTrainer)

Luento 1 (verkkoluento 1) Ohjelman sijainti Ohjelman esitysmuoto Laitteiston nopeus

TIEA241 Automaatit ja kieliopit, kevät Antti-Juhani Kaijanaho. 12. tammikuuta 2012

Laitteistonläheinen ohjelmointi

Lisää pysähtymisaiheisia ongelmia

Teemun juustokakku Rekisterien, välimuistin, muistin, levymuistin ja magneettinauhan nopeudet suhteutettuna juuston hakuaikaan juustokakkua tehdessä?

TIE448 Kääntäjätekniikka, syksy Antti-Juhani Kaijanaho. 27. lokakuuta 2009

ITKP102 Ohjelmointi 1 (6 op)

Ohjelmointitaito (ict1td002, 12 op) Kevät Java-ohjelmoinnin alkeita. Tietokoneohjelma. Raine Kauppinen

Harjoitustyö: virtuaalikone

Ohjelmoijan binaarialgebra ja heksaluvut

Chapel. TIE Ryhmä 91. Joonas Eloranta Lari Valtonen

811120P Diskreetit rakenteet

Eloisuusanalyysi. TIE448 Kääntäjätekniikka, syksy Antti-Juhani Kaijanaho. 16. marraskuuta 2009 TIETOTEKNIIKAN LAITOS. Eloisuusanalyysi.

Johdanto. TIE448 Kääntäjätekniikka, syksy Antti-Juhani Kaijanaho. 8. syyskuuta 2009 TIETOTEKNIIKAN LAITOS. Johdanto. Luennoija.

IDL - proseduurit. ATK tähtitieteessä. IDL - proseduurit

PERL. TIE Principles of Programming Languages. Ryhmä 4: Joonas Lång & Jasmin Laitamäki

TAMPEREEN TEKNILLINEN YLIOPISTO Digitaali- ja tietokonetekniikan laitos. Harjoitustyö 4: Cache, osa 2

Se mistä tilasta aloitetaan, merkitään tyhjästä tulevalla nuolella. Yllä olevassa esimerkissä aloitustila on A.

ATK tähtitieteessä. Osa 3 - IDL proseduurit ja rakenteet. 18. syyskuuta 2014

11.4. Context-free kielet 1 / 17

System.out.printf("%d / %d = %.2f%n", ekaluku, tokaluku, osamaara);

TIES325 Tietokonejärjestelmä. Jani Kurhinen Jyväskylän yliopisto Tietotekniikan laitos

13. Loogiset operaatiot 13.1

Ohjelmistojen mallintaminen, mallintaminen ja UML

Luento 4 (verkkoluento 4) Aliohjelmien toteutus

Tietojenkäsittelyteorian alkeet, osa 2

Ohjelmointikielten periaatteet. Antti-Juhani Kaijanaho


Ohjelmointikielen määritteleminen (tai edes tyhjentävä luonnehtiminen) on todella vaikeaa. Voisi yrittää vaikka näin:

815338A Ohjelmointikielten periaatteet Harjoitus 5 Vastaukset

Koottu lause; { ja } -merkkien väliin kirjoitetut lauseet muodostavat lohkon, jonka sisällä lauseet suoritetaan peräkkäin.

Luku 6. Dynaaminen ohjelmointi. 6.1 Funktion muisti

Aliohjelmatyypit (2) Jakso 4 Aliohjelmien toteutus

Käännös, linkitys ja lataus

Tieto ja sen osoite (3) Jakso 3 Konekielinen ohjelmointi (TTK-91, KOKSI) Osoitinmuuttujat. Tieto ja sen osoite (5)

Luento 4 Aliohjelmien toteutus

TIE448 Kääntäjätekniikka, syksy Antti-Juhani Kaijanaho. 13. lokakuuta 2009

Ehto- ja toistolauseet

5. HelloWorld-ohjelma 5.1

Ohjelmointikielten periaatteet. Antti-Juhani Kaijanaho

OHJELMOINTIKIELTEN PERIAATTEET ANTTI-JUHANI KAIJANAHO

Transkriptio:

Ohjelmointikielten periaatteet Syksy 2004 Antti-Juhani Kaijanaho

Copyright c 2002, 2004 Antti-Juhani Kaijanaho Tästä teoksesta saa valmistaa kappaleita ja sen saa saattaa yleisön saataviin, muuttamattomana tai muutettuna, käännöksenä tai muunnelmana, toisessa kirjallisuus- tai taidelajissa taikka toista tekotapaa käyttäen, mikäli kaikki seuraavat ehdot tulevat täytetyksi: (1) Kun teoksesta valmistetaan muutettu kappale tai muutettu teos kokonaan tai osittain saatetaan yleisön saataviin, on teoksen muuttajan nimi, salanimi tai nimimerkki ilmoitettava tekijän nimen rinnalla. (2) Kun teoksesta valmistetaan muutettu kappale tai muutettu teos kokonaan tai osittain saatetaan yleisön saataviin, on pidettävä huoli, ettei muutettua teosta voi hyvällä tahdolla sekoittaa alkuperäiseen. (3) Teoksen tekijän nimeä ei saa ilman hänen eri lupaansa käyttää teoksen markkinoinnissa. Erikseen korostettakoon, että teoksen tekijä ei vaadi maksua teoskappaleiden valmistamisesta tai teoksen saattamisesta yleisön saataviin. Teoskappaleen valmistaja tai teoksen yleisön saataviin saattaja saa periä tästä haluamansa maksun. Tekijänoikeuslaki (404/1961) 3 1 mom: Kun teoksesta valmistetaan kappale tai teos kokonaan tai osittain saatetaan yleisön saataviin, on tekijä ilmoitettava sillä tavoin kuin hyvä tapa vaatii.

Sisältö Esipuhe v Luku 1. Johdanto 1 1. Kielten jaotteluja 1 2. Kielen valinta 6 3. Ohjelmointikielten suunnitteluperiaatteita 7 4. Kielen määrittely 8 5. Toteutustekniikoista 10 Luku 2. Konekielinen ohjelmointi 11 1. Konekielet 11 2. Symboliset konekielet 13 3. Abstraktit koneet 14 4. Neloskielen idea 16 5. Systeemikutsut 21 6. Konekielellä ohjelmoinnista 22 Luku 3. Suoraviivaohjelmat 25 1. Suoraviivaohjelman elementit 25 2. Abstrakti syntaksi 33 3. Denotationaalinen semantiikka 36 4. Tyyppijärjestelmä 44 5. Konkreetti kielioppi 49 6. Toteutuksesta 55 Luku 4. Paikallinen kontrollivuon ohjaus 59 1. Kontrollivuo ja kontrollinohjaus 59 2. Testit 60 3. Valintalauseet 62 4. Toistolauseet eli silmukat 66 5. Vahtikomennot 71 6. Väitteet 73 Luku 5. Aliohjelmat 75 1. Kutsusekvenssit 75 2. Parametrinvälitysmekanismit 78 3. Staattinen ja dynaaminen vaikutusalue 79 4. Aktivaatiotietue 80 5. Vuorottaisrutiinit 82 iii

iv SISÄLTÖ 6. Ohjelmointi laskentana 82 Luku 6. Tyyppiteoriaa 91 1. Yksinkertaisesti tyypitetty λ-laskento 91 2. Alityypitys 100 3. Rekursiiviset tyypit 100 4. System F 100 5. Hindley Milner Damas -tyypitys 100 Luku 7. Samanaikaisuus 101 1. Jaettu muisti 101 2. Erilliset kommunikoivat prosessit 101 Luku 8. Abstraktit tietorakenteet ja modulit 103 Luku 9. Olioabstraktio 105 Kirjallisuutta 107 Liite A. Neloskoneen systeemikutsut 111 Liite B. Ohjelmointikielten historia 117 1. Kaksi ensimmäistä sukupolvea: ennen vuotta 1955 117 2. Automaattinen ohjelmointi ja ohjelmointikielten synty: 1955 1960 117 3. Baabelin torni: 1960-luku 119 4. Modernismi: 1970-luku 120 5. Postmodernismi: 1980-luku 121 6. Internetin nousu: 1990-luku 122 7. Sukupolvista 123 Liite C. Semanttiset alueet 125 1. Tuloalueet 127 2. Summa-alueet 128 3. Funktioalueet 129 Liite D. ALKEIS-suoran kääntäjä 133 1. Pääohjelma 133 2. Selain 137 3. Apuluokat 144 4. Lauseet 149 5. Tyypit 156 6. Lausekkeen jäsennin 163 7. Primäärilausekkeet 166 8. Operaattorilausekkeet 170 9. Muunnoslausekkeet 175

Esipuhe Tämä moniste on toinen yritykseni hahmottaa ohjelmointikielten alaa syventävän yliopistokurssin kokoiseksi suupalaksi. Ensimmäiseni kirjoitin syksyllä 2002 opettaessani Jyväskylän yliopiston ohjelmistotekniikan opiskelijoille Ohjelmointikielten periaatteita. Jotain tuosta monisteesta on jäljellä tässä monisteessa, mutta suurin osa on kokonaan uusiksi kirjoitettu, pääosin siksi, että olen tämän kahden vuoden kuluessa oppinut niin paljon uutta alasta. Toisaalta tuo moniste oli aina tarkoitukseni kirjoittaa uusiksi, tosin kuvittelin pääseväni tästä hommasta helpolla, toimittamalla vanhan tekstini luettavammaksi. Tämä moniste on kirjoitettu Ohjelmointikielten periaatteet -opintojakson tarpeisiin, kun luennoin sen toisen kerran syksyllä 2004. Kirjoitan tätä kolmenlaisten opiskelijoiden tarpeisiin: (1) Ohjelmistotekniikan tai yleisemmin tietotekniikan syventäviä opintoja tekevä opiskelija etsinee tästä monisteesta (2) Jokunen opiskelija on varmaankin kiinnostunut oman ohjelmointikielen suunnittelusta. Jokaiselta monisteen lukijalta oletan muutamia taitoja ja tietoja: (1) Ohjelmointitaito ja -kokemus on välttämätön. Tämä moniste ei opeta ohjelmointia! (2) Oletan, että lukijoille on tämän ajan valtaohjelmointikielistä ainakin Java tuttu. Jonkin verran käytän esimerkkeinä myös muita C-sukuisia kieliä (C, C++, C#, Cyclone). (3) Yliopistomatematiikan peruskäsitteitä käytän vapaasti olettaen, että ne eivät tuota lukijoille ongelmia. Tällaisia käsitteitä ovat joukko ja funktio sekä niiden perusoperaatiot. Lisäksi oletan, että lause- ja predikaattilogiikan alkeet ovat lukijoille tuttuja. XXX Christopher Strachey on kirjoittanut (lainaus Hoaren [14]): It has long been my personal view that the separation of practical and theoretical work is artificial and injurious. Much of the practical work done in computing, both in software and in hardware design, is unsound and clumsy because the people who do it have not any clear understanding of the fundamental design principles of their v

vi ESIPUHE work. Most of the abstract mathematical and theoretical work is sterile because it has no point of contact with real computing. Kuten Hoare, myös minä jaan tämän näkemyksen, ja senpä vuoksi tämä moniste käsittelee aihettaan sekä teorian että käytännön näkökulmasta. Pääosa luentomonisteesta jakaantuu lukuihin, joista jokaisessa tarkastellaan yhtä tätä monistetta varten kehitettyä ohjelmointikieltä. Kukin ohjelmointikieli määritellään formaalisti, ja kullekin niistä rakennetaan yksinkertainen kääntäjä. Nämä kielet muodostavat ALKEIS-kielten perheen. Esiversioita ovat lukeneet: Hilkka Heikkilä Markus Inkeroinen Tommi Kärkkäinen Ville Tirronen Antti Vuorenmaa Kiitokset myös kaikille vuoden 2002 kurssin opiskelijoille, erityisesti Joni Töyrylälle, joka antoi kurssin aikana tekemänsä muistiinpanot käyttööni.

LUKU 1 Johdanto How many times do I have to tell you? The right tool for the right job! Scotty elokuvassa Star Trek V Ohjelmointi on ongelmien ratkaisemista. Asiaa voi katsoa kahdelta kannalta: voi keskittyä ymmärtämään ongelmaa tai ratkaisumalleja. Voidaan ajatella, että maailma jakautuu kahteen osaan, ongelma-avaruuteen ja ratkaisuavaruuteen. Ongelma-avaruus koostuu kaikista mahdollisista ongelmista, ja ratkaisuavaruus koostuu kaikista mahdollisista ratkaisuista. Ohjelmoijan tehtävänä on siirtää ongelma (joka elää ongelma-avaruudessa) ratkaisuavaruuteen, jolloin siitä tulee ratkaisu. Ohjelmointikieli on ohjelmoijan pääasiallisin työkalu, ja kielen toteutus (kääntäjä tai tulkki) auttaa ohjelmoijaa ratkaisun luomisessa: kielen ominaisuudet ratkaisevat, kuinka syvälle ratkaisuavaruuteen ohjelmoijan tulee ongelmaansa viedä suunnitteluvaiheessa ja mistä lähtien kielen toteutus tekee sen ohjelmoijan puolesta. 1. Kielten jaotteluja Ohjelmointikieliä on mielettömän paljon. Jonkinlaista järjestystä tähän suureen joukkoon saadaan erilaisilla jaotteluilla. 1.1. Sukupolvet. Eräs tunnettu mutta nykyisin vähämerkityksinen jaottelu jakaa kielet eri sukupolviin: Ensimmäinen sukupolvi: koostuu konekielistä. Konekielellä tarkoitan tässä nyt ohjelman sitä muotoa, jonka kone pystyy sellaisemaan lukemaan. Seuraavassa on Hello World - ohjelma erään tietokoneen konekiellä (esitettynä 16-lukujärjestelmässä tavujonona): 980901013d6e185798020100980600036f686a656c6d612e6d6d7300 98070002f4ff00000000070100000000980400039807000748656c6c 980700076f2c2057980700076f726c6498070007210a0000980a00ff 0000000000000100980b0000203a4050104040204d20612069026e01 00812053207410100272010c82000000980c0008 1

2 1. JOHDANTO Käytännössä konekieliset ohjelmat suunniteltiin kirjoittamalla ohjelmat ensin paperilla symbolisesti, käyttämällä konekäskyistä muistikasnimiä. Myös vuokaavioita saatettiin harrastaa monimutkaisten ohjelmien kirjoittamisessa. Konetta varten ohjelma käännettiin käsin konekielelle. Ensimmäinen sukupolvi oli vallalla 1940-luvun lopulta 1950-luvun alkupuolelle. Toinen sukupolvi: syntyi, kun keksittiin, että voidaan kirjoittaa ohjelma suorittamaan yllämainittu käännöstyö muistikkaista konekielelle. Toinen sukupolvi koostuu siis symbolisista konekielistä. Edellä esitetty Hello World -ohjelma käännettiin seuraavasta symbolisella konekielellä kirjoitetusta ohjelmasta: argv IS $1 LOC #100 Main LDOU $255,argv,0 TRAP 0,Fputs,StdOut GETA $255,String TRAP 0,Fputs,StdOut TRAP 0,Halt,0 String BYTE ", world",#a,0 Toinen sukupolvi oli vallalla 1950-luvun puoleenväliin asti. Kolmas sukupolvi: syntyi vuonna 1955, kun FORTRAN kehitettiin. Lähes kaikki nykyisin ohjelmointikieliksi kutsumamme kielet ovat kolmannen sukupolven kieliä. Neljäs ja viides sukupolvi: esiintyvät puheissa toisinaan, mutta niiden sisältö riippuu täysin puhujasta. Tavallisimmin neljännen tai viidennen sukupolven kielellä tarkoitetaan joko sovelluskehittimiä (Delphi ym.) tai ohjelmoitavia sovellusohjelmia (Excel ym.). 1.2. Paradigmat. Edward Sapir ja Benjamin Whorf esittivät viime vuosisadan alkupuolella kielitieteellisen hypoteesin, joka tunnetaan nykyisin nimellä Sapirin Whorfin hypoteesi. Sen heikompi muoto, johon nykyisetkin kielitieteilijät enemmän tai vähemmän uskovat, kuuluu: Kieli vaikuttaa ajatteluun. Myös ohjelmoinnin yhteydessä tämä heikompi muoto on totta: se, mitä ohjelmointikieltä osaa, vaikuttaa siihen, miten ohjelmointiongelmia ryhtyy ratkaisemaan. Kaikki kielet eivät ole tässä suhteessa samanarvoisia: esimerkiksi Pascalin ja C:n tukema ajattelutapa on niin samanlainen, että kummastakin kielestä voi varsin vähällä vaivalla siirtyä toiseen. Vastaavanlainen samankaltaisuus pätee, tosin vähemmässä määrin, myös C++:aan ja Javaan. Jotkin kielet puolestaan suosivat niin erilaisia

1. KIELTEN JAOTTELUJA 3 ajattelutapoja, että ne tuntuvat aluksi aivan eri maailmoilta, eikä toisen osaamisesta ole kovin suurta apua toisen osaamisessa. Ajatustavoiltaan läheisten kielten katsotaan yleensä kuuluvan samaan kieliparadigmaan, mutta niiden väliset rajapyykit eivät ole useinkaan kovin selvät. Kieliparadigmoille sukua ovat ohjelmointiparadigmat, tosin aika usein näiden välistä eroa ei nähdä (myös tämän kirjoittaja on ne monesti sekoittanut!). Kukin ohjelmointiparadigma on oikeastaan idealisoitu versio niistä ajattelutavoista, joita vastaava kieliparadigma suosii. Alan Perlis [33] kirjoitti kaksi vuosikymmentä sitten: A language that doesn t affect the way you think about programming, is not worth knowing. Minä kääntäisin tämän väitteen toisin päin: A language that affects the way you think about programming is worth knowing. Itse asiassa väittäisin, että jokaisen ohjelmointiin vakavissaan suhtautuvan tulisi tuntea ainakin pari-kolme eri kieliparadigmoihin kuuluvaa kieltä eli oikeastaan pari-kolme eri ohjelmointiparadigmaa. Ohjelmointiparadigmoiksi lasketaan tavallisimmin ainakin seuraavat (huomaa, että nämä paradigmat eivät ole toisistaan erillisiä, vaan ne menevät osittain päällekkäin): Imperatiivisen ohjelmoinnin: (käskyohjelmoinnin) juuret ovat syvällä ratkaisuavaruudessa, von Neumannin arkkitehtuurin perusrakenteissa. Imperatiivinen ohjelmoija näkee maailman joukkona muistipaikkoja, joita muutellaan yksitellen von Neumannin pullonkaulaa pitkin. Imperatiiviselle ohjelmoinnille tyypillistä on tiedon muuttaminen in place (sitä kopioimatta), silmukoiden runsas käyttö, ohjelmakoodin korostuminen sekä käskymetafora. Deklaratiivisen ohjelmoinnin: (kuvausohjelmoinnin) juuret ovat ongelma-avaruudessa, ongelmien täsmällisessä kuvaamisessa yleensä matematiikan keinoin. Sen taustalla on yleensä vahva matemaattinen teoria, joka on sovitettu ohjelmoinnin tarpeisiin. Deklaratiiviselle ohjelmoinnille tyypillistä on tiedon muuttumattomuus (muutos tehdään tekemällä uusi, erilainen kopio), rekursion runsas käyttö, tietorakenteiden korostuminen sekä laskentametafora. Proseduraalisen ohjelmoinnin: (toiminto-ohjelmoinnin) perusabstraktio on aliohjelma. Proseduraalinen ohjelmoija keskittyy etsimään koodistaan tai suunnitelmastaan toimintoja, jotka toistuvat, ja tekee niistä aliohjelmia. Proseduraalinen ohjelmointi esiintyy yleensä imperatiivisen ohjelmoinnin kanssa yhdessä.

4 1. JOHDANTO Olio-ohjelmointi: näkee kaiken olioina ja niiden välisenä kommunikaationa. Olio-ohjelmoija keskittyy hahmottamaan olioita ja niiden välisiä suhteita. Myös olio-ohjelmointi on yleensä imperatiivista ohjelmointia, vaikka deklaratiivinen olioohjelmointi ei olekaan käsitteenä mahdoton ajatus. Funktio-ohjelmointi: (funktionaalinen ohjelmointi) on eräässä mielessä proseduraalisen ohjelmoinnin yleistys. Funktioohjelmoija näkee ohjelman (matemaattisena) funktiona. Funktioohjelmalle tyypillisiä ilmaisukeinoja ovat funktionaalit (funktiot, jotka ottavat toisia funktioita parametrikseen). Funktioohjelmointi lasketaan perinteisesti deklaratiiviseksi ohjelmoinniksi, vaikka oikeastaan deklaratiivinen funktio-ohjelmointi onkin harvinaisempaa kuin imperatiivinen funktio-ohjelmointi, jossa funktiot ovat oikeastaan vain yleistettyjä aliohjelmia. Logiikkaohjelmointi: perustuu ajatukseen, että looginen päättely voidaan mekanisoida (joka pitää paikkansa vain rajoitetusti). Logiikkaohjelmoija näkee ohjelman loogisena väitteenä, joka pitää todistaa. Logiikkaohjelma koostuu kyseisen väitteen muotoilusta sekä niiden oletusten muotoilusta, joita päättelyssä voidaan käyttää. Puhtaimmillaan logiikkaohjelmointi on täysin deklaratiivista, mutta käytännössä logiikkaohjelmointia harrastetaan myös imperatiivisesti, jossa päättelysäännöt vastaavat aliohjelmia. Olio-ohjelmoinnista ja funktio-ohjelmoinnista on kummastakin tiedekunnassamme laudaturkurssi (TJTL33 ja TIE328). Ohjelmointikielet jaotellaan usein myös yllämainitttuihin paradigmalokeroihin. Tämä on kuitenkin varsin ongelmallista. Jokaisella ohjelmointikielellä on omin paradigmansa, jota se tukee parhaiten, mutta hyvin monet kielet ovat aidosti moniparadigmakieliä siinä mielessä, että niitä voidaan käyttää mainiosti useammassakin ohjelmointiparadigmassa. Seuraavassa olen luetellut tyypillisiä ohjelmointikieliparadigmojen tunnusmerkkejä ja tyyppiesimerkkejä; kannattaa kuitenkin muistaa, että minkään paradigman tyyppiesimerkit eivät rajoitu pelkästään sen paradigman sisälle. Imperatiivisen kielen: (käskykielen) merkittävimmät tunnusmerkit ovat sijoituslause (tai -lauseke) ja peräkkäistäminen. Useimmissa kielissä peräkkäistäminen on implisiittistä: peräkkäin kirjoitetut lauseet suoritetaan peräkkäin. Niissäkin kielissä yleensä puolipiste on peräkkäistyksen merkkinä. Lähes kaikki kielet ovat imperatiivisia. Tyyppiesimerkkejä ovat Algol-suvun kielet (Pascal, C, C++, Java).

1. KIELTEN JAOTTELUJA 5 Deklaratiivisen kielen: (kuvailukielen) tunnusmerkki on se, että siitä puuttuu sijoituslause ja peräkkäistäminen. Deklaratiivisen kielen pääetu on, että siinä pätee viittausläpinäkyvyys (referential transparency), mikä helpottaa huomattavasti ohjelmien formaalia pyörittelyä käsin tai koneellisesti. Aidosti deklaratiiviset kielet ovat harvinaisia, ja niihin pätee Alan Perlisin [33] lausahdus: Purely applicative languages are poorly applicable. (Perlis käytti deklaratiivisista kielistä nimeä applikatiivinen kieli.) Esimerkkejä lienevät λ-laskenta ja John Backusin FP-kieli [7]. Viime vuosikymmenellä kehitettiin muutamia tekniikoita, joilla kielestä saatettiin tehdä eräässä mielessä sekä deklaratiivinen että imperatiivinen. Näitä tekniikoita käyttäviä kieliä ovat Clean ja Haskell. Proseduraaliset kielet: (toimintokielen) tukevat aliohjelmarakennetta. Tyyppiesimerkkejä ovat C ja Pascal. Myös C++ ja Java kuuluvat tähän luokkaan, vaikka ne ovatkin ensisijaisesti oliokieliä. Oliokielet: tukevat olioita, joilla on identiteetti ja toiminta (metodit) sekä toimintojen periminen oliolta toiselle (tai luokkapohjaisissa kielissä luokalta toiselle). Tyyppiesimerkkejä ovat Smalltalk, Python, Java ja C++. Funktiokielten: (funktionaalisten kielten) tärkein tunnusmerkki on tuki täysivaltaisille funktioille (funktioille, joita voi palauttaa funktiosta, antaa funktiolle parametriksi ja tallettaa tietorakenteisiin). Muita tyypillisiä mutta ei universaaleja ominaisuuksia ovat tuki halvalle tiedon kopioinnille, algebralliset tietotyypit ja niiden hahmonsovitus ja tehokas rekursio. Tyyppiesimerkkejä ovat Lisp, SML, Haskell ja Clean. Myös useimmat oliokielet voidaan sijoittaa tähän kategoriaan, sillä olioilla ja täysivaltaisilla funktioilla on paljon yhteistä ainakin pellin alla. 1.3. Ousterhoutin jaottelu. John Ousterhout [32] määrittelee järjestelmäohjelmointikielen ja juontokielen eron seuraavasti: järjestelmäohjelmointikieli: Nämä kielet on tarkoitettu tietorakenteiden ja yksittäisten komponenttien toteuttamiseen siirrettävästi ja korkeammalla tasolla kuin symbolisella konekielellä kirjoitettaessa. Ne käyttävät vahvaa staattista tyypitystä. Järjestelmäohjelmointikieliä ovat esimerkiksi PL/I, Pascal, C, C++ ja Java.

6 1. JOHDANTO juontokieli: Juontokielet olettavat, että on jo olemassa joukko hyödyllisiä komponentteja, jotka on kirjoitettu järjestelmäohjelmointikielillä. Juontokieliä käytetään komponenttien yhdistelemiseen, eräänlaisina liimoina (usein puhutaankin englanniksi glue languageistä). Juontokielet käyttävät tyypillisesti dynaamista tyypitystä - tyypityksen vahvuus vaihtelee. (Ousterhout puhui tyypittömyydestä mutta hän tarkoitti dynaamista tyypitystä.) Juontokieliä ovat esimerkiksi Perl, Python, Rexx, Tcl, Visual Basic ja Unixin kuoret. Ousterhoutin jaottelua on kovasti kritisoitu. 2. Kielen valinta Tyypilliset tavat valita toteutuskieli ovat me ollaan aina käytetty tätä kieltä tuo kieli on kaikkien huulilla; käyttäkäämme sitä Kumpaakin tapaa voidaan kenties perustella liiketoiminnallisilla syillä, mutta teknisesti molemmat tavat ovat huonoja. Varsinkin ison projektin suunnittelijoiden kannattaa pohtia kielen valintaa tosissaan, sillä sillä voi olla iso vaikutus projektin tuottavuuteen. Projektiorhanisaatiolle uuden ja oudon kielen valintaa pidetään merkittävänä riskitekijänä projektin onnistumiselle 1 Kannattaa huomata, että kielen valintaa ei voi tarkastella irrallaan kielen tukityökalujen (kääntäjä, tulkki, debuggeri yms.) tarkastelusta, mutta niitä ei silti pidä sekoittaa toisiinsa. Useimmissa kielisodissa, mitä esimerkiksi netissä käydään, kielet ja niiden tukityökalut sekoitetaan toisiinsa niin autuaasti, että vertailun mielekkyys katoaa kokonaan. Lawlis [26] on esittänyt prosessin, jolla toteutuskielen voi valita (näennäisen?) täsmällisesti vaatimusten mukaan. Burgess [8] on esittänyt kriteereitä, jota hänen mukaansa on syytä tarkastella valittaessa toteutuskieltä korkealaatuisen tuotteen tekemistä varten. Raymond [38] on käsitellyt kielen valintaa Unix-ympäristöön ohjelmaa rakentavan vapaaohjelmaprojektin näkökulmasta, mutta hänen havaintonsa ovat yleisemminkin sovellettavissa. Seuraavassa olen koonnut Lawlisin, Burgessin ja Raymondin pohjalta muutaman kysymyksen, joita voidaan käyttää kielten arvioinnissa (ei missään erityisessä järjestyksessä): (1) Tukeeko kieli hyvää ohjelmointityyliä ja tekeekö se huonon tyylin hankalaksi? 1 Eikö sitten olisikin parempi, että kaikki opettelisivat useita kieliä jo koulussa tai yliopistolla?

3. OHJELMOINTIKIELTEN SUUNNITTELUPERIAATTEITA 7 (2) Onko kielestä olemassa standardi? Miten tarkasti ja selkeästi se määrittelee kielen? Tukevatko kielen toteutukset standardia ja tekevätkö ne epästandardin koodin kirjoittamisen hankalaksi? (3) Onko kielestä olemassa laadukkaita toteutuksia? (4) Onko kieli riippumaton yksittäisistä käyttöjärjestelmistä ja laitteistoista? Toisin sanoen, onko se siirrettävä myös käytännössä? (5) Tukeeko kieli sovellusalueita, joilla projekti tai organisaatio toimii? (6) Auttaako vai haittaako se turvallisuuden (safety) ja luotettavuuden tavoittelua? (7) Ovatko sen toteutukset ajanmukaisia? (8) Onko sille olemassa tarvittavat prosessia tukevat työkalut? 3. Ohjelmointikielten suunnitteluperiaatteita Tony Hoare kirjoitti 1970-luvun alussa poleemisen artikkelin [13], jossa hän antoi mielestään objektiiviset kriteerit ohjelmointikielen hyvyydelle. Ne ovat seuraavat. Kielen tulisi auttaa ohjelman suunnittelussa. Kielen tulisi auttaa ohjelman dokumentoinnissa. Kielen tulisi auttaa virheiden jäljittämisessä. Näiden pohjalta Hoare nimeää viisi avainlausahdusta, jotka kuvaavat hyvää ohjelmointikieltä: yksinkertaisuus: Yksinkertaisuus on välttämätöntä, sillä muuten edes kielen kehittäjä ei voi arvioida tekemiensä valintojen seurauksia eikä kielen toteuttaja voi saavuttaa edes luotettavuutta saati sitten tehokkuutta ja nopeutta. Yksinkertaisuudesta hyötyy kuitenkin ensisijaisesti kielen käyttäjä. turvallisuus: Turvallisuudella Hoare tarkoittaa sitä, että kielellä kirjoitettu ohjelma ei saa missään tilanteessa edes ohjelmoijan virheen seurauksena käyttäytyä hallitsemattomasti (esimerkiksi sekoittaa käyttöjärjestelmän tai muistinhallinnan tietorakenteita) tai eri järjestelmissä eri tavoilla. Lisäksi kielen syntaksin tulee olla sellainen, ettei yksinkertainen kirjoitusvirhe muuta ohjelman merkitystä merkittävästi. Nykyisinä Internet-ohjelmoinnin aikoina turvallisuus on entistä merkittävämpi, sillä juuri ohjelmointikielten turvallisuuden puute on ollut merkittävä tietomurtoja edistävä tekijä. lyhyt kääntymisaika: Käännösajan (se aika, joka kestää koodin muuttamisen päättymisestä siihen, kun muutettua ohjelmaa voi ajaa) tulee olla mahdollisimman lyhyt, jotta virheenjäljitys ja -korjaus onnistuisi mahdollisimman hyvin. Tämä

8 1. JOHDANTO vaikuttaa myös kielensuunnittelijan työhön, sillä eri ominaisuudet vaativat eri määrän työtä käännösaikana. ajonaikainen tehokkuus: Ohjelmointikieli on työkalu ja siksi sen tulee olla tehokas. Tässä pätee sama kuin edellisessäkin kohdassa: kielensuunnittelija voi vaikuttaa paljon siihen, että onko hänen kielensä toteutettavissa tehokkaasti. Tämä ja edellinen vaatimus ovat osittain keskenään ristiriitaisia, sillä pääsääntöisesti ajonaikainen tehokkuus paranee, kun käännösaikaa kasvatetaan lisäämällä kääntäjään optimointeja. Kuitenkin kielen suunnittelija voi vaikuttaa paljon siihen, että kuinka paljon näitä optimointeja välttämättä tarvitaan. luettavuus: Ohjelman luettavuuden tärkeyden pitäisi olla kaikille selvä. Hoare antaa myös artikkelissaan monia ohjeita siitä, miten hänen mielestään näihin tavoitteisiin voitaisiin päästä, mutta monet niistä ovat jo varsin kyseenalaisia. Silti kielensuunnittelusta kiinnostuneen kannattaa tutustua Hoaren artikkeliin kokonaisuudessaan. Hoaren listaan voimme lisätä vielä yhden avainlausahduksen: stabiilius. Tuotantokäytössä olevan kielen tulee olla saavuttanut tietynasteinen kypsyys. Kielen käyttäjien tulee voida luottaa siihen, että juuri se kielen versio on validi vielä kahden, viiden ja aika usein vielä kymmenenkin vuoden kuluttua sekä ei pelkästään Windowsissa vaan myös Unixissa, Macissa ja vaikkapa GNU:ssa. Tässä auttaa voimakkaasti kielen standardointi, sillä standardi antaa stabiiliutta ja antaa yhteisen pohjan kielen eri toteutuksille. TEHTÄVÄ 1.1. Ovatko nämä kriteerit tarkoituksenmukaisia? Ovatko ne kattavia? Mitä sinä pidät tärkeänä ohjelmointikielessä? TEHTÄVÄ 1.2. Löydätkö vastaavuuksia ohjelmointikielen ja ohjelman hyvyyden arviointiin käytettävien kriteereiden väliltä? 4. Kielen määrittely Kielestä on aina syytä kirjoittaa määrittelydokumentti (specification) samaan aikaan kun sitä ensimmäistä kertaa toteutetaan tai heti sen jälkeen. Määrittelydokumentti kertoo ohjelmoijalle, mitä kieli tarjoaa, ja toteuttajalle, mitä hänen ohjelmansa tulee tehdä. Tämän dokumentin lisäksi on toki hyvä olla ohjelmoijan opas erikseen, koska määrittelydokumentti on ollessaan hyödyllinen tavallisesti suhteellisen vaikealukuinen. Määrittelydokumentissa kuvataan kaikki oleellinen kielestä riittävällä tarkkuudella. Tässä on valittavissa monta tarkkuustasoa: voidaan esittää asia epämuodollisesti tai sitten hieman muodollisemmin. Kielioppi esitetään nykyisin lähes aina täysin muodollisesti käyttäen tiettyjä formalismeja (tavallisimmin Backus-Naur Form (BNF)

4. KIELEN MÄÄRITTELY 9 tai sen muunnokset EBNF ja ABNF). Merkitysoppi on tapana ilmaista sanallisesti, koska olemassaolevat merkitysopin muodolliset kuvausmenetelmät (aksiomaattinen, denotationaalinen ja operationaalinen) ovat varsin vaikeita kirjoittaa ja lukea. Myös mahdollinen tyyppijärjestelmä voidaan kuvata muodollisesti esittelemällä se päättelysääntöjoukkona. Palaamme näihin kaikkiin formalismeihin myöhemmin. Uuden ohjelmointikielen määrittelydokumenttia levitetään tavallisimmin kielen alkuperäisen toteutuksen mukana. Toisinaan erityisesti yliopistoissa ja tutkimuslaitoksissa luotujen kielten määrittelydokumentti julkaistaan laitosraporttina tai jopa kansainvälisenä tutkimusartikkelina, ja monien määrittelyjen nimi onkin muotoa Report on the X Language Y, missä X on jokin kieltä Y kuvaava lausahdus. Tavallista on myös julkaista kielten paranneltuja versioita, jolloin saattaa tulla kyseeseen julkaista myös Revised Report on the X Language Y. Scheme on tästä mainio esimerkki: Scheme-raportista on nyt voimassa viides revisio [20]. Kun kieli on saavuttanut tietyn kypsyystason ja käyttäjäjoukon, aletaan tavallisesti puuhata sen standardointia. Standardointia harrastavat monet järjestöt, tärkeimpinä ISO (kansainvälinen standardointijärjestö, http://www.iso.ch/), usein yhteistyössä IEC:n (kansainvälinen sähköteknillinen standardointijärjestö, http://www.iec.ch/) kanssa, ECMA (informaatio- ja kommunikaatojärjestelmiä standardoiva eurooppalainen järjestö, http://www.ecma.ch/) sekä IEEE (Institute of Electrical and Electronics Engineers, http://www.ieee.org/). Takavuosina merkittävässä asemassa olivat myös Yhdysvaltain kansallisen standardointijärjestön ANSI:n standardit, mutta nykyisin lähes kaikki ohjelmointikieliin liittyvät ANSI-standardit ovat myös kansainvälisiä ISO/IEC-standardeja. Usein tosin kielten standardointi tehdään näiden virallisten tahojen ulkopuolella (esimerkiksi Haskell-kielen standardointi tehtiin käyttäjä- ja toteuttajayhteisön sisällä, ja kielen määrittely julkaistiin kirjana [34] ja lehtiartikkelina [35]). Seuraavassa on listattu joidenkin tunnettujen kielten standardit: C-kielestä on kolme standardia: ANSI X3.159-1989, ISO/IEC 9899:1990 ja ISO/IEC 9899:1999. Kaksi ensimmäistä ovat oleellisesti sama standardi ja käsittelevät sitä C:n versiota, jota on tapana kutsua ANSI C:ksi, C89:ksi tai C90:ksi. Kolmas standardi käsittelee C:n uutta versiota C99:ää. C++-kielen standardi on ISO/IEC 14882:1998. Scheme-kielen standardi on IEEE Std 1178-1990. C#-kielen standardi on ECMA-334. BASIC-kielestä on neljä standardia: ANSI X3.60-1978, ECMA- 116, ANSI X3.113-1987 (R1998) ja ISO/IEC 10279:1991 (E). Ne kaikki käsittelevät hieman eri versiota kielestä. Java-kielestä ei ole standardia.

10 1. JOHDANTO 5. Toteutustekniikoista Ohjelmointikielten toteutukset jaetaan kahteen pääluokkaan: on tulkkeja (interpreter) ja kääntäjiä (compiler). Rajanveto näiden luokkien välillä on hankalaa. Räikeimmissä tapauksissa se on suhteellisen helppoa: tulkki lukee ohjelmatekstiä ja suorittaa sitä lukiessaan, kääntäjä tuottaa ohjelmatekstistä konekielisen version, joka ajetaan erikseen. Käytännössä kuitenkaan puhtaita tulkkeja ei ole. Varsin suosittu tapa toteuttaa tulkki on rakentaa se kaksivaiheiseksi. Tulkkiin sisältyy eräänlainen esikääntäjä, joka kääntää ohjelman jonkin kuvitteellisen koneen konekielelle. Tämä kuvitteellinen eli virtuaalinen kone on suunniteltu siten, että sille on kyseinen kieli helppo kääntää ja toisaalta sitä on helppo simuloida ohjelmallisesti. Tulkin toinen osa on sitten tämän virtuaalisen koneen simulaattori, jota usein sanotaan virtuaalikoneeksi, joka sitten tosiasiassa tulkkaa tuon käännetyn tuloksen. Tämän virtuaalisen koneen konekieli on eräänlainen välikieli, jota tulkki käyttää apunaan. Sitä kutsutaan toisinaan tavukoodiksi (bytecode). Ohjelmointikielten kääntäjän (ja virtuaalikoneratkaisua käyttävän tulkin esikääntäjäosan) perusrakenne on kaksijakoinen: sillä on lähdekielestä riippuva etupää ja kohdekielestä (kohdekoneen konekieli) riippuva takapää. Etupää koostuu neljästä vaiheesta: leksikaalinen analyysi, syntaktinen analyysi, semanttinen analyysi ja välikoodin (intermediate code) tuottaminen. Takapää koostuu optimoimesta ja tuloskoodin (object code) tuottimesta. Tavallisesti osa näistä vaiheista tapahtuu limittäin, ei erillisinä vaiheina välituloksineen. Kääntäjätekniikan (jota myös tulkeissa käytetään) perusteos on ns. lohikäärmekirja [4], mutta se on suhteellisen vanha ja keskittyy lähinnä käskykielten kääntämiseen. Uudempi, ihan kohtuullinen kirja on Andrew Appelin Modern compiler implementation in Java [6], josta on olemassa versiot myös C- ja ML-kielille. Funktiokielten toteutusta käsittelee Peyton Jones [36].

LUKU 2 Konekielinen ohjelmointi Tässä luvussa käsitellään konekielistä ohjelmointia ja esitellään kuvitteellinen kone, jonka avulla monisteessa myöhemmin esitellään eri kieliominaisuuksien toiminta. 1. Konekielet Konekielellä tarkoitetaan sitä kieltä, jota tietokone ymmärtää suoraan. Koska kaikki nykytietokoneet perustuvat samaan ideaan siitä, miten tietokone toimii (ns. von Neumannin kone), ovat myös eri koneiden konekielet suurpiirteisesti katsoen samanlaisia. Yhteistä kaikille konekielille on se, että ne eivät ole tekstuaalisia vaan asiat ilmaistaan konekielellä koodaamalla ne jollakin annetulla tavalla tavujonoksi 1, jota sitten koneen prosessori tulkitsee. Yhteistä myös kaikille konekielille on se, että ohjelmat voidaan jäsentää dataksi ja koodiksi, ja koodi voidaan edelleen jäsentää jonoksi käskyjä (engl. instructions), jotka viittaavat dataan ja muualle koodiin. Käskyt puolestaan voidaan luokitella karkeasti seuraavasti: (1) datan siirto (2) kokonaislukuaritmetiikka (3) vertailu (4) ehdolliset hypyt (5) ehdottomat hypyt (6) järjestelmäkutsut (7) sovellusaluekohtaiset käskyt (8) järjestelmäkäskyt Näistä viisi ensimmäistä käskyluokkaa ovat jatkuvassa käytössä kaikissa ohjelmissa. Järjestelmäkutsujakin käytetään kaikissa ohjelmissa, mutta niiden käyttötaajuus vaihtelee. Järjestelmäkäskyjä eivät tavalliset ohjelmat kykene käyttämään, ne ovat olemassa käyttöjärjestelmien tarpeita varten. Useimmat tietokoneet tarjoavat lisäksi koko joukon erilaisia sovellusaluekohtaisia käskyjä; näistä lähes yleiskäyttöisen aseman on saanut liukulukukäskyt, pöytäkoneissa tavallisia 1 Tavu (engl. byte) ei välttämättä ole kahdeksanbittinen, vaikka nykykoneissa näin onkin. Huolitellussa kielenkäytössä tavu tarkoittaa koneen pienintä osoitettavissa olevaa tietoyksikköä; jos halutaan puhua nimenomaan kahdeksanbittisestä tietoyksiköstä, korrekti termi on oktetti. 11

12 2. KONEKIELINEN OHJELMOINTI ovat erilaiset reaaliaikagrafiikan piirtämisessä tarvittavat laskentamenetelmät (esimerkiksi SIMD ja MMX). Yleensä konekielissä on jokin varsin yksinkertainen mekanismi, jolla eri käskyt voidaan erottaa toisistaan. Vanhoissa, ns. CISC-malleissa 2 käskyn aloitti yleensä ns. operaatiokoodi (engl. opcode), joka yleensä vei yhden tavun. Operaatiokoodi ilmaisi, mistä käskystä oli kyse sekä mistä löytynyttä dataa käsky käsitteli. Operaatiokoodia seurasi vaihteleva määrä operandeja, jotka tarkensivat datan sijaintia (antaen esimerkiksi kiinteän osoitteen, mistä data löytyy). Uudemmissa RISC-maleissa 3 käskyt ovat yleensä kiinteämittaisia (esimerkiksi neljän tavun mittaisia riippumatta käskystä) sekä sisältävät operaatiokoodin ja yhden tai useamman operandin. Vaikka konekielet ovatkin suurpiirteisesti katsoen samanlaisia, niiden välillä on huomattavia eroja yksityiskohdissa. Pääsääntöisesti vain samanlaiselle koneelle tarkoitettu konekielinen ohjelma toimii toisessa koneessa. Eri prosessorit jaotellaan sen mukaan, toimivatko toiselle kirjoitetut ohjelmat toisessa prosessorissa. Keskeinen käsite on käskykanta-arkkitehtuuri (engl. instruction set architecture), joka määrää, mitä käskyjä prosessori tukee. Tällä hetkellä merkittävät käskykanta-arkkitehtuurit ovat IA-32: Ehkäpä kaikkien aikojen menestynein käskykanta-arkkitehtuuri: Lähes kaikki nykyiset pöytäkoneet ja kannettavat perustuvat IA-32:lle. Myös monet vähäistä tai keskinkertaista tehoa vaativat palvelimet käyttävät IA-32:a. Ensimmäinen prosessori, joka tuki tätä käskykantaa, oli Intelin 80-386 vuodelta 1985. Myöhemmät prosessorit ovat laajentaneet käskykantaa, mutta sen ydin on edelleen sama, jota 80-386 tuki. IA-32:ta tukevia prosessoreita valmistavat lähinnä Intel ja AMD, mutta on muitakin valmistajia. Kuten tietokonealalla yleensäkin on tapana, markkinajohtaja IA-32 ei ole teknisesti paras markkinoilla olevista vaihtoehdoista. AMD64: AMD:n suunnittelema uusi (2003) käskykanta-arkkitehtuuri, joka on IA-32:n laajennus samassa mielessä kuin IA-32 itse oli aiempien Intel 8086- ja 80286-arkkitehtuurien laajennus. Vaikka AMD64 on varsin nuori, sen odotetaan seuraavien vuosien korvaavan IA-32:n. Kuten IA-32:ta, AMD- 64:ää tukevia prosessoreita valmistavat sekä AMD (Athlon64 ja Opteron) että Intel (EM64T). 2 Complex Instruction Set Computer; valtaosassa pöytäkoneita käytettävä Intelin 32-bittinen prosessoriarkkitehtuuri IA-32 (80386, Pentium ym.) kuuluu tähän luokkaan historiansa vuoksi. 3 Reduced/Regular Instruction Set Computer; näitä ovat käytännössä kaikki 1980-luvun loppupuolella tai sitä myöhemmin puhtaalta pöydältä suunnitellut prosessoriarkkitehtuurit; esimerkiksi Sparc, Alpha ja mobiililaitteista usein löytyvä ARM.

2. SYMBOLISET KONEKIELET 13 PowerPC: Applen, IBM:n ja Motorolan yhteishankkeena 1990- luvun alussa lanseeraama käskykanta-arkkitehtuuri, jota käyttävät nykyisin lähinnä uudehkot Macintosh-koneet. ARM: ARM on suhteellisen suurta laskentatehoa tarvitsevien sulautettujen järjestelmien (esimerkiksi mobiilipäätelaitteet) ja kämmenmikrojen prosessorien käskykanta-arkkitehtuuri. ARM-yhteensopivia prosessoreita valmistaa moni mikroprosessorivalmistaja. MIPS: Merkittävä suurta laskentatehoa vaativien sulautettujen ja mobiilien järjestelmien prosessorien käskykanta-arkkitehtuuri; käytössä myös SGI:n työasemien prosessoreissa. Sparc: Suosittu käskykanta-arkkitehtuuri palvelinkäytössä. Alkuperäinen Sparc on 32-bittinen; sen 64-bittinen laajennus on nimeltään UltraSparc. 2. Symboliset konekielet Konekieltä ei juuri koskaan kirjoiteta käsin. Konekieliset ohjelmat syntyvät yleensä varsinaisten ohjelmointikielten kääntäjien vasteena. Toisinaan kuitenkin ohjelmoija haluaa kirjoittaa samalla abstraktiotasolla, jonka konekieli tarjoaa. Tällaisia tilanteita varten jokaiseen konekieleen liittyy symbolinen konekieli (engl. assembly). Se on konekielisen ohjelman tekstuaalinen esitysmuoto, jossa kukin käsky on omalla rivillään, osoitteiden sijasta käytetään symbolisia nimiä ja jossa käskyn toimittama operaatio ja sen operandit on selvästi erotettu toisistaan. Symbolisella konekielellä kirjoitetun ohjelman kääntämiseen varsinaiselle konekielelle tarvitaan oma ohjelmansa (tosin onnistuu se kyllä käsinkin), ns. assembler 4. Lähes kaikilla symbolisilla konekielillä on sama perusrakenne. Kuten edellä jo todettiin, kukin käsky on omalla rivillään. Pystysuunnassa symbolisella konekielellä kirjoitettu ohjelma jakautuu sarakkeisiin, joiden avulla käskyn eri osat jäsentyvät. Tavallisesti käskyn osoitetta edustava symbolinen nimi, ns. label, on ensimmäisessä sarakkeessa. Jos sellaista ei ole, ensimmäinen sarake on tyhjä. Toinen sarake on varattu käskyn muistikkaalle (engl. mnemonic) yleensä kolmikirjaiminen, joskus kaksi- tai nelikirjaiminen tunnus, joka nimeää käskyn. Kolmas, neljäs ja joskus viideskin sarake on käskyn operandien käytössä. Tämän tarkemmin ei symbolisia konekieliä voi kuvata yleisesti, sillä jokaisella symbolisella konekielellä on omat merkintätapansa. Tavallisesti kullakin käskykanta-arkkitehtuurilla on täsmälleen yksi symbolinen konekieli; IA-32 on tästä poikkeus, sillä on nimittäin niitä kaksi: AT&T:n ja Intelin symboliset konekielet. 4 Huomaa: assembly on kieli, assembler ohjelma.

14 2. KONEKIELINEN OHJELMOINTI unsigned long str2ulong(char const * s) { unsigned long rv = 0; for (/**/; *s!= 0; s++) { rv = 10 * rv + (*s - '0'); } return rv; } KUVA 1. Yksinkertainen aliohjelma C-kielellä.text.p2align 4,,15.globl str2ulong.type str2ulong, @function str2ulong: pushl %ebp xorl %edx, %edx movl %esp, %ebp movl 8(%ebp), %ecx movzbl (%ecx), %eax testb %al, %al je.l8.p2align 4,,15.L6: movsbl %al,%eax incl %ecx leal (%edx,%edx,4), %edx leal -48(%eax,%edx,2), %edx movzbl (%ecx), %eax testb %al, %al jne.l6.l8: popl %ebp movl %edx, %eax ret KUVA 2. Yksinkertainen aliohjelma IA32-arkkitehtuurin symbolisella konekielellä (AT&T) Kuvissa 2, 3 ja 4 on kuvattu yksinkertainen aliohjelma kolmella eri symbolisella konekielellä. Vastaava C-kielinen aliohjelma on kuvassa 1. 3. Abstraktit koneet Abstrakti kone on käskykanta-arkkitehtuuri, jota ei ole tarkoitettu toteutettavaksi suoraan laitteistossa. Jos abstaktilla koneella on ohjelmistototeutus, se on virtuaalikone.

3. ABSTRAKTIT KONEET 15.section ".text".align 4.global str2ulong.type str2ulong, #function.proc 016 str2ulong:!#prologue# 0!#PROLOGUE# 1 ldub [%o0], %o4 sll %o4, 24, %g1 cmp %g1, 0 be.ll8 mov 0, %o3 mov %g1, %o4.ll6: add %o0, 1, %o0 ldub [%o0], %g1 sra %o4, 24, %o5 sll %g1, 24, %g1 orcc %g1, 0, %o4 sll %o3, 2, %g1 add %g1, %o3, %g1 add %g1, %g1, %o3 add %o3, %o5, %g1 bne.ll6 add %g1, -48, %o3.ll8: retl mov %o3, %o0 KUVA 3. Yksinkertainen aliohjelma UltraSparc-arkkitehtuurin symbolisella konekielellä Ohjelmoijan näkemä tietokone on harvoin täsmälleen se, jonka tietokoneen laitteisto toteuttaa. Yleensä ohjelmoija voi luottaa käyttöjärjestelmän palveluihin. Käyttöjärjestelmä siis rakentaa fyysisen tietokoneen päälle eräänlaisen virtuaalisen tietokoneen, siis virtuaalikoneen. Joskus ohjelmoijan kirjoittamaa ohjelmaa suorittaa simulaattori, joka imitoi jotain abstraktia konetta; tällöin ohjelmoijan näkemä kone on tyystin toinen kuin mitä laitteistosta voisi päätellä. Tällainen tilanne on esimerkiksi silloin, kun Java-ohjelmaa ajetaan Java-virtuaalikoneessa. Tämän luvun loppuosa esittelee erään abstraktin koneen, joka on eräänlainen todellisten koneiden idealisaatio, ja se tarjoaa jonkin verran myös käyttöjärjestelmältä odotettavia palveluita. Ideana on, että myöhemmin tässä monisteessa voidaan sitten pohtia kehittyneempien ohjelmointikielten toteutusta tämän abstraktin koneen tarjoamassa viitekehyksessä ilman, että tarvitsee miettiä sellaisia kääntäjätekniikan inhottavuuksia kuten rekisterien valintaa.

16 2. KONEKIELINEN OHJELMOINTI.section ".text".align 2.globl str2ulong.type str2ulong,@function str2ulong: mr 11,3 lbz 0,0(11) li 3,0 cmpwi 0,0,0 bclr 12,2.L6: slwi 9,3,3 add 9,9,3 add 9,9,3 addi 9,9,-48 add 3,0,9 lbzu 0,1(11) cmpwi 0,0,0 bc 4,2,.L6 blr KUVA 4. Yksinkertainen aliohjelma PowerPC-arkkitehtuurin symbolisella konekielellä 4. Neloskielen idea Neloskieli on erään abstraktin koneen symbolinen konekieli. Ennen kuin voidaan puhua itse kielestä, pitää hahmotella kyseinen abstrakti kone. Olkoon se nimeltään Neloskone. Neloskoneen rakenne on kaksijakoinen: sillä on muisti ja prosessointiyksikkö. Neloskoneen muisti on 2 32 tavun taulukko; tavun koko on tavanomaiset kahdeksan bittiä. Jokaisella muistin tavulla on yksikäsitteinen osoite, joka on 32-bittinen etumerkitön kokonaisluku. Muisti jakautuu viiteen osaan: tekstimuistiin, datamuistiin, nollamuistiin, käyttämättömän muistin alueeseen sekä pinomuistiin. Kukin muistin osa on yhtenäinen osa muistia. Tekstimuisti alkaa aina osoitteesta 0, datamuisti heti tekstimuistin jälkeen ja nollamuisti heti datamuistin jälkeen. Nollamuistin jälkeen alkaa käyttämättömän muistin alue, jota ei saa lukea ja johon ei saa kirjoittaa. Muistin lopussa on pinomuisti, joka päättyy osoitteeseen 2 32 1. Kunkin

4. NELOSKIELEN IDEA 17 muistialueen eli segmentin koko voi vaihdella käynnistyskerrasta toiseen; nollamuistin ja pinomuistin koko voi vaihdella myös suorituksen aikana. Tekstimuistiin kirjoittaminen on kiellettyä; muistipaikan, jonka osoite on 0, lukeminen on myös kiellettyä. 5 Vaikka muisti onkin tavutaulukko, sitä tavallisesti käsitellään neljän tavun eli sanan (engl. word) yksiköissä 6. Sana voidaan tilanteesta riippuen tulkita etumerkittömäksi 32-bittiseksi kokonaisluvuksi, etumerkilliseksi 32-bittiseksi kokonaisluvuksi tai yksinkertaisen tarkkuuden liukuluvuksi. Jos sanan tavut ovat x 0,..., x 3, missä x 0 on se tavu, jonka osoite on pienin, ja x 3 on se tavu, jonka osoite on suurin, sana tulkitaan seuraavasti etumerkittömäksi kokonaisluvuksi: Sanan lukuarvo etumerkittömänä kokonaislukuna on 3 i=0 256 i x i, toisin sanoen vähiten merkitsevä tavu on sanassa ensimmäisenä. Neloskone on siis little-endian; jos eniten merkitsevä tavu olisi ensimmäisenä, se olisi big-endian. etumerkilliseksi kokonaisluvuksi: Kun sana tulkitaan etumerkilliseksi kokonaisluvuksi, on sen etumerkki negatiivinen, jos sanan eniten merkitsevä bitti on 1 ja positiivinen, jos sanan eniten merkitsevä bitti on 0. Jos sanan etumerkki on negatiivinen, sanan itseisarvo saadaan, kun siihen sovelletaan ensin biteittäistä negaatiota, minkä jälkeen se tulkitaan yllä esitetysti etumerkittömäksi kokonaisluvuksi ja tulokseen lisätään 1. Positiivisen luvun arvon saamiseksi riittää soveltaa sanaan suoraan yllä esitettyä etumerkittömän luvun menetelmää. Toisin sanoen Neloskone käyttää kahden komplementti -esitystä (engl. two s complement). yksinkertaisen tarkkuuden liukuluvuksi: IEEE 754 -standardin [1] mukaisesti. Sanasta voidaan käsitellä myös vähemmän merkitsevää puolikasta, siis sitä 16-bittistä kokonaisuutta, joka koostuu sanan kahdesta ensimmäisestä tavusta. Tällainen puolisana (engl. half-word) voidaan tulkita etumerkittömäksi kokonaisluvuksi tai etumerkilliseksi 5 Tämä muistin jako segmentteihin on itse asiassa matkittu UNIX-käyttöjärjestelmien käyttämästä kullekin ohjelmalle erikseen annetun muistiavaruuden rakenteesta. Unixissa nollamuistin nimi on bss. 6 Sanan pituus vaihtelee käskykanta-arkkitehtuurista riippuen; yleensä se on kaksi, neljä tai kahdeksan tavua pitkä. Sitä sanotaan myös konesanaksi (engl. machine word).

18 2. KONEKIELINEN OHJELMOINTI kokonaisluvuksi yllä esitettyä vastaavalla tavalla. Tietyissä tilanteissa kaksi peräkkäistä sanaa muodostavat kaksoissanan (engl. doubleword), joka tulkitaan kaksinkertaisen tarkkuuden liukuluvuksi IEEE 754 -standardin mukaisesti. Kaksoissana, sana tai puolisana voidaan lukea vain alkaen neljällä jaollisista osoitteista. Ajatus on, että tekstimuisti sisältää suoritettavan ohjelman konekielisen koodin. Kukin käsky vie 16 tavua; käskyjen koodaus jätetään tarkoituksella määrittelemättä. Datamuisti sisältää kaiken ohjelman alussa varattavan ja alustettavan datan, nollamuisti sisältää kaiken ohjelman alussa varattavan ja nollaksi alustettavan datan. Koneessa pyörivä ohjelma voi lisäksi kasvattaa nollamuistin kokoa ja näin mahdollistaa dynaamisen muistinvarauksen (tosin ohjelma joutuu itse pitämään kirjaa yksittäisten dynaamisten muuttujien varauksista ja vapauttamisista). Pinomuistia ohjelma voi käyttää esimerkiksi aliohjelmien hallintaan. Koneessa on viisi 32-bittistä rekisteriä: SP: (Stack Pointer) Pienin osoite, joka kuuluu pinomuistiin. (Nolla tulkitaan siten, että pino on tyhjä.) FP: (Frame Pointer) On aina suurempi tai yhtäsuuri kuin SP. IP: (Instruction Pointer) Sisältää sen tekstimuistin tavun osoitteen, josta seuraavaksi suoritettava käsky alkaa. ZL: (Zero Limit) Pienin osoite, joka ei kuulu tekstimuistiin, datamuistiin eikä nollamuistiin. AR: (Address Register) Käytetään epäsuorien osoitusten osoitelähteenä. Kun Nelikone käynnistyy, se lataa ajettavan ohjelman jostakin tarkemmin määrittelemättömästä lähteestä. Ohjelma määrää tekstija datamuistin koon sekä nollamuistin lähtökoon sekä sen, mitä tekstimuistiin sisältyy ja mitkä ovat datamuistin tavujen alkuarvot. Ennen suorituksen alkamista kone nollaa kaikki nollamuistin tavut ja pinomuisti on tyhjä (SP on nolla). Nelikoneen käskyt on lueteltu taulukossa 1. Kukin operandi (opr1, opr2 tai opr3) voi olla [nimi], jolloin operandin sisältö luetaan alkaen muistipaikasta, jonka osoite on vakion nimi arvo; [R + n], jolloin operandin sisältö luetaan alkaen muistipaikasta, joka saadaan, kun R-rekisterin (FP, SP tai AR) sisältämään osoitteeseen lisätään vakio n (0 n < 2 32 ); sekä [R n], jolloin operandin sisältö luetaan alkaen muistipaikasta, joka saadaan, kun R-rekisterin (FP, SP tai AR) sisältämästä osoitteesta vähennetään vakio n (0 n < 2 32 ). Operandi voi myös olla jotain seuraavista, jos se ei esiinny nuolen ( ) vasemmalla puolella:

4. NELOSKIELEN IDEA 19 Käsky Kuvaus opr1 opr2 Kopioi opr2:n sisältö opr1:een. opr1 opr2 + opr3 Laita opr2:n ja opr3:n summa opr1:een. opr1 opr2 opr3 Laita opr2:n ja opr3:n erotus opr1:een. opr1 opr2 opr3 Laita opr2:n ja opr3:n tulo opr1:een. opr1 opr2 / opr3 Jaa opr2 opr3:lla ja laita osamäärä opr1:een. opr1 opr2 mod opr3 Jaa opr2 opr3:lla ja laita jakojäännös opr1:een. opr1 opr2 and opr3 Suorita biteittäinen ja-operaatio opr2:n ja opr3:n välillä ja tallenna tulos opr1:een. opr1 opr2 or opr3 Suorita biteittäinen tai-operaatio opr2:n ja opr3:n välillä ja tallenna tulos opr1:een. opr1 opr2 xor opr3 Suorita biteittäinen poissulkeva tai -operaatio opr2:n ja opr3:n välillä ja tallenna tulos opr1:een. opr1 not opr2 Suorita biteittäinen negaatio opr2:lle ja tallenna tulos opr1:een. opr1 reg Kopioi rekisterin reg (AR, SP, FP tai ZL) sisältö opr1:een. reg opr1 Kopioi rekisterin opr1:n sisältö reg:iin (AR, SP, FP tai ZL). reg1 reg2 Kopioi rekisterin reg1:n (AR, SP, FP tai ZL) sisältö reg2:een (AR, SP, FP tai ZL). SP SP + n Lisää SP:hen vakio n. SP SP n Vähennä SP:stä vakio n. if opr1 = opr2 goto opr3 Jos opr1:n ja opr2:n sisällöt ovat samat, laita opr3:n sisältö IP:hen. if opr1 < opr2 goto opr3 Jos opr1:n sisältö on pienempi kuin opr2:n sisältö, laita opr3:n sisältö IP:hen. if opr1 opr2 goto opr3 Jos opr1:n sisältö on pienempi tai yhtäsuuri kuin opr2:n sisältö, laita opr3:n sisältö IP:hen. if opr1 > opr2 goto opr3 Jos opr1:n sisältö on pienempi kuin opr2:n sisältö, laita opr3:n sisältö IP:hen. if opr1 opr2 goto opr3 Jos opr1:n sisältö on suurempi tai yhtäsuuri kuin opr2:n sisältö, laita opr3:n sisältö IP:hen. if opr1 opr2 goto opr3 Jos opr1:n ja opr2:n sisällöt ovat erisuuret, laita opr3:n sisältö IP:hen. goto opr1 Laita opr1:n sisältö IP:hen. syscall n Tee systeemikutsu, jonka tunnus on vakio n (ks. liite A). TAULUKKO 1. Nelikoneen käskykanta n, jolloin operandin sisältö on kyseinen vakio n (0 n < 2 32 ); tai nimi, jolloin operandin sisältö on se luku, jota ko. nimi edustaa. Operandi, joka on muotoa [jotain+0] tai [jotain-0] voidaan lyhentää [jotain].

20 2. KONEKIELINEN OHJELMOINTI Operandien koko ja tulkinta määräytyy käskyn mukaan. Pääsääntöisesti operandien koko on neljä tavua ja ne tulkitaan etumerkittömiksi kokonaisluvuiksi. Tämä voidaan muuttaa kirjoittamalla käskyrivin loppuun jokin seuraavista merkinnöistä: (FS): Operandit ovat nelitavuisia sanoja, jotka tulkitaan yksinkertaisen tarkkuuden liukuluvuiksi. (FD): Kukin operandi koostuu kahdesta perättäisestä nelitavuisesta sanasta, joka tulkitaan kaksinkertaisen tarkkuuden liukuluvuiksi. (S): Operandit ovat nelitavuisia sanoja, jotka tulkitaan etumerkillisiksi kokonaisluvuiksi. (H): Operandit ovat kaksitavuisia puolisanoja, jotka tulkitaan etumerkittömiksi kokonaisluvuiksi. (HS): Operandit ovat kaksitavuisia puolisanoja, jotka tulkitaan etumerkillisiksi kokonaisluvuiksi. (B): Operandit ovat tavuja, jotka tulkitaan etumerkittömiksi kokonaisluvuiksi. (BS): Operandit ovat tavuja, jotka tulkitaan etumerkillisiksi kokonaisluvuiksi. Kertolaskun tulosoperandi ja jakolaskun jaettavan sisältävä operandi on kokonaislukulaskennassa aina kaksi kertaa niin pitkä kuin muut operandit. Vertailukäskyjen kolmas operandi sekä goton ainoa operandi on kuitenkin tällaisista merkinnöistä huolimatta aina nelitavuinen sana, joka tulkitaan etumerkittömäksi kokonaisluvuksi, joka on tekstimuistiin kuuluva osoite. Bitittäisten operaatioiden (and, or, xor, not) kanssa merkinnät (FS) ja (FD) ovat kiellettyjä, sillä liukuluvun bitittäinen käsittely ei ole mielekästä. Esimerkiksi käsky [tmp] [rv] 10 (B) kertoo rv:n edustaman muistipaikan sisällön kymmenellä ja sijoittaa tuloksen puolisanaan, joka alkaa tmp:n sisältämästä osoitteesta. Symbolisessa konekielessä tulee olla myös tapa määritellä symbolisia nimiä osoitteille ja vakioille. Sovitaan niin, että rivin alussa oleva sana, jonka perässä on kaksoispiste, määrittelee kyseisen sanan symboliseksi vakioksi, jonka arvona on seuraavan käskyn osoite. Sovitaan myös, että pseudokäsky data, jonka perään kirjoitetaan yksi luku, lisää datamuistiin uuden nelitavuisen muuttujan, jonka alkuarvo on kyseinen luku. Lisäksi sovitaan, että pseudokäsky zero lisää nollamuistin kokoa neljällä tavulla (varaten näin tilaa yhdelle nolla-alusteiselle muuttujalle). Sovittakoon vielä, että pseudokäsky string, jonka perään kirjoitetaan C-tyylinen merkkijono, lisää datamuistiin tavujonon, jonka sisältö on tuo merkkijono. Pseudokäskyn osoite olkoon sen määrittelemän uuden muuttujan osoite (zeron tapauksessa nollamuistin ylärajan vanha arvo).

str2ulong: SP SP 4 [SP] FP FP SP [s] [FP + 8] [rv] 0 loop: AR [s] [tmp] 0 [tmp] [AR] (B) if [tmp] = 0 goto finish [tmp] [tmp] 48 [rv] [rv] 10 [rv] [rv] + [tmp] [s] [s] + 1 goto loop finish: [FP + 8] [rv] FP [FP] tmp [SP + 4] SP SP + 8 goto [tmp] tmp: zero s: zero rv: zero zero 5. SYSTEEMIKUTSUT 21 KUVA 5. Yksinkertainen aliohjelma Nelikielellä Lisäksi on vielä pseudokäsky noinstr, joka ei tee yhtään mitään (paitsi kertoo, että samalla rivillä oleva symbolinen nimi saa arvonsa juuri tekstimuistista). Kuvassa 5 on esitetty kuvien 1, 2, 3 ja 4 aliohjelma nelikielellä. 5. Systeemikutsut Ollakseen hyödyllinen ohjelman tulee voida vaikuttaa ympäristöönsä sekä ottaa ympäristöstään vaikutteita. Tämä tapahtuu ohjaamalla erilaisia oheislaitteita kuten näyttöä, printteriä ja hiirtä. Yleensä tavallisen ohjelman ei anneta kuitenkaan vapaasti keskustella oheislaitteiden avulla; syyt lienevät kaikille selvät. Sen sijaan käyttöjärjestelmä keskustelee laitteiden kanssa ja välittää ohjelmien pyynnöt eteenpäin huolehtien siitä, että samaan aikaan käynnissä olevat ohjelmat eivät tallo toisiaan varpaille. Käytännössä ohjelman tulee voidakseen keskustella ympäristönsä kanssa siis keskustella käyttöjärjestelmän kanssa. Tavallisesti tämä tapahtuu systeemikutsujen (engl. system calls) avulla: kun ohjelma haluaa käyttää käyttöjärjestelmän palveluita, se käyttää jotain erityistä mekanismia kutsuakseen käyttöjärjestelmää 7. 7 Toinen vaihtoehto olisi antaa käyttöjärjestelmän kutsua ohjelmaa tarvittaessa. Tätä sanottaisiin tapahtumapohjaiseksi (engl. event-based) systeemiksi. Tavallisesti todellisissa järjestelmissä käytetään tarpeen mukaan molempia järjestelyitä.