Kokeilin tutustua curses-kirjastoon Linuxilla. Käänsin seuraavan ohjelman:
#include <curses.h> // required int r,c, // current row and column (upper-left is (0,0)) nrows, // number of rows in window ncols; // number of columns in window void draw(char dc) { move(r,c); // curses call to move cursor to row r, column c delch(); insch(dc); // curses calls to replace character under cursor by dc refresh(); // curses call to update screen r++; // go to next row // check for need to shift right or wrap around if (r == nrows) { r = 0; c++; if (c == ncols) c = 0; } } main() { int i; char d; WINDOW *wnd; wnd = initscr(); // curses call to initialize window cbreak(); // curses call to set no waiting for Enter key noecho(); // curses call to set no echoing getmaxyx(wnd,nrows,ncols); // curses call to find size of window clear(); // curses call to clear screen, send cursor to position (0,0) refresh(); // curses call to implement all changes since last refresh r = 0; c = 0; while (1) { d = getch(); // curses call to input from keyboard if (d == 'q') break; // quit? draw(d); // draw the character } endwin(); // curses call to restore the original window and leave }
Käänsin ohjelman gcc:llä komennolla
gcc -g demo.c -lcurses
ja ajoin
valgrind ./a.out
ja painoin näppäintä q, niin sain ilmoituksen:
==5291== HEAP SUMMARY: ==5291== in use at exit: 102,646 bytes in 88 blocks ==5291== total heap usage: 95 allocs, 7 frees, 107,527 bytes allocated ==5291== ==5291== LEAK SUMMARY: ==5291== definitely lost: 0 bytes in 0 blocks ==5291== indirectly lost: 0 bytes in 0 blocks ==5291== possibly lost: 0 bytes in 0 blocks ==5291== still reachable: 102,646 bytes in 88 blocks ==5291== suppressed: 0 bytes in 0 blocks ==5291== Rerun with --leak-check=full to see details of leaked memory ==5291== ==5291== For counts of detected and suppressed errors, rerun with: -v ==5291== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Onko tuossa koodissa bugi, kun tuo still reachable ei näytä nollaa?
Koodistasi taitaa puuttua aika iso osa: tuossahan on vain yksi funktio, joka ei ole main-funktio, joten et varmasti ole voinut kääntää tuota ohjelmaa.
”Still reachable” tarkoittaa, että osoittimet asioihin ovat jossain tallessa. Tiedot ovat siis todennäköisesti ncursesin sisäisiä tietoja, jotka varataan alussa ja joita ei vapauteta, koska varaus on kertaluontoinen (eli ei vuoda jatkuvasti) ja tarpeellinen koko ncursesin käytön ajan.
Jos käytät valgrindin ohjeen mukaan parametreja --leak-check=full --show-leak-kinds=all, näet, mistä vuodot tulevat. Esimerkiksi yksinkertaisimmassa ncurses-ohjelmassa (vain initscr() ja endwin()) kaikki varaukset tapahtuvat alustusvaiheessa, ja vaikka alustusta toistaisi moneen kertaan, varauksia ei tule lisää.
Nykyaikainen käyttöjärjestelmä vapauttaa tiedot joka tapauksessa lopuksi, joten tuollainen kertaluontoinen ”muistivuoto” on melko yleinen käytäntö.
Metabolix kirjoitti:
Koodistasi taitaa puuttua aika iso osa: tuossahan on vain yksi funktio, joka ei ole main-funktio, joten et varmasti ole voinut kääntää tuota ohjelmaa.
Jeps. Kömmähdys, kopioin näköjään vain osan ohjelmasta tänne.
Metabolix kirjoitti:
Nykyaikainen käyttöjärjestelmä vapauttaa tiedot joka tapauksessa lopuksi, joten tuollainen kertaluontoinen ”muistivuoto” on melko yleinen käytäntö.
Yleinen? Jostain luin, että muistivuodot ovat aina virheellisiä ja ne tulisi korjata. No, mielipiteitä nettiin mahtuu. Ilmeisesti ei kannata käyttää aikaansa kirjastojen hiomiseen tai siihen, että tekee ohjelmista "vuotamattomat" versiot?
Jaska kirjoitti:
Yleinen? Jostain luin, että muistivuodot ovat aina virheellisiä ja ne tulisi korjata.
Muistivuoto on muistivuoto vasta silloin, kun muistia oikeasti vuotaa. Tässä tapauksessa taas voisi sanoa, että muistia laitetaan aluksi ämpärillinen sivuun ja jätetään siihen, joten kyseessä ei ole varsinaisesti vuoto.
Monissa kirjastoissa on käytännössä staattisia objekteja, jotka kuitenkin varataan dynaamisesti jonkin käytännön syyn takia. Syy voi olla muistin säästäminen, jokin muu optimointi tai se, että tarvittava määrä tiedetään vasta ohjelman käynnistyttyä. Esimerkiksi monet ohjelmat sisältävät sekä graafisen käyttöliittymän että komentorivitoimintoja, jolloin voidaan jättää grafiikan vaatima muisti varaamatta silloin, kun käyttäjä haluaa pelkän komentorivin. Periaate on siis yleisellä tasolla tällainen:
static char A[100]; // Huono: tämä muisti varataan aina. static char* B; // Parempi: tämä muisti varataan vain tarvittaessa. int main() { if (tarvitaan_ominaisuutta_B) { B = calloc(100, 1); } }
Tässä A ei aiheuta muistivuotoa ja B aiheuttaa, vaikka oikeasti molemmat vievät suunnilleen saman verran muistia ja tietyissä tilanteissa B säästää muistia. Monissa kirjastoissa B jätetään sitten syystä tai toisesta vapauttamatta.
Jaska kirjoitti:
Ilmeisesti ei kannata käyttää aikaansa kirjastojen hiomiseen tai siihen, että tekee ohjelmista "vuotamattomat" versiot?
Tietenkin kannattaa ”käyttää aikaa” tai oikeammin vain hankkia rutiinia ja osata tehdä omasta koodista sellaista, että ei itse aiheuta muistivuotoa. Jos taas syy on käyttämässäsi kirjastossa ja tiedät tehneesi itse kaiken oikein, et oikein mahda tilanteelle mitään.
http://invisible-island.net/ncurses/ncurses.faq.html#config_leaks
Eli ei se oikeasti vuoda. Se jättää vain vapauttamatta tuollaisen lopussa
Aihe on jo aika vanha, joten et voi enää vastata siihen.