Virtuaaliosoite
Virtuaaliosoite tai looginen osoite on suorittimen käyttämä osoite, jonka muistinhallintamekanismi muuttaa fyysiseksi osoitteeksi, muistiosoitteeksi. Vaikka virtuaalimuisti vaatii toimiakseen virtuaaliosoitteet, näitä käsitteitä ei pidä sotkea keskenään. Myös sellaisissa järjestelmissä, joissa ei ole virtuaalimuistia, voi olla virtuaaliosoitteita. Suorittimen kyky viitata muistiin muodostaa osoiteavaruuden, fyysinen muisti puolestaan muistiavaruuden. Nykykoneissa fyysinen muisti on käytännössä aina pienempi kuin osoiteavaruus, mutta vanhimmissa koneissa tilanne saattoi olla päinvastoin.
Virtuaaliosoitteiden tärkein merkitys on siinä, että ne antavat jokaiselle prosessille samanlaisen osoiteavaruuden. Näin jokainen ohjelma (tai sen näkemä osoiteavaruus) alkaa osoitteesta nolla. Tämä mahdollistaa sen, että sama ohjelma voidaan sijoittaa eri kohtaan fyysistä muistia muuttamatta sen sisäisiä muistiviittauksia. Ennen virtuaaliosoitejärjestelmiä piti ohjelma instrumentoida käynnistyksen yhteydessä siten, että sen muistiosoitteet toimivat kulloisessakin suorituspaikassa oikein.
Kun virtuaaliosoitteisiin lisätään oikeustarkistuksia, voidaan huolehtia siitä, että ohjelmat eivät pääse koskemaan toistensa muistipaikkoja.
Yksinkertaisimmat mekanismit virtuaaliosoitteiden muuttamiseksi muistiosoitteiksi olivat segmenttirekisterit ja sivurekisterit. Nykyjärjestelmät käyttävät yleensä sivuttavaa muistinhallintaa, jota toteuttamaan tarvitaan muistinhallintayksikkö (, MMU). Tämä yksikkö huolehtii sekä virtuaaliosoitteiden muuttamisesta fyysisiksi osoitteiksi että mahdollisista oikeustarkistuksista.
Segmentoivissa järjestelmissä virtuaaliosoite yhdistetään segmenttirekisterin kanssa, jolloin saadaan varsinainen muistiosoite.
Yhdistäminen voi tarkoittaa yhteenlaskua, pitkän osoitteen muodostamista laittamalla segmenttirekisterin arvo ja virtuaaliosoite yhdeksi pitkäksi luvuksi, tai näiden kahden tavan sekamuotoa, jossa rekisterin arvo ja virtuaaliosoite ovat osittain päällekkäin yhteenlaskua tehtäessä.
Varsinaisen segmenttirekisterin lisäksi järjestelmissä on usein myös segmentin pituuden määrittelevä rekisteri, jolla voidaan tarkistaa, että ohjelma ei viittaa sille varatun muistin ulkopuolelle.
Segmenttirekistereillä voidaan helposti tuottaa tarvittava illuusio siitä, että kaikkien prosessien osoiteavaruudet alkavat nollasta. Erottamalla koodisegmentti datasegmentistä saavutetaan kaksi asiaa: koodisegmenttiin ei voi kirjoittaa, ja sama koodisegmentti voidaan jakaa useamman prosessin kesken, mikä säästää keskusmuistin tarvetta. Segmenttirekisterillä on myös luonteva säätää oikeuksia segmenttikohtaisesti, mutta virtuaalimuistin toteutukseen segmentit ovat kömpelöitä.
Segmenttirekistereitä ei yleensä ole monta. Intel käyttää arkkitehtuurissaan neljää segmenttirekisteriä (käsky, pino, data ja ekstra). Käytettävä segmenttirekisteri riippuu siitä, noudetaanko viittauksella käsky, siirretäänkö tietoa pinoon tai pinosta vai data-alueelta. Mahdollista ekstrasegmenttiä varten on omat viittausmuotonsa.
Sivurekisterit muistuttavat hieman segmenttirekistereitä, mutta niitä on useampia, ja käytettävä sivurekisteri ei määräydy siitä, missä tarkoituksessa muistiin viitataan, vaan virtuaaliosoitteen ylimpien bittien avulla.
Jos oletamme, että virtuaaliosoite on 16-bittinen,
voitaisiin siitä varata kolme ylintä bittiä sivurekisterin valintaan. Näin tarvitaan kahdeksan sivurekisteriä. Kun suoritin sitten viittaa muistiin, valitaan kolmen ylimmän bitin osoittama sivurekisteri, jonka arvo yhdistetään virtuaaliosoitteen 13 alimpaan bittiin samalla tapaa kuin segmenttirekistereilläkin; saatu arvo on varsinainen muistiosoite. Sivurekisterille ei välttämättä ole asetettu mitään arvoa, jolloin viittaus sivuun aiheuttaa aina ajonaikaisen virheen.
Kuten segmentoivassa muistissa oli segmentin pituusrekisteri, oli sivurekisterikoneissa yleensä sivunpituusrekisteri, jolla huolehdittiin siitä, että muistiosoitukset eivät menneet toisen prosessin alueelle. Sivurekisterikoneessa sivun koko riippui koneen arkkitehtuurista. Esimerkissämme sivun maksimikoko oli 8 kilotavua.
Sivuttava muistihallinta, sivutus, on loogisesti sivurekisteri-idean jatko. Kun segmentti- ja sivurekisterit ovat selvästi suorittimen sisäisiä, on sivuttava muistinhallinta yleensä ratkaistu erillisellä muistihallintayksiköllä, joka tosin voi fyysisesti sijaita samalla sirulla suorittimen kanssa.
Sivuttavassa muistinhallinnassa muisti on jaettu tasakokoisiin sivuihin, joten sivunpituustieto on tarpeeton. Sivun koko on usein joko neljä tai kahdeksan kilotavua, mutta pienimmät sivutusjärjestelmät ovat käyttäneet 512 tavun sivuja ja suurimmat megatavuluokkaa olevia sivuja. Sivun koko on monissa järjestelmissä ohjelmallisesti muutettavissa.
Kuten sivurekisteriratkaisussa on myös sivuttavassa muistinhallinnassa virtuaalimuistiosoite jaettu (ainakin) kahteen osaan. Alin osa on osoite viitattavan sivun sisällä, ja siihen tarvitaan yleisessä 4 kilotavun sivussa 12 alinta bittiä. Ylempien bittien avulla etsitään oikea muistisivun alkuosoite. Koska pitkissä osoitteissa tämä aiheuttaisi valtavan määrän sivurekistereitä, jotka kaikki tulee vaihtaa ajettavaa prosessia vaihdettaessa, käytetään muunnokseen keskusmuistissa olevaa sivutaulua. Virtuaaliosoitteen ylimmillä biteillä valitaan sivutaulusta oikea rivi, jolta varsinaisen muistisivun osoite löytyy. Prosessia vaihdettaessa riittää vaihtaa sivutaulun alkuun osoittavan rekisterin arvo.
Koska edellä oleva tapa viitata muistiin aiheuttaisi jokaista ohjelman tekemää muistiviittausta kohti ainakin kaksi viittausta (ensin sivutaulun arvon lukeminen, jonka jälkeen lasketulla osoitteella varsinainen viittaus), käytetään apuna assosiatiivista välimuistia, jossa on muutaman viimeksi viitatun sivun tiedot. Tästä välimuistista käytetään useissa järjestelmissä nimitystä TLB (). Tämän välimuistin koko ei yleensä ole kovin suuri, muutamasta rekisteristä noin sataan. Huomaa, että vaikka TLB on välimuisti, se on sitä vain osoitteille. Tavallisesti välimuisti tarkoittaa suorittimessa tai muistinhallintayksikössä olevia muistipaikkoja, joilla voidaan vähentää varsinaisia keskusmuistiin asti meneviä muistiviittauksia. Normaalin välimuistin tapaan myös TLB voi olla hierarkkinen.
Kun edellä olevaan sivuttavaan muistinhallintaan lisätään tieto siitä, onko sivu keskusmuistissa vai ei, ja jos ei ole, mistä se voidaan löytää, saadaan aikaan virtuaalimuistijärjestelmä.
Virtuaalimuistijärjestelmässä koko ohjelman ei tarvitse olla kerrallaan keskusmuistissa, vaan osa siitä voi sijaita massamuistilla, tavallisesti levyllä. Kun tällaiseen massamuistilla olevaan sivuun viitataan, aiheutuu tästä keskeytys, ja käyttöjärjestelmä noutaa halutun sivun keskusmuistiin. Viitanneelle ohjelmalle tämä operaatio on loogisesti näkymätön, mutta kuluttaa tietenkin aikaa.
Koska virtuaalimuistijärjestelmissä sivutaulunkin koko kasvaa ylettömän suureksi – varsinkin, kun useimmissa ohjelmissa osoiteavaruudesta käytetään vain pientä osaa – siirretään usein osa sivutaulustakin pois keskusmuistista. Tällöin virtuaalimuistiosoitteen yläosa, jolla alun perin valittiin suoraan sivu sivutaulusta, jaetaan kahteen osaan, josta ylempi osa on aina keskusmuistissa ja alempi haetaan sinne tarvittaessa samaan tapaan kuin yllä kuvattiin tavalliselle muistisivulle. Tällainen virtuaalimuistijärjestelmä on nimeltään kaksitasoinen sivutaulu. Sivutaulussa voi olla useampiakin tasoja, jopa neljä.
Sivutauluratkaisu vie aina aika paljon keskusmuistitilaa, koska jokaiselle prosessille pitää olla oma sivutaulunsa. Tämän takia suurilla osoiteavaruuksilla käytetään myös käänteistä sivutaulua, jonka koko riippuu ainoastaan keskusmuistin koosta. Avukseen käänteinen sivutaulu tarvitsee toisen tietorakenteen, jonka avulla massamuistilla oleva sivu löytyy.
Segmentoivassa muistissa muisti suojataan segmenttikohtaisesti siten, että sitä voi käyttää vain asianomainen prosessi ja käyttöjärjestelmä. Samalla voidaan määritellä, voidaanko segmentin sisältöä muuttaa vai vain lukea.
Sivuttavassa muistissa oikeudet ovat sivukohtaisia, eli jokaista sivua kohti voidaan määritellä, ketkä voivat lukea ja ketkä muuttaa sen sisältöä. Näin mahdollistetaan se, että esimerkiksi ajettava ohjelma jaetaan usean prosessin kesken, kun prosessit eivät vahingossa pääse muuttamaan ajettavaa koodia. Samalla tavalla voidaan luoda yhteisen datamuistin alueita, joissa voi olla esimerkiksi ohjelman käyttämiä merkkijonoja ja muuta sellaista tietoa, jota ei ole tarkoitus ohjelman ajon aikana muuttaa.
Muistin suojaus voi perustua myös valtakirjoihin (). Tällaisessa ratkaisussa valtakirja sisältää muistissa olevan olion osoitteen ja käyttöoikeustiedot. Viittaukset olioon on tehtävä aina tämän valtakirjan kautta. Rakenne on harvinainen, vaikka sitä on joissain konearkkitehtuureissa kokeiltu, kuten Nokian Mikko-sarjan viimeiseksi jääneessä minitietokoneessa MPS-10.
Ilkka Haikala ja Hannu-Matti Järvinen: Käyttöjärjestelmät (luku 4), Talentum 2003, ISBN 951-762-837-4.