Muistinhallintaohjeita

Syyskuussa 1997 uutisryhmässä sfnet.atk.ohjelmointi käytiin mallocin ja freen toteuttamiseen liittyvää keskustelua. Osana sitä Paul Keinänen postitti 10.9.1997 alle luetellut ohjeet, jotka olen toimittanut HTML-muotoon ja laittanut nähtäville Keinäsen luvalla. Ohjeet koskevat sulautettujen järjestelmien muistinhallintaa eivätkä kaikkea ohjelmointia.

-- Antti Valmari

Olen noudattanut seuravia periaatteita kun teen systeemejä, joiden tulee pysyä kuukausia tai vuosia keskeytyksettä pystyssä:

  1. Vältä varsinaisen ajon aikana mallocin ja freen kanssa pelaamista. Lopputuloksena on kuitenkin, että muistipooli fragmentoituu ennemmin tai myöhemmin pieniin palasiin ja oikein hirvittää ajatella, millaiselta sillisalaatilta muistipooli näyttää vuoden kuluttua ohjelman käynnistämisestä. Lisäksi malloc voi epäonnistua fragmentoinnista johtuen koska tahansa, joten jokaisen mallocin yhteyteen on tehtävä tarkistus, onnistuiko muistin varaus _ja_ennen_kaikkea_ mietittävä ylemmällä tasolla miten tästä toivutaan, koska yksinkertaisesti ei voida lyödä hanskoja naulaan (tyyliin GPF, kuten eräissä "käyttöjärjestelmissä" on tapana).

    Jostain C-ohjelmointiin on pesiytynyt tapa varata jo muutaman kilon vakiomittaista työtilaa varten funktion alussa lohko mallocilla ja funktion lopussa vapauttaa se freellä. Minulla on tapana laittaa etukäteen tiedossa olevat funktion työtilojen varaukset ihan reilusti paikallisiin (automatic) muuttujiin (ainakin muutamaan kymmeneen kilotavuun asti) ja vastaavasti varata lisää pinoa tätä varten. Tällöin muistin allokointi ja deallokointi tapahtuu automaattisesti funktioon tultaessa ja sieltä poistuttaessa. 80186 prosessori tietysti tässä tapauksessa aiheuttaa sen rajoituksen, ettei pinoa kannata juuri yli 64 kiloa varata. Paikallisten taulukoiden kanssa tietysti pitää olla varovainen, ettei ohjelmassa tapahdu taulukon ylityksiä, se kun helposti tuhoaa paluuosoitteen :-). Pahin mahdollinen pinon muistinkulutus on ennealta laskettavissa (rekursiota pitää ehdottomasti välttää) muistaen myös huomioida keskeytysten mahdolliset tarpeet. Laskelman oikeellisuus voidaan vielä tarkistaa, täyttämällä alussa pinoalue 0xFFFF sanoilla, jolloin pidemmän ajan kuluttua voi käydä tarkistamassa, koinka pitkällä pino on käynyt.

  2. Jos mallocia joudutaan käyttämään, kannattaa kaikki muistivaraukset tehdä ohjelman käynnistyksen yhteydessä, jos se esim. parametroinnin perusteella on laskettavissa. Tällöin vältytään ajonaikaisen freen käytöstä, jolloin fragmentoitumisriskiä ei ole.

  3. Jos kaikesta huolimatta pitää ajon aikana varailla ja vapautella muisilohkoja eri käyttötarkoituksiin, kannattaa järjestelmän käynnistyessä varata (lähes) koko vapaa muisti kiinteänkokoisia lohkoja ja ketjuttaa nämä vapaiden lohkojen listaan. Aina kun tarvitaan muistia, otetaan lohko vapaiden listasta ja laitetaan sovelluskohtaiseen listaan. Vastaavasti käytön loppuessa lohko palautetaan vapaiden listaan. Tätä systeemiä käytetään yleisesti käyttöjärjestelmissä. Tämän mekanismin voisi periaatteessa piilottaa malloc/free rajapinnan taakse, mutta silloin pitää etukateen selvittää, mikä on pisin kerralla varattava lohko ja rakentaa kaikki lohkot saman mittaisiksi (+ ketjutusheaderit). Jos tarve olisi yksi pitkä varaus ja paljon lyhyitä varauksia, on kussakin lohkossa keskimäärin paljon tyhjää hukkaan heitettyä tilaa, mutta sille ei kertakaikkiaan mahda mitään. Ketjutettujen vakiomittaisten lohkojen kanssa täytyy tietysti jokaisessa varausyrityksessä varautua siihen, ettei vapaata lohkoa ole, mutta se ilmenee samalla muistitarpeella heti startin yhteydessä, joten testaaminen on helppoa.

    AV:n kommentti: Kaikkien lohkojen ei välttämättä tarvitse olla saman mittaisia, jolloin keskimääräinen hukkaan heitetty tila saadaan pysymään kohtuullisena. Tällaiset järjestelmät ovat kuitenkin niin monimutkaisia, että niitä ei yleensä kannata tehdä itse. Katso kurssin 81120 Tietorakenteet ja algoritmit materiaali. Yksi kompromissi on ottaa käyttöön muutama koko, mutta silloin pitää etukäteen sopia, kuinka iso osa muistista annetaan millekin lohkokoolle.

    Järjestelmissä, joiden on tarkoitus toimia pitään, on vapaan muistin pirstoutumista varottava kuin ruttoa, sillä muutoin saadaan aikaan järjestelmiä, jotka testipenkissä toimivat ongelmitta, mutta alkavat vuoden jälkeen osoittamaan kaikennäköisiä selittämättömiä muistin varauksen epäonnistumisista johtuvia oireita. Kun laitteen ottaa testipenkkiin, se toimii taas virran päällelaittamisen jälkeen kuin enkeli.