Kirjautuminen

Haku

Tehtävät

Keskustelu: Ohjelmointikysymykset: C: Onko curses-ohjelmassa muistivuoto?

Jaska [03.07.2014 16:21:44]

#

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?

Metabolix [03.07.2014 16:42:40]

#

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ö.

Jaska [03.07.2014 17:01:13]

#

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?

Metabolix [03.07.2014 17:42:07]

#

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.

vuokkosetae [04.07.2014 02:18:28]

#

http://invisible-island.net/ncurses/ncurses.faq.html#config_leaks

Eli ei se oikeasti vuoda. Se jättää vain vapauttamatta tuollaisen lopussa

Vastaus

Aihe on jo aika vanha, joten et voi enää vastata siihen.

Tietoa sivustosta