3. Luento: Muistin hallinta Arto Salminen, arto.salminen@tut.fi
Agenda Mitä väliä? Erityyppiset muistit Ohjelman sijoittelu muistiin Ohjelman sisäinen muistinhallinta Muistinhallintayksikkö Välimuisti Yhteenveto
Mitä väliä vai onko ollenkaan? Ohjelmoijan näkökulma muistiin ja sen kulutukseen vaihtelee Ohjelman sijoittelu Ohjelmoijan mahdollisuudet erilaiset Sivuvaikutukset vaihtelevat Debuggaus voi vaatia asiantuntemusta Muistibudjetti
Erityyppiset muistit Luku-kirjoitusmuistit, RAM (Random Access Memory) Lukumuistit, ROM (Read Only Memory) Haihtumattomat muistit Patterivarmistus Sähköisesti tyhjennettävä muisti (EEPROM; Flash)
Luku-kirjoitusmuistit (RAM) Tavallisin vaihtoehto; oletusarvo Sisältö ei säily sähkökatkoksen yli, haihtuva muisti Yleensä nopeaa Useita eri alatyyppejä SRAM (Static RAM) Tieto säilyy niin kauan kuin käyttöjännitekin DRAM (Dynamic RAM) Tiedon säilyminen perustuu kapasitanssiin ja vaatii virkistämistä Vaatii vähemmän tilaa Toteutuksessa virkistys voi olla integroitu osaksi piiriä, joten ulkoisesti DRAM voi näyttää SRAM:lta Periaatteessa voidaan optimoida muistin ominaisuuksien kautta (esim. samalla sivulla olo tms) mutta harvoin kannattaa käytännössä
Fast Page Mode (FPM) Ensin kirjoitetaan muistiosoitteen ylemmät bitit (row) RAS\ (Row Access Strobe) jää alas Seuraavaksi kirjoitetaan alemmat (column) Käytetään CAS\ alhaalla (Column Access Strobe) -> Muistin lukeminen samalta riviltä on nopeampaa, koska riviosoitteen vaihdosta ei aiheudu viivettä
Lukumuistit Peruslukumuisti ROM Sisältö langoitetaan tekovaiheessa Nykyisin käytössä harvoin; esim. suorittimen sisällä johonkin erikoistarkoitukseen jos edes siellä Programmable ROM Muistin sisältö voidaan asettaa kerran Erasable PROM Voidaan tyhjentää ja kirjoittaa uudelleen Kirjoittaminen vaatii usein poikkeavan jännitteen; ei tehdä tuotantokäytössä Ohjelmoija ei voi muuttaa sisältöä Vanhanaikaisia; harvoin muuta kuin historiallista arvoa, mainittu tässä johdonmukaisuuden vuoksi
Haihtumattomat muistit Patterivarmennettu luku-kirjoitusmuisti SRAM jolle erityisjärjestelyin turvattu sähkönsyöttö Sähköisesti tyhjennettävä muisti EEPROM (Electrically EPROM) 1 sukupolvi -> erityisjännite muutettaessa 2 sukupolvi -> FLASH
Flash-muistin ominaisuuksia Tyhjennys isoina palasina Ensimmäiset tyhjennettävä kokonaan Myöhemmät piirit lohko kerrallaan Kirjoitus muistipaikka kerrallaan Rajoituksia käyttökertojen määrään liittyen; muistipaikkojen kierrätys Hidas lukea ja kirjoittaa; tavallisesti sisältö luetaan luku-kirjoitusmuistiin ennen käyttöä Ei tietystikään nopeuta kirjoitusta; sopii esim. ohjelman lataamiseen
Flash-muistin käytöstä Lohkotason tyhjennys ohjaa usein suunnittelua Sähkökatko pakottaa kopioimaan! Kirjoituskertojen rajoitukset voivat olla matalia Esim. 10.000-1.000.000 luotettavaa kirjoituskertaa Käyttötarkoitus voi ohjata piirin valintaa
2 Ominaisuuksiltaan eroavaa Flash-tyyppiä Sarjaliityntä (NAND) vs. osoiteperustainen liityntä (NOR) Osoiteperustaisessa liitynnässä edelleen 2 eri tyyppiä Vakiokokoiset lohkot, käyttö symmetristä (FAT-tyyppi) Muutama suurehko lohko ja paljon pieniä (bootsector-tyyppi) Suuret koodille, pienet muuttujille sopivia Tyypillinen tietokoneen alustusmuistin tallennuksessa (boot ROM, BIOS)
2 Ominaisuuksiltaan eroavaa Flash-tyyppiä NOR RAM mahdollinen Korvaa ROM piirin Kallista Lohkot 8k-128k 100k-1M kirjoituskertaa Tyhjennysaika 1 sek Voidaan kirjoittaa tavu kerrallaan Viallisia lohkoja ei ole uutena NAND Aksessoidaan sivu kerrallaan Korvaa massamuistin Halpaa Lohkot 32x512b / 64x2k 10k-100k kirjoituskertaa Tyhjennysaika 2 ms Voidaan kirjoittaa sivu kerrallaan Viallisia lohkoja voi olla uutena
Muistitiedon päivittämisestä Flashissa Bittien nollaus Tavallisesti tyhjä tieto muistipaikassa tarkoittaa että kaikki bitit ykkösiä Kirjoittamalla voidaan nollata, mutta vain koko lohko voidaan kerralla muuttaa ykkösiksi Tietorakenteilla ja suunnittelulla voidaan vähentää tyhjennystarvetta (esim. lukematon viesti -> 1, luettu viesti -> 0) Kokonaisia tiedostojärjestelmiä olemassa (WORM, write once, read mostly) Lohkon tunnistaminen Muuttujien paikat vaihtuvat kun lohkoja joudutaan päivittämään Ei vakiopaikkaa, jossa tieto käytettävästä muistista; yleensä jokainen lohko sisältää tietoa talletetusta tiedosta Lohkon koko?
Flash-muistin lohkon koko: pieni vs iso
Flash-muistin lohkon koko: pieni vs iso
Flashtiedostojärjestelmät Tiedostojärjestelmä pitää kirjaa tiedon fyysisistä tallennuspaikoista. YAFFS1 Objektit (tiedostot, kansiot) tallennetaan useille sivuille. 1. sivu sisältää objektin otsikkokentät ja seuraavat sivut täytetään datalla. Jokainen Flash-muistin sivu merkataan objektin tunnisteella, palanumerolla (chunk) ja muilla metatiedoilla. Tiedot tallennetaan spare area bitteihin. Kun tiedostoa muutetaan kirjoitetaan muuttuneet kohdat uusille Flash-muistin sivuille samoilla metatiedoilla. Vanhat sivut merkitään hylätyiksi. Myöhemmin hylätyt sivut alustetaan, jotta niitä voidaan käyttää uudelleen (carbage collection).
Ohjelman sijoittelu muistiin Ajettava ohjelma -> ROM, Flash Alustettu data, kirjoitussuojattu -> ROM, Flash Staattinen muistialue -> RAM Asennusparametrit (esim. tunnistetiedot) -> Patterivarmennettu RAM, Flash Dynaaminen muistialue (pino) -> RAM Oheislaitteet -> sijoitus absoluuttisiin osoitteisiin (näyttää RAM:lta) Keskeytysvektorit -> Sijoitus absoluuttiosoitteisiin (ROM); voidaan siirtää RAM-muistiin käynnistettäessä Patch-alue -> RAM, Flash
Erityispiirteitä Ohjelma valmiina muistissa heti; ei tarvita latausta Osa osoitteista suoraan laiteosoitteita Keskeytysvektori varaa osan laiteosoitteista Ei helppo toteuttaa suoraan alustetun datan aluetta Alustetun datan kopiointi lukumuistiin Kääntäjät tekevät tai eivät tee, kääntäjästä riippuen
Ohjelman sisäinen muistinhallinta Ohjelmoijan vastuulla Se miten koodi kirjoitetaan vaikuttaa merkittävästi muistinkulutukseen Tietorakenteet Muistin sijainti Staattisesti varattu muuttuja Dynaamisesti varattu muuttuja pinossa Dynaamisesti varattu muuttuja keossa Usein muistibudjetointi ennen suunnittelua, jotta voidaan olla varmoja muistin riittävyydestä
Nopea kertaus osoittimista int c=100; int *p = &c; void print() { printf(" c = %d\n", c); printf("&c = %p\n", (void *)&c); printf(" p = %p\n", (void *)p); } int main(void) { print(); *p++; print(); p = &c; (*p)++; print(); return 0; } http://codepad.org/ibdagnhe
Staattisesti varattu muuttuja Ohjelman suorituksen alussa luotuja muuttujia; muistiosoite on vakio (static) int x = 0; Voidaan myös piilottaa aliohjelmien sisään abstraktioihin liittyvistä syistä int * osoitin_stattiseen() { static int x = 0; return & x; }
Dynaamisesti varattu muuttuja pinossa Syntyvät pinoon ohjelman suorituksen edessä; häviävät yhtä lailla suorituksen edetessä Aliohjelmien sisäiset muuttujat Parametrit Paluuarvot int * osoitin_dynaamiseen() { // ELÄ IKINÄ TEE NÄIN! int x = 0; return & x; }
Dynaamisesti varattu muuttuja keossa Muisti varataan ja vapautetaan ohjelmoijan toimesta Malloc/Free, New/Delete jne. int * x = (int *)malloc(sizeof(int)) *x = 0;
Dynaaminen muisti ja ohjelmistosuunnittelu Kolme perusongelmaa Jäänneviittaukset Usein ohjelman yllättävä kaatuminen (segmentation fault etc); jos muistialue uudessa käytössä virheet mielikuvituksellisempia Roskaantuminen Usein hidastuminen, ja lopulta kaatuminen esim. muistin loppumiseen Pirstoutuminen Varausten ja vapautusten seurauksena eri kokoisia muistialueita; mikä valitaan seuraavaksi käytettäväksi muistia varattaessa? Seuraavassa joitakin käytännössä toimivia muistinkäytön strategioita
Vain varauksia Yksinkertaisin vaihtoehto Varaa kaikki dynaaminen muisti ohjelman suorituksen alussa Muuttujat erikseen? Kokonainen alue? address alkumuisti; int koko; address malloc(int varaa) { address apu; apu = alkumuisti; alkumuisti += varaa; koko -= varaa; return apu; } Voidaan myös toteuttaa prosessitasolla Käynnistys -> varaus Päättyminen -> vapautus Varsin toimiva ratkaisu kun halutaan varmentaa toiminta huolellisesti
Varauksia ja vapautuksia Ohjelmoija joutuu huolehtimaan jäänneviittauksista ja roskaantumisesta Oleellisimmat erot eri algoritmien välillä liittyvät pirstoutumiseen Nyrkkisääntöjä: Vapauta aikaisin, varaa myöhään Käytä standardikokoista varausyksikköä Varaus on hidasta; ehkä voisi varata samalla tilaa usealle tietoalkiolle?
Ohjelmointikielistä C Ohjelmoija voi päättää täysin mitä tehdään C++ Vaarallisempi, sillä muistinvaraus ja vapautus joskus verhojen takana (kokeile!) Java Roskienkeruu automaattista, voi yllättää kesken suorituskykykriittisen operaation JSR-001 Real-Time Specification for Java Ada Osa ominaisuuksista melko raskaita ja ennakoimattomia Kielen ominaisuudet voidaan korvata käyttöjärjestelmän mekanismeilla ennustettavuuden lisäämiseksi Skriptit Ennakointi lähes mahdotonta virtuaalikonetta tuntematta
Muuta huomattavaa Mitä suorempi kontrolli muistin suhteen, sitä selkeämpää debuggaus yleensä on Muistinhallinnassa avustava kalusto nimensä mukaisesti auttaa useimmissa tapauksissa, mutta rajatapausten ja ongelmien selvittelyn suhteen vastaava kalusto yleensä monimutkaistaa Siksi apuvälineiden toiminnan ymmärtäminen on tärkeää ohjelmaa suunnitellessa ja erityisesti debugatessa
Muistinhallintayksikkö Suojaa prosesseja Rekisterit tyhjennetään prosessia vaihdettaessa Hallitsee kunkin prosessin muistialuetta Kätkee epäjatkuvuuskohdat Muisti näyttää rakenteeltaan samanlaiselta Käyttöjärjestelmistä tuttu muistin jako sivuihin
Muistisivukohtaista tietoa Miten sivua voi käsitellä käyttöjärjestelmätilassa (luku/kirjoitus) Miten sivua voi käsitellä käyttäjätilassa (luku/kirjoitus) Onko sivu ollenkaan olemassa Onko sivu pinon vai tavallisen muistin osa (voi vaikuttaa laillisen osoitteen laskentaan) Käytetäänkö sivulle muunnosta vai päästetäänkö sivun osoitteet suoraan väylälle Onko välimuisti päällä vai ei (esim. lukitusrutiinien lukkomuuttujien ja oheislaitteiden ohjaus- ja tilarekisterien kohdalla ei saa olla päällä!)
Mahdollisia virhetilanteita Kirjoittaminen ohjelmakoodin päälle Ei välttämättä näy koskaan, mutta huomataan jos kyseessä lukumuisti Osoitteenmuunnoksessa huomataan ettei sivua ole olemassa Aito virhe (ei virtuaalimuistia) vs. läsnäolokeskeytys (virtuaalimuisti) Koska virtuaalimuistia ei yleensä sulautetuissa ole, lähdetään virheoletuksesta
Välimuisti Muistin ja prosessorin välissä sijaitseva keskusmuistia nopeampi muisti Pyrkii nostamaan ohjelman suorituskykyä Usein käytetyt muuttujat löytyvät nopeammin kuin mitä keskusmuistin kautta Sekä koodille että datalle Joskus useita tasoja (esim. L1 & L2; jos ei löydy tasolta 1, etsitään tasolta 2, ja vasta sitten keskusmuistista) Muutos voidaan kirjoittaa keskusmuistiin heti tai viivästettynä (esim. kun arvo poistuu välimuistista)
Välimuistista Nopeutus tilastollista Ohjelmat paikallisia -> viittaukset kohdistuvat ohjelman suorituksen jossakin vaiheessa tiettyihin muuttujiin, ja jossain toisessa vaiheessa joihinkin muihin muuttujiin Välimuistin tyhjennys kun joudutaan vaihtamaan prosessia Reaaliaikaisiin (ts. ajankäytöltään ennustettaviin) järjestelmiin joudutaan yleensä omaksumaan pessimistinen kanta Eli siis nopeutus useimmiten tapahtuu, mutta koska ei voida olla varmoja joudutaan elämään huonoimman mahdollisimman vaihtoehdon mukaan
Yhteenveto Eri muistityypit soveltuvat eri käyttöön Myös saman tyypin sisällä erilaisia toteutuksia, hintoja, kokoja, ominaisuuksia, jne. Muistin käytön suunnitteleminen ohjelmoijan vastuulla Sekä koko muistin kulutuksen osalta pienissä järjestelmissä Että yksittäisen prosessin osalta suuremmissa järjestelmissä MMU tarjoaa suojausmekanismin prosessien välille; ei aina vaihtoehto Välimuisti nopeuttaa tilastollisesti, mutta ei sovi kaikkein reaaliaikaisimpien ohjelmistojen toteuttamiseen Näkyy ohjelmoijalle eri tavoin (ominaisuudet, debugging, jne)