Artikkelit

Artikkelit olivat Suomipelit.comissa sivuston sulkeuduttua. Näin ollen niihin saattaa sisältyä rikkinäisiä linkkejä tai epämääräisiä viitteitä asioihin, joita ei enää ole olemassa, ja joskus jokin saattaa näkyä väärin.

Palaa artikkelilistaan

Kuinka tietokonepeli toimii - Osa 3: Kohti korkeampaa abstraktiotasoa

Tämä artikkelisarja on tarkoitettu aloittelijoille, eikä sen lukemiseen tarvita mitään ennakkotietoja tietotekniikasta. Artikkelin tarkoitus on antaa perinpohjainen selvitys siitä, kuinka yksi aikamme ihmeistä, tietokonepeli, toimii, ja mikä tärkeintä, auttaa alkuun omien tietokonepelien tekemisessä. Samalla selviää myös, kuinka tietokoneet ylipäätään toimivat ja joitakin mielenkiintoisia asioita niiden historiasta ja takana olevasta teoriasta.

24.12.2006 julkaistun artikkelin on kirjoittanut Markus.

  1. Assembly
  2. Kääntäjät ja tulkit
  3. Tiedon koodaus
  4. Käyttöjärjestelmä
  5. Miksi tietokone ”bootataan”?

Assembly #

Ironista, että niinkin hankalalta kuulostava sana, kuin abstraktio, tarkoittaa helpommin ymmärrettävää. Konekielessä nimittäin on se paha ongelma, että se on ihmisen kannalta erittäin hankala käyttää. Ensinnäkin ihmisen on hankala erottaa toisistaan, saati sitten muistaa, ykkösistä ja nollista koostuvia sarjoja. Toiseksi yhdellä konekielen ohjelmarivillä saadaan aikaan niin valtavan vähän. Pelkkä kahden luvun yhteenlasku vie kolme ohjelmariviä. Tämän takia, niin kauan kuin tietokoneita on ollut olemassa, on etsitty helpompia tapoja tehdä niille ohjelmia, eli saada ohjelmointiin korkeampi abstraktiotaso.

Ensimmäinen askel, joka otettiin jo tietokoneiden alkuaikoina, oli palauttaa teknisistä rajoitteista johtunut asioiden muuttaminen binäärijärjestelmään, takaisin selväkieliseen muotoon. Binääriset käskyt korvattiin oikeilla sanoilla ja binääriluvut kymmenjärjestelmän luvuilla. Syntyi symbolinen konekieli eli Assembly.

Asiaan mitenkään liittymättömänä sivuhuomautuksena mainittakoon, että Assembly-kielen mukaan nimetty Assembly-tietokonefestivaali pidetään joka syksy Hartwall-areenalla.Asiaan mitenkään liittymättömänä sivuhuomautuksena mainittakoon, että Assembly-kielen mukaan nimetty Assembly-tietokonefestivaali pidetään joka syksy Hartwall-areenalla.

Assemblyssä on kuitenkin se ilmeinen vika, että tietokone ei ymmärrä sitä. Tietokone ymmärtää vain ykkösiä ja nollia, eikä mitään muuta voisi sen muistiin tallentaakaan. Mitä itua on kirjoittaa ohjelma Assemblylla, kun se pitää kuitenkin tehdä uudestaan konekielellä, jotta kone osaa suorittaa sen?

Tässä on se itu, että Assemblyllä kirjoitetun ohjelman muuntaminen eli kääntäminen konekielelle ei ole mitenkään henkisesti haastava asia. Se on yksinkertainen ”etsi ja korvaa” -operaatio. Korvataan vain jokainen löytyvä käsky sen binäärisellä vastineella, ja muutetaan jokainen kymmenjärjestelmän numero binäärijärjestelmän numeroksi. Tämä käännös on niin yksinkertainen suorittaa, että se voidaan teettää Kiinassa halpatyövoimalla tai rakentaa kone, joka suorittaa sen automaattisesti. Itse asiassa tietokone on erittäin hyvä kone tämän käännöksen suorittamiseen ja kun olemme juuri sellaisesta puhumassa, niin käytetään kääntämiseen sellaista, eikä lietsota kiinailmiötä.

Assemblyn käyttäminen yksinkertaistaa ohjelmointia huomattavasti, mutta vain sen verran, mitä fyysisen hajasaantikoneen rajoitukset sitä alun perin vaikeuttivat. Tämän takia ei auta pysähtyä tähän vaan jatkaa matkaa.

Ihanteellinen tilanne olisi, jos kirjoittaisimme ohjelman ihan selvällä suomen kielellä. Eli kirjoittaisimme esimerkiksi:

”Ohjelma, joka laskee muistipaikoissa 1-1000 olevien lukujen keskiarvon ja piirtää jakauman histogrammin ruudulle.”

Ihanteellisessa tapauksessa tässä olisi kaikki mitä tarvitaan. Kyseinen teksti vaan käännettäisiin ja tuloksena olisi kuvauksen mukainen ohjelma. Tässä on kuitenkin se paha vika, että käännös ei enää ole yksinkertainen. Ohjelman alun keskiarvo on vielä on aika yksiselitteinen. Kääntäjä vain tuottaisi ”ADD” ja ”JZERO”-käskyjen avulla jonkinlaisen silmukan, joka summaisi muistipaikkojen 1-1000 sisällön akkuun ja perään vielä yhden ”DIV”-käskyn, joka jakaisi tuloksen tuhannella.

Loppu sen sijaan on hankalampi. Histogrammin piirtääkseen kääntäjän pitäisi tuottaa ohjelma, joka muuttaa monitorissa näkyvien muistipaikkojen sisältöä, niin että kuva esittää histogrammia. Mistä tietää kääntäjä tietää, että histogrammi tarkoittaa juuri pylväskaaviota? Jos tietääkin, niin minkähän värinen sen pitäisi olla? Kuinka monta pylvästä? Olisiko tulos sama, jos sanamuotoa vähän muutetaan? Entäpä jos ohjelma olisi tehty savon murteella? Kääntäjän tekijä ei varautua mitenkään kaikkiin mahdollisiin tilanteisiin.

Lisäksi suomen kieli jättää aika paljon tulkinnan varaa. Esim. jos ruudulle pitää piirtää kuusi. Tarkoittaako tämä sitä, että piirretään luku vai puu?

Ongelma on siis se, että mitä helpompi jokin kieli on ihmisen kannalta, sitä hankalempi (ellei mahdoton) se on kääntää konekielelle automaattisesti. Ja mitä helpompi kielen kääntäminen konekielelle on automatisoida, sitä hankalampi kielen käyttö on ihmisen kannalta.

Tämän takia kompromissiksi on kehitetty korkeamman tason ohjelmointikieliä. Nämä kielet ovat sen verran yksinkertaisia, että niillä tehdyt ohjelmat voidaan kääntää automaattisesti konekielelle, mutta niin kehittyneitä, että ihmisen on "helppo" kirjoittaa niillä ohjelmia. Ensimmäinen tällainen kieli oli vuonna 1954 kehitetty Fortran. Sen jälkeen kieliä on kehitetty aikojen saatossa satoja. Kuuluisimpia ovat Pascal, Ada, BASIC, Java, C, C++, C#, Python ja Perl. Näistä C++ on se, jolla lähes poikkeuksetta jokainen peli tänä päivänä tehdään. Muista kielistä mainittakoon, että Javaa käytetään aika paljon matkapuhelinpelien tekemiseen.

Kääntäjät ja tulkit #

Kääntäjä (englanniksi compiler) on ihan tavallinen tietokoneohjelma siinä missä mikä tahansa muukin ohjelma. Sen tarkoitus on muuntaa korkeamman tason kielellä kirjoitettu ohjelma, jota kutsutaan lähdekoodiksi (englanniksi source code) tai lähdeohjelmaksi, konekielelle. Konekielelle käännettyä versiota taas kutsutaan objektikoodiksi (englanniksi object code) tai binäärikoodiksi. Sanan lähdekoodi englanninkielisen vastineen takia lähdekoodia kutsutaan joskus myös lempinimellä ”sorsa”.

Kääntäjä tuottaa jollakin korkeamman tason ohjelmointikielellä kirjoitetusta ohjelmasta konekielisen version.Kääntäjä tuottaa jollakin korkeamman tason ohjelmointikielellä kirjoitetusta ohjelmasta konekielisen version.

Kun ohjelma on käännetty, ei lähdekoodia tarvita enää ohjelman suorittamiseen, vaan tarvitaan pelkkä objektikoodi. Lähdekoodia ei kuitenkaan sovi hävittää, sillä jos ohjelmaan halutaan tehdä muutoksia tehdään ne lähdekoodiin, jonka jälkeen siitä käännetään uusi objektikoodi.

Jos ohjelma koostuu useista eri palasista tarvitaan kääntämisen jälkeen vielä linkitykseksi kutsuttu vaihe, jossa käännetyt palaset kootaan yhteen. Tämän vaiheen hoitaa linkittäjäksi kutsuttu ohjelma.

Ohjelmointikielten kääntäminen konekielelle ei ole mikään yksinkertainen urakka ja tämän takia kääntäjät ovatkin erittäin monimutkaisia ohjelmia, enkä käsittele sellaisen laatimista tässä. Kääntäjää ei kuitenkaan tarvitse lähteä itse tekemään, sillä sellaisia on saatavana valmiina. Ehkä kaikkein kuuluisin ja käytetyin kääntäjä on GCC (GNU Compiler Collection), joka on itse asiassa paketti, joka sisältää kääntäjät mm. C, C++, Java ja Ada kielille. Sen asentamisesta ja käyttämisestä tarkemmin artikkelisarjan viimeisessä osassa.

Toinen mahdollinen menettely on käyttää tulkkia (englanniksi interpreter). Tulkki eroaa kääntäjästä siinä että, se kääntää ohjelmaa lennossa sen suorituksen aikana, sitä mukaa kuin tarvitaan. Haittana tietenkin se, että ohjelman kääntäminen sen suorituksen aikana hidastaa ohjelman suoritusta valtavasti. Tulkkaamisen hitauden vuoksi tulkattavia kieliä ei käytetä juuri pelien tekoon. Yleisimpiä kieliä, joiden kanssa käytetään tulkkia, ovat Python ja Perl.

On myös mahdollista käyttää hybridiratkaisua, jossa yhdistetään kääntäminen ja tulkkaaminen. Näissä lähdekoodi käännetään ensin, mutta ei konekielelle, vaan ”välikielelle” eli kielelle, joka on huomattavasti yksinkertaisempi kuin alkuperäinen kieli. Tämä välikielinen ohjelma sitten suoritetaan tulkin avulla. Etuna on se, että välikieli on yksinkertaisuutensa vuoksi nopeampaa tulkata kuin alkuperäinen kieli. Kuuluisia tällaista hybridiratkaisua käyttävät kielet ovat Java ja C#.

Korkeamman tason kielen käyttö tekee ohjelmasta myös riippumattoman käytettävän tietokoneen käskykannasta. Sopivien käskyjen tuottaminen jää kääntäjän vastuulle. Käännetty ohjelma tietenkin toimii vain niissä tietokoneissa, joissa ohjelman käyttämät käskyt on käytettävissä. Jos tietokoneen käskykanta vaihtuu, pitää ohjelma vain yksinkertaisesti kääntää uudestaan. Tulkilla on tässä se etu, että ohjelma ei tarvitse kääntää uudestaan, vaan se toimii millä tahansa laitteistolla, jolle tulkkikin on olemassa.

Ohjelmointiparadigmat

Vaikka ohjelmointikieliä on satoja, voidaan ne jakaa muutamaan eri luokkaan eli paradigmaan. Näistä yleisimmät ovat:

  • Proseduraaliset kielet
  • Oliokielet
  • Funktionaaliset kielet
  • Logiikkakielet

Useimmat ohjelmointikielet eivät kuitenkaan ole puhdasoppisia, vaan ne yhdistelevät piirteitä eri paradigmoista.

Proseduraaliset kielet

Proseduraaliset kielet olivat ensimmäisiä kehitettyjä ohjelmointikieliä. Tällaisia kieliä ovat mm. Fortran, Pascal ja C. Näissä kielissä ohjelma koostetaan pienistä määrätyn tehtävän suorittavista palasista eli proseduureista. Tunnistamisen takia proseduurit on mahdollista nimetä mielivaltaisesti.

Ohjelman jakaminen pieniin käyttötarkoituskeltaan rajattuihin palasiin helpottaa sen laatimista. Jos ohjelmaa on tekemässä useampi henkilö voidaan tehtävät jakaa niin, että jokainen kirjoittaa, hänelle määrätyt proseduurit.

Jokainen proseduuri koostuu lauseista, jotka suoritetaan järjestyksessä yksi kerrallaan. Lause on jokin matemaattinen lauseke, joka suorittaa laskutoimituksia muistipaikkojen sisällöillä. Muistipaikkoihin ei tarvitse kuitenkaan viitata numeroilla vaan ne voidaan nimetä. Tällaista nimettyä muistipaikkaa kutsutaan muuttujaksi. Ohjelmoija ei edes valitse mitä muistipaikkaa hän haluaa käyttää. Ohjelmoija yksinkertaisesti kertoo, kuinka monta muistipaikkaa hän haluaa käyttää ja antaa näille nimet. Kääntäjän tehtäväksi jää valita sopivat muistipaikat.

Lauseiden lisäksi jokainen proseduuri voi suorittaa haluamassaan välissä minkä tahansa toisen proseduurin. Tästä proseduurin suorittamisesta käytetään nimitystä proseduurin kutsuminen. Kääntäjän näkökulmasta tämä toteutetaan hyppykäskyllä ”JUMP” proseduurin toteuttavan ohjelmakoodin ensimmäiselle riville. Lisäksi proseduurin viimeiselle riville kääntäjän täytyy tuottaa toinen hyppykäsky, joka palauttaa suorituksen takaisin siihen kohtaan, josta proseduuria kutsuttiin.

Proseduuri voi myös ottaa parametreja, joiden arvot vaikuttavat sen suoritukseen. Lisäksi proseduuri voi palauttaa jonkin arvon kutsuvaan proseduuriin. Jos näin on, käytetään proseduurista nimitystä funktio, koska se toimii hieman matemaattisen funktion tavoin.

Oliokielet

Ohjelmien koon kasvaessa proseduraalisilla kielillä tehtyjen ohjelmien laatiminen käy yhä hankalammaksi ja hankalammaksi. Ihmelääkkeeksi tähän vaivaan kehitettiin seuraavaksi oliokielet. Ne ovat ohjelmointikielien (lähes) uusin suuntaus. Tällaisia kieliä ovat mm. C++ ja Java.

Olio-ohjelmoinnissa ohjelma koostetaan olioiksi kutsutuissa komponenteista. Olio voi sisältää kahdenlaisia asioita: tietoja eli attribuutteja ja toimintoja eli metodeja. Esim. koira-oliolla voisi olla attribuutit ”nimi” ja ”rotu”, sekä toiminnot ”istu” ja ”hauku”.

Attribuutit vastaavat proseduraalisten kielien muuttujia ja kääntyvät muistipaikoiksi. Metodit vastaavat proseduureja. Ne koostuvat lauseista, jotka suorittavat laskentaa olion attribuuteilla ja voivat suorittaa toisia metodeja kutsumalla niitä. Oliokielillä tehdyt ohjelmat ovat helpommin hallittavissa, koska jokainen olio on selvemmin rajattu kokonaisuus. Jokainen olion metodi voi käsitellä vain kyseisen olion attribuutteja ja kutsua kyseisen olion metodeja.

Funktionaaliset ja logiikkakielet

Funktionaaliset ja logiikkakielet ovat ohjelmointikielten outolintuja. Ne eivät sovi pelien tekoon vaan ovat lähinnä tarkoitettu tiettyihin erityistarkoituksiin. Kyseiset kielet ovat lisäksi usein miten tulkattavia kieliä, eivätkä tämänkään takia sovellu hyvin peleihin.
Funktionaaliset kielet perustuvat Alonzo Churchin (kyllä, sama heppu, joka oli mukana kehittämässä Chuch Turing-teesiä) 30-luvulla kehittämään λ-kalkyyliin. Se muistuttaa hyvin paljon periaatetta, jolla kirjoitamme funktioita matematiikassa. Kuuluisimpia funktionaalisia kieliä ovat Lisp ja Haskell.

Logiikkakielet perustuvat matemaattiseksi logiikaksi kutsuttuun matematiikan osa-alueeseen. Ensimmäisenä logiikan käyttöä ohjelmointikielenä ehdotti John McCarthy vuonna 1958. Kuuluisin logiikkakieli on Prolog.

Stantardikirjastot

Tietokoneohjelman kirjoittamisessa tulee usein esiin joitakin asioita, jotka lähes jokainen ohjelma tarvitsee. Tällaisia ovat mm. tekstin tulostus näytölle (eli monitorin esittämien muistipaikkojen muuttaminen niin, että kuva esittää jotakin tekstiä). Jotta tällaisia rutiineita ei tarvitsisi kirjoittaa jokaisen ohjelman yhteydessä uudestaan, tarjoavat käytännössä kaikki ohjelmointikielet kokoelman valmiiksi tehtyjä rutiineja ohjelmoijan käytettäväksi. Tätä kokoelmaa kutsutaan kielen standardikirjastoksi.

Riippuu hieman kielestä mitä rutiineja tarjotaan ja kuinka kirjasto on toteutettu. Esim. proseduraalisten kielten yhteydessä kirjasto sisältää proseduureja, joita ohjelmoija voi kutsua ohjelmastaan. Oliokielessä taas tarjotaan valmiita olioita.

Tiedon koodaus #

Tietokoneen teknisistä rajoitteista johtuen koneen muistiin ei voinut tallentaa kuin ykkösiä ja nollia. Tästä seuraa ongelma: Kuinka muuta tietoa sitten tallennetaan sen muistiin? Ratkaisu on koodaus. Koodaus on sitä, että otetaan jokin mielivaltainen tieto ja sovitaan jokin sarja ykkösiä ja nollia, joka tarkoittaa tätä tietoa. Sovitaan esimerkiksi, että ”110111100010101” tarkoittaa lehmää. Koska mikä tahansa jono bittejä voidaan binäärijärjestelmän avulla tulkita numeroksi riittää, että sovitaan jokin numero esittämään haluttua tietoa. (Sanalla koodaus tarkoitetaan myös tietokoneohjelman kirjoitusprosessia. Tätä merkitystä ei pidä sotke sen tiedon koodauksen kanssa, josta nyt puhumme.)

Mutta miten valita binääriset vastineet eri tiedoille? Ohjelman laatija voisi tietenkin keksiä ne ohjelmaa tehdessään, mutta jos muidenkin ohjelmien olisi tarkoitus käyttää samaa tietoa, olisi hyvä, jos niiden laatijatkin tietäisivät, mitä mikäkin bittijono tarkoittaa. Tarvitaan standardeja eli maailman laajuisia sopimuksia siitä, miten mikäkin tieto koodataan. Tunnetuimpia järjestöjä, jotka kehittävät tällaisia standardeja ovat ANSI ja ISO.

Teksti

Yksi yleisimpiä ei numeerisia tietoja, jotka tarvitsee tallentaa tietokoneen muistiin on teksti. Jotta ohjelma voitaisiin kääntää kääntäjällä pitäähän lähdekoodikin voida sijoittaa tietokoneen muistiin tekstinä.

Tekstin koodaamiseen yleisin standardi on ASCII-standardi. Siinä jokaiselle kirjaimelle on sovittu jokin 8-bittiä (eli yhden tavun) pitkä vastine. Yksi tavu siksi, että näin jokainen merkki vie tasan yhden muistipaikan. Seuraava taulukko näyttää lähes koko ASCII-standardin. Sarake ”Nro” kertoo mitä bittijono olisi tulkittuna kymmenjärjestelmän numeroksi. Huomaa, kuinka kirjaimet a-z ja A-Z ovat numerojärjestyksessä.


ASCII oli pitkään se tapa jolla tekstiä tallennettiin. Sen rajoitukset ovat kuitenkin tulleet vastaan. 8 bittiä voidaan järjestää vain 256 eri tavalla eli ASCII-koodauksella on mahdollista esittää vain 256 eri merkkiä. Tämä on tarpeeksi englanninkieliselle aakkostolle, mutta jos mukaan halutaan esim. Suomen å, ä ja ö puhumattakaan erikoismerkeistä kuten € eivät käytössä olevat 256 eri yhdistelmää enää riitä. Tarvitaan pidempiä sarjoja. Tätä varten kehitettiin uusi standardi nimeltä UNICODE. Siinä pituus ei enää ole rajoitettu. Unicode sisältääkin noin 200000 eri merkkiä.

UNICODE:ssa on sopimukset lähes kaikille kuviteltavissa oleville merkeille, kuten kiinan ja arabian kielen merkeille. Jopa Klingonin kielen merkkien ja Haltija kielen merkkien lisäämistä UNICODE:en on ehdotettu. Yhteensopivuuden takaamiseksi ensimmäiset 128 UNICODE merkkiä vastaavat samoja ASCII merkkejä.

Värit

Itse asiassa artikkelisarjan ensimmäisessä osassa olemme jo käyttäneet yhtä värien koodaustapaa. Käytimme numeroa 0 tarkoittamaan valkeaa ja mitä tahansa muuta lukua tarkoittamaan mustaa. Tuloksena oli mustavalkeita kuvia.

Harmaasävy voidaan tallentaa yksinkertaisesti numerona, joka kertoo sävyn kirkkauden. Numero 0 vastaa mustaa ja sitä suuremmat numerot vaaleampia sävyjä. Mitä suurempi numero, sitä kirkkaampi sävy. Valkea saadaan kun käytetään suurinta mahdollista numeroa. Jos väri vie yhden tavun on tämä numero 255.

Yleisin varsinaisten värien koodaamiseen käytetty tapa on RGB-standardi. Se perustuu havaintoon, jonka mukaan mikä tahansa väri voidaan tuottaa sekoittamalla kolmea eri väriä: punainen (R), vihreä (G) ja sininen (B). Värin tallentamiseen tarvitaan täten kolme eri numeroa, joista ensimmäinen ilmaisee punaisen määrän, toinen vihreän määrän ja kolmas sinisen määrän. Kun kaikki arvot ovat yhtä suuret saadaan harmaasävy. Jokainen värikomponentti vie tavallisesti yhden tavun eli yksi väri vie kolme tavua eli 24 bittiä. RGB:stä on tietenkin eri variaatioita esim. BGR, jossa komponentit vain ovat eri järjestyksessä.

Kuvat

Kuva koostetaan pikseleistä eli kuvapisteistä ja jokaisen pikselin väri tallennetaan. Värien lisäksi pitää kuitenkin vielä tallentaa kuvan leveys ja korkeus, sillä pelkästä pikselien määrästä tätä ei voi päätellä. Esim. jos kuvassa on 5000 pikseliä sen koko voi olla 100x50, 50x100, 10x500 jne...

Kuvat vievät huomattavan paljon muistia. Jos yhden pikselin värin tallentaminen vie kolme tavua vie 1024x768 kokoinen kuva yli kaksi megatavua muistia. Onkin kehitetty lukemattomia muita koodaustapoja, joilla kuva saadaan pienempään tilaan. Tällaisia ovat mm. jpg, png ja gif.

Videokuva koostuu useista peräkkäisistä kuvista. Niinpä videokuvaa voidaan tallentaa tallentamalla se kuvina. Videossa näytetään yleensä noin 30 kuvaa sekunnissa. Täten yksi minuutti 480x480 resoluutioista videokuvaa vie yli 400 megatavua! Myös videokuvaa varten on kehitetty vähemmän tilaa vieviä koodaustapoja, joista tunnetuin lienee mpg.

Äänet

Ääni on itseasiassa aaltoliikettä. Se tallennetaan mittaamalla aallon korkeus tietyin väliajoin ja tallentamalla tämä korkeus lukuna.
Puolen millisekunnin mittainen ääniaalto (ylhäällä) ja siitä tasaisin välimatkoin otetut näytteet (alhaalla).Puolen millisekunnin mittainen ääniaalto (ylhäällä) ja siitä tasaisin välimatkoin otetut näytteet (alhaalla).

Näytteiden määrää mitataan hertseinä. Yksi hertsi vastaa yhtä näytettä sekunnissa. Esim. CD-levyjen näytetaajuus on 44100 hertsiä eli äänen jokaisesta sekunnista otetaan 44100 näytettä. Yksi tavu ei riitä yhden näytteen aallonkorkeuden tallentamiseen vaan tarvitaan ainakin kaksi tavua. Täten ääniaallon korkeus tallennetaan lukuna välillä 0-65536. Yksi minuutti ääntä vie yli viisi megatavua. Kuten kuvien tapauksessakin, myös äänen saa paremmin valitulla koodaustavalla pienenpään tilaan. Tunnetuin tällainen koodaustapa on mp3, jolla yksi minuutti ääntä vie tilaa noin yhden megatavun.

Tiedostojärjestelmät

Myös massamuistit voivat tallentaa vain ykkösiä ja nollia. Massamuisteissa säilytettävä tieto onkin koodattava samoin, kuin varsinaisessa muistissakin oleva. Massamuistit on kuitenkin tarkoitettu hyvin suurien tietomäärien pitkäaikaiseen arkistointiin. Tämän varaston saattaa jakaa keskenään suuri joukko eri tietokoneita ja käyttäjiä.

Tarvitaan järjestelmä tiedon löytämiseen ja hallintaan. Tätä varten on kehitetty tiedostojärjestelmiä. Niissä tiedot jaetaan tiedostoiksi kutsuttuihin paloihin. Jokaisen tiedoston voi nimetä mielivaltaisesti niiden tunnistamisen helpottamiseksi. Tiedoston luova ohjelma tietenkin päättää mitä se tiedoston tallentaa ja kuinka se sen sisällön koodaa.

Tiedostojen nimeämisessä tavataan käyttää kirjoittamatonta sääntöä, jonka mukaan tiedoston nimen loppuun kirjoitetaan niin sanottu tiedostopääte. Tämä pääte koostuu pisteestä ”.” ja jostakin, yleensä kolmikirjaimisesta, kirjainyhdistelmästä. Päätteen tarkoituksena on tehdä tiedoston sisällön (ja käytetyn koodaustavan) tunnistaminen helpommaksi käyttämällä samantyyppisten tiedostojen nimessä samaa päätettä. Päätteen käyttö ei siis ole pakollinen ja pääte ei pakota tiedoston sisältöä tietyn laiseksi. Seuraavassa joitakin tavallisesti käytettyjä tiedostopäätteitä.
[DEFTABLE]
[D]Pääte[/D][E]Merkitys[/E]
[D].txt[/D][E]tiedosto sisältää tekstiä ASCII-koodattuna.[/E]
[D].html[/D][E]Tiedosto on nettisivu.[/E]
[D].bmp[/D][E]Tiedosto on kuva BGR -koodattuna.[/E]
[D].jpg[/D][E]Tiedosto on kuva jpg -koodattuna.[/E]
[D].wav[/D][E]Tiedosto sisältää ääntä.[/E]
[D].mp3[/D][E]Tiedosto sisältää ääntä mp3 -koodattuna.[/E]
[D].mpg[/D][E]Tiedosto on video mpg -koodattuna.[/E]
[/DEFTABLE]

Käyttöjärjestelmä #

Tähän mennessä olemme tarkastelleet tilannetta, jossa tietokoneessa on ollut käynnissä yksi ohjelma kerrallaan. Se käynnistyy kun tietokonekin käynnistyy, tekee tehtävänsä ja kun ohjelman suoritus päättyy myös tietokone sammuu.

Näin ensimmäiset tietokoneet toimivatkin. Tämä on kuitenkin hyvin epäkäytännöllistä. Jos ajettavana on monta ohjelmaa, pitää niitä vaihdella tietokoneeseen vähän niin kuin CD-levyjä CD-soittimeen. Olisi parempi, jos tietokone suorittaisi yhden ohjelman ja sen päätyttyä suorittaisi automaattisesti seuraavan jne.

Joskus ohjelma saattaa jäädä odottamaan jotakin tapahtumaa esim. syöttölaitteesta tulevaa syötettä. Tällöin tietokoneella on luppoaikaa, jota ei varsinaisesti käytetä mihinkään. Olisikin hyvä jos tietokone osaisi katkaista tällaisen ohjelman suorituksen odotuksen ajaksi ja käyttää luppoajan jonkin toisen ohjelman suoritukseen. Muunkinlainen kontrolli olisi tervetullutta esim. tarpeeton ohjelma pitäisi voida sammuttaa käskes suorituksen.

Tällaisen kontrollin tarjoavia kontrolliohjelmia alkoikin ilmestyä hyvin pian ensimmäisten tietokoneiden jälkeen. Niitä kutsuttiin ajanhallintajärjestelmiksi, koska niillä kontrolloitiin kuinka tietokoneen suoritusaika jaettiin eri ohjelmien kesken. Myöhemmin nämä järjestelmät monipuolistuivat entisestään ja niitä alettiin kutsua käyttöjärjestelmiksi. Nykyään käyttöjärjestelmät tarjoavat hyvin monipuolisia ja hienostuneita palveluja, mukaan lukien graafisen käyttöliittymän, jonka kautta käyttäjä voi käynnistellä ohjelmia.

Kolmen tavallisimman käyttöjärjestelmän käyttöliittymät. Vasemmalta oikealle Ubuntu Linux, MacOS X ja Windows XP.Kolmen tavallisimman käyttöjärjestelmän käyttöliittymät. Vasemmalta oikealle Ubuntu Linux, MacOS X ja Windows XP.

On sanomattakin selvää, että käyttöjärjestelmä on kääntäjän tapaan suunnattoman monimutkainen ohjelma. Jotkut lähteet vertaavat käyttöjärjestelmän monimutkaisuutta avaruussukkulaan. Käyttöjärjestelmääkään ei kuitenkaan tarvitse lähteä tekemään itse, vaan sellaisiakin on saatavana valmiina. Lähes kaikkien tietokoneiden kanssa tulee kytkykauppana myös käyttöjärjestelmä halusi sitten ostaja sitä tai ei.

Prosessien hallinta

Käyttöjärjestelmien yhteydessä sen suorittamia ohjelmia kutsutaan prosesseiksi ja näiden prosessien kontrollointia prosessien hallinnaksi. Ohjelma, joka kontrolloi muita ohjelmia, saattaa kuulostaa mahdottomalta, mutta sen perusperiaate on yksinkertainen.

Prosessit (ohjelmat), joita käyttöjärjestelmä hallinnoi, sijaitsevat yleensä jollakin massamuistilla, kuten kiintolevyllä. Jotta prosessi voidaan käynnistää, pitää se ensin kopioida massamuistista varsinaiseen muistiin. Kun prosessi on muistissa, käynnistetään se yksinkertaisesti kirjoittamalla sen muistipaikan numero, johon ohjelman ensimmäinen ohjelmarivi kopioitiin, ohjelmalaskuriin. Näin tietokone jatkaa suoritusta kyseisen prosessin ensimmäiseltä riviltä. Jotta käyttöjärjestelmä jatkaisi suoritusta prosessin sammumisen jälkeen, pitää prosessia muistiin kopioidessa tehdä yksi muutos tai pidemminkin lisäys. Prosessin viimeiseksi riviksi pitää lisätä yksi ohjelmalaskuria muuttava käsky, joka siirtää suorituksen jatkumaan taas käyttöjärjestelmän seuraavalta riviltä.

Moniajo

Vielä askel pidemmälle mentäessä päästään tilanteeseen, jossa useampi prosessi on suorituksessa yhtä aikaa. Kaksi prosessi ei tietenkään voi olla oikeasti suorituksessa yhtä aikaa, vaan niiden on vuoroteltava niin, että kummastakin suoritetaan vuoron perään pieni pala kerrallaan. Näin tietokoneen käyttäjälle syntyy illuusio siitä, että prosesseja suoritetaan yhtä aikaa.

Voidakseen vuorotella ohjelmia käyttöjärjestelmä ei voi enää yksinkertaisesti siirtää suoritusta prosessista toiseen, vaan tarvitaan monimutkaisempia toimenpiteitä, joita en käsittele tässä.

Useamman prosessin yhtäaikainen suoritus saa kuitenkin aikaan pahoja ongelmia. Mitä esimerkiksi tapahtuu, jos kaksi prosessia haluaa käyttää samaa muistipaikkaa? Erityisen paha konflikti syntyy kun kaksi ohjelmaa haluaa piirtää yhtä aikaa monitoriin kuvan. Eli muuttaa monitorin esittämien muistipaikkojen sisältöä, niin että tulos esittää jotakin kuvaa. Sama ongelma koskee mitä tahansa muuta syöttö-/tulostuslaitetta
Käyttöjärjestelmän onkin suojeltava prosesseja toisiltaan.

Käyttöjärjestelmän täytyy estää prosesseja käyttämästä mitään resursseja suoraan. Tätä varten käyttöjärjestelmä tarjoaa oman standardikirjaston prosessien käyttöön. Kirjasto sisältää rutiineja, joiden avuilla prosessit voivat käyttää IO-laitteita ja varata käyttöönsä muistipaikkoja. Jos jokin prosessi yrittää käyttää mitä tahansa muistipaikkaa, jota sillä ei ole lupa käyttää, sulkee käyttöjärjestelmä prosessin ja antaa tietokoneen käyttäjälle virheilmoituksen.
Kun ohjelma yrittää käyttää muistipaikkaa, jota se ei ole varannut käyttöönsä, antaa Windows XP -käyttöjärjestelmä näyttää seuraavan virheilmoituksen.Kun ohjelma yrittää käyttää muistipaikkaa, jota se ei ole varannut käyttöönsä, antaa Windows XP -käyttöjärjestelmä näyttää seuraavan virheilmoituksen.

IO-laitteiden käyttäminen käyttöjärjestelmän kautta tuo vielä yhden suuren hyödyn: se peittää eri OI-laitteiden eroavaisuudet. Erilaisia IO-laitteita on nimittäin olemassa lukemattomia. Ohjelman laatija ei voi mitenkään varautua käyttämään niitä kaikkia. IO-laitteen käyttö jää nyt käyttöjärjestelmän vastuulle. Itse ohjelma yksinkertaisesti käyttää käyttöjärjestelmän tarjoama rutiinia, joka pysyy samana riippumatta siitä millainen itse IO-laite on.

Käyttöjärjestelmä myös jakaa yhden IO-laiteen usean sitä käytävän ohjelman kesken. Esim. monitorin tapauksessa kuva jaetaan ikkunoiksi kutsuttuihin palasiin, niin että jokainen ohjelma piirtää omaan ikkunaansa. Näin ei synny konfliktia.

Moniprosessori- / moniydinjärjestelmät

On kuitenkin mahdollista rakentaa tietokone, joka voi oikeasti suorittaa montaa prosessia yhtä aikaa. Itse asiassa on myös mahdollista rakentaa kone, joka suorittaa useampaa saman ohjelman ohjelmariviä yhtä aikaa. Tämä saavutetaan yksinkertaisesti lisäämällä hajasaantikoneeseen useampi logiikkayksikkö ja vastaavasti oikeaan tietokoneeseen useampi prosessori tai ydin yhteen prosessoriin.

Tarkastellaan kuinka paljon hyötyä tällaisella kokoonpanolla saadaan. Otetaan seuraavat neljä laskutoimitusta ja oletetaan, että yhdeltä prosessorilta menee yhden laskutoimituksen laskemiseen yksi sekunti.

A = 12 + 46
B = 66 + 85
C = 97 + 68
D = 23 + 42

Jos koneessa on yksi prosessori, suorittaa se yhden laskutoimituksen kerrallaan ja näin kokonaisaika on neljä sekuntia. Jos koneessa kaksi prosessoria suorittaa ensimmäisen sekunnin aikana toinen laskun A ja toinen laskun B. Seuraavan sekunnin aikana toinen suorittaa laskun C ja toinen laskun D. Aikaa menee vain kaksi sekuntia.

Lisätään kolmas prosessori. Tällöin ensimmäisen sekunnin aikana ensimmäinen suorittaa laskun A, toinen laskun B ja kolmas laskun C. Toisen sekunnin aikana ensimmäinen prosessori suorittaa laskun A. Ja kaksi muuta ovat tyhjän panttina, koska niille ei riitä enää laskettavaa. Kokonaisaika on taas kaksi sekuntia. Ei yhtään parannusta kahden prosessorin tapaukseen.

Otetaan vielä neljäs prosessori. Nyt jokiselle laskulle on oma prosessori ja kokonaisaika on enää yksi sekunti. Jos prosessoreita lisätään vielä enemmän, ei aika enää parane, sillä muut prosessorit olisivat vain tyhjän panttina.

Otetaan seuraavaksi seuraavat neljä laskua ja oletetaan taas, että yhteen laskuun menee yksi sekunti.

A = 12 + 46
B = A + 85
C = B + 68
D = C + 42

Yhdeltä prosessorilta menee taas neljä sekuntia, jossa ei ole muutosta ensimmäiseen esimerkkiin. Sen sijaan, jos laskut jaetaan kahden prosessorin kesken kohdataan ongelma. Annetaan lasku A ensimmäiselle prosessorille ja lasku B toiselle prosessorille. Toinen prosessori ei voi aloittaa laskemista yhtä aikaa ensimmäisen kanssa, koska sen tarvitsee tietää laskutoimituksen A tulos. Sen täytyy odottaa sekunti. Sama tapahtuu taas laskujen C ja D kohdalla ja kokonaisaika on taas neljä sekuntia.

Tilanne ei parane yhtään vaikka prosessoreita olisi kuinka monta. Yksikään prosessori ei voi aloittaa omaa laskuaan ennen, kuin se tietää edellisen laskun tuloksen. Näin ollen yhteisaika on aina neljä sekuntia.

Huomataan, että monen prosessorin tuoma hyöty vaihtelee sen mukaan, millainen tehtävä niiden täytyy suorittaa. Sitä kuinka hyvin tehtävä jakautuu useamman prosessorin kesken kutsutaan tehtävän rinnakkaistuvuudeksi. Valitettavasti suurin osa oikeista tehtävistä ei ole kovin täydellisen rinnakkaistuvia eli ne eivät hyödy juurikaan monen prosessorin käytöstä.

Supertietokoneiden isänä pidetty Seymour Cray kiteytti ehkä parhaiten monien prosessoreiden/ydinten tuoman ongelman seuraavaan lauseeseen: ”Jos sinun pitäisi aurata pelto, niin kumman valitsisit vetämään auraasi: yhden härän vai tuhat kanaa?”.

On kuitenkin yksi peleihin läheisesti liittyvä osa-alue, joka on ideaalinen rinnakkaislaskentaan: grafiikan piirto. Tähän asiaan palaamme lähemmin artikkelisarjan viimeisessä osassa.

Miksi tietokone ”bootataan”? #

Tietokoneen käynnistämisestä käytetään termiä ”boottaus”. Oletko koskaan miettinyt miksi? Asia liittyy tietokoneen muistin pysyvyyteen tai pidemminkin sen pysymättömyyteen. Muistin sisältö nimittäin katoaa, kun tietokoneesta katkeaa virta. Koska tietokone kohtelee kaikkia ohjelmia, mukaan lukien käyttöjärjestelmää muistissa olevana datana, poistuvat ohjelmat muistista virran katkettua.

Ainoan poikkeuksen tekevät massamuistit. Niissä oleva sata säilyy sähkökatkosten yli. Niinpä kaikkia ohjelmia on säilytettävä massamuistissa esim. kiintolevyllä. Täten aina, kun tietokone käynnistetään, pitää käyttöjärjestelmä ensin siirtää massamuistista, yleensä kiintolevyltä, muistiin. Tämä varten tarvitaan ohjelma, joka käyttää massamuistilaitetta ja kopioi siinä olevan käyttöjärjestelmän muistiin. Jotta tämä käyttöjärjestelmän muistiin lataava ohjelma saataisiin muistiin pitää sekin kopioida sinne massamuistista jonkin toisen ohjelman toimesta jne. Syntyy paradoksi. Jotta ohjelma saadaan suoritusta varten tietokoneen muistiin, pitää olla jokin toinen ohjelma, joka sen sinne siirtää. Mistä se ensimmäinen ohjelma sinne sitten tuli?

Saksalaisissa kansansaduissa esiintyy hahmo nimeltä Baron von Münchhausen, jolla oli samantapainen ongelma. Hän oli hukkua mereen, mutta kuitenkin pelastautui nostamalla itse itsensä ylös merestä omista saappaan rakseistaan (englanniksi bootstraps). Tästä syntyi termi boottaus. Tietokoneen käynnistäminen on siis yhtä hankalaa kuin kannatella itseään ilmassa omista saappaan rakseistaan, sillä ensimmäisen ohjelman muistiin saamiseksi on sen siirrettävä sinne itse itsensä.

Disneyn versiossa  Baron von Münchhausen sadusta Aku Ankka pelastautuu putoamiselta kannattelemalla itseään omasta kauluksestaan. (Kuva: Aku Ankan taskukirja nro. 63)Disneyn versiossa Baron von Münchhausen sadusta Aku Ankka pelastautuu putoamiselta kannattelemalla itseään omasta kauluksestaan. (Kuva: Aku Ankan taskukirja nro. 63)

Paradoksi on ratkaistu sillä, että osa tietokoneen ohjelmistosta on muistissa, joka ei tyhjene virran katketessa. Tätä osaa ohjelmistosta kutsutaan nimellä BIOS eli Basic Input Output System. Kun tietokone käynnistyy, aloittaa se suorituksen BIOS-ohjelmasta. BIOS alkaa käynnistyttyään käydä läpi tietokoneen massamuistilaitteita ja etsiä niiltä pientä käynnistyslataajaksi (englanniksi bootloader) kutsutta ohjelmaa. Kun se löytää tämän jostakin, siirtää BIOS käynnistyslataajan muistiin ja siirtää suorituksen jatkumaan sen ensimmäiseltä riviltä (eli kirjoittaa ohjelmalaskurin arvoksi sen muistipaikan numeron, johon kopioi käynnistyslataajan ensimmäisen rivin).

Käynnistyslataajan ainut tehtävä on siirtää käyttöjärjestelmä tietokoneen muistiin. Jos tietokoneessa on useita käyttöjärjestelmiä saattaa käynnistyslataaja näyttää pienen valikon, josta käyttäjä voi itse valita minkä käyttöjärjestelmän hän käynnistää. Kun käyttöjärjestelmä on muistissa siirtää käynnistyslataaja suorituksen sille ja tietokone on valmis käytettäväksi.

Samantapainen paradoksi saattaa tulla mieleen kääntäjien yhteydessä. Kääntäjät ovat olemassa, koska monimutkaisten ohjelmien tekeminen konekielellä on (lähes) mahdotonta. Täten ohjelmat tehdään korkeamman tason kielellä ja sitten käännetään konekielelle. Kääntäjä on kuitenkin valtavan monimutkainen ohjelma, joten sekin on täytynyt tehdä jollakin korkeamman tason kielellä ja sitten kääntää konekielelle. Kääntää millä? Tietenkin jollakin toisella kääntäjällä, joka taas on täytynyt kääntää jollakin toisella kääntäjällä jne. Mistä se ensimmäinen kääntäjä sitten tuli?

Tämä paradoksi ratkeaa sillä, että se ensimmäinen kääntäjä todellakin tehtiin konekielellä. Kyseessä oli IBM:n kehittämä, vuonna 1957 valmistunut, Fortran-kielen kääntäjä. Sen tekemiseen kului kolme vuotta ja valtava summa rahaa. Joskin on huomattava, että se Fortran kielen ensimmäinen versio, jolle tuo kääntäjä tehtiin oli nykyisin käytössä olevia kieliä yksinkertaisempi.

Artikkelisarjan seuraavassa ja viimeisessä osassa siirrymme teoriasta käytäntöön ja teemme ihan oikean pelin käyttäen oikeita menetelmiä. Sitä odotellessa lähetäthän kaikki tästä artikkelista löytämäsi virheet osoitteeseen markus.ilmola@pp.inet.fi, niin korjaan ne mahdollisimman pian. Kaikki muukin palaute ja parannusehdotukset ovat tervetulleita.