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ä:
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.
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.
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.