Fórumok
Hello!
Akadt egy kis problémám a következő nagyon egyszerű kódrészlettel kapcsolatban:
#include stdio.h
#include stdlib.h
#include string.h
void main() {
char *String;
char *String_2;
String = (char *) malloc(255);
String_2 = (char *) malloc(255);
strcpy(String, "Valami");
printf("String = %s", String);
free(String);
strcpy(String_2, "Valami");
printf("String_2 = %s", String_2);
free(String_2);
/*Probléma*/
printf("String = %s\n", String);
printf("String_2 = %s\n", String_2);
}
A probléma a /*Problémánál*/ lép fel, a kimenet pontosan ez:
String = p?v?p?v?
String_2 = Valami
Ebből én arra következtetek hogy a String-nek lefoglalt memóriaterület felszabadult, de a String_2 nem? Rossz a következtetés? Ha nem, akkor mi lehet a baj?
Hozzászólások
Az, hogy a lefoglalt memóriaterületet felszabadítod, nem jelenti azt, hogy felülíródik ami a memóriaterületen volt. :)
mindket foglalas felszabadult.
csak veletlen, hogy a String_2 mukodni latszik.
miutan felszabaditottad free()-vel nem szabad hasznalnod a memoriateruletet.
A lényeg, hogy a pointereid értéke változatlan - mintha értékes dolgot mutatnának, pedig "véletlenszerű" valamire mutatnak. Abból ne vonj le semmiféle következtetést, hogy olykor (akár rendszeresen) a valami éppen az, amit a free() előtt odaírtál.
Én valami egészen mást nem értek. Ha felszabadította a memóriát és címez rá, akkor nem kellene elhasalnia segfault-tal a programnak?
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
Csak akkor, ha ott butasag van. Attol, hogy felszabaditott, meg nem feltetlenul fog elhasalni.
--
|8]
Nem vagyok programozó, de ez érdekel, mert ezek szerint butaság a fejemben van. Elmondom, mit gondoltam eddig erről, ezek szerint rosszul.
Az alkalmazás foglal memóriát, a malloc() visszaadja a pointert, ahonnan használható. Ha a kért területen belülre címez az alkalmazás, akkor a kernel fizikai memóriát lapoz alá. Ha rossz helyre címez, kivétel keletkezik, a kernel elveszi tőle a vezérlést, felszabadítja az alkalmazás helyét, a foglalt memóriáinak helyét, kinyírja tehát a hibás process-t. Úgy gondolom, free() után már nem az övé a memória, tehát ugyanennek kellene történnie.
Vagy esetleg ez úgy van, hogy ha más által foglalt területre címzünk, akkor van a fent leírt történés, ha nem lefoglalt területre, akkor legfeljebb memória szemetet olvasunk?
Vagy egészen máshogy néz ez ki? Ne hagyjatok tudatlanságban! :)
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
Máshogy néz ki. A standard C világkép úgy néz ki, hogy a program adatterületének az egyik végétől nő a malloc() által visszadott "új" világ. Először kb. nulla a mérete, de ahogy allokálgatsz, úgy növeszti a program az operációs rendszertől elkért (belapozott) terület méretét. A malloc() ebből egy-egy darabra ad vissza mutatókat. A felszabadítás viszont csak a program saját hatáskörében történik, azaz a felszabadított területet soha nem adja vissza az operációs rendszernek a program, a már belapozott terület sosem lapozódik ki. Azaz a free() után továbbra is ottmarad a memória, és amíg másra nem allokálod, és felül nem írod, addig még a tartalma is változatlan marad.
Természetesen a malloc/free() implementáció működése ebből a szempontból a környezet belügye, _akár_ meg is írhatják olyanra, hogy adjon vissza memóriaterületeket az operációs rendszernek, de szerintem ez nem standard működés, mivel nyilvánvalóan lassítja a programot már pusztán csak annak a felismerése, hogy lehet-e bármit visszaadni, lévén a lapozás legalább page méretekben történhet, a malloc() pedig ennél kisebb darabokat is kezel.
Ezek szerint a kernel csak akkor kapja vissza a RAM-ot, ha a process kilép, megszűnik?
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
az osszeset igen.
nagy blokkokat hamarabb is visszadhat, pl:
http://www.gnu.org/software/libc/manual/html_node/Efficiency-and-Malloc.html#Efficiency-and-Malloc
de ez malloc()/free() implementaciofuggo, ugye :).
Csak a virtual memory-t nem kapja vissza, amit allokált, a fizikai memoria kilapozható ilyenkor míg újra befoglalásra nem kerül (de lehet hogy csak mmap/munmap esetén, de ez megint allokátor függő így)
// Happy debugging, suckers
#define true (rand() > 10)
A kernel ahogy írtad is, a virtual memory-n keresztűl fizikai memóriát fog a progi alá adni. Viszont ha te egyszer 255 byte -ot foglalsz, akkor is egy teljes page kerül alád (a következő 255 byte malloc pedig majd ugyan ebbe a page-be kerül míg van benne hely, aztán kapsz másikat ha elfogyott). Ráadásként a free -vel valóban felszabadítod a memóriát, amit később újrafoglalhatsz, viszont a free nem jelenti azt hogy azonnal visszakerül a kernel kezébe a page, így ha később véletlen bele is írsz, nem biztos hogy azonnal segmentation fault az eredménye. Ráadásként ezek erősen allocator függő dolgok, a tcmalloc -nak pl rá kell csapni a kezére időnként hogy legalább a kernelig visszapasszolja az általa lefoglalt területeket
// Happy debugging, suckers
#define true (rand() > 10)
Elhasal, ha talál egy olyan hosszú "sztringet" a kiíráskor, ami valahol idegen területre lóg - addig védenek (és tévhitben tartanak) a kóbor 0-ák.
Aha. Tehát, ha jól értelek, nem lefoglalt területre szabad címezni, azt hagyja a kernel, az alkalmazás vessen magára. Akár írható is, gondolom, de lehet, el fogja lapozni a kernel, szóval ilyen területre nyilván nem lehet számítani. Ha viszont más által lefoglalt területre írna, vagy onnan olvasna adatot az alkalmazás, akkor a kernel SIGSEGV-vel beszántja.
Jól gondolom? Vagy legalább is ez már közelebb van az igazsághoz?
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
A malloc(3)-ból nem feltétlenül lesz rendszerhívás (sbrk(2) vagy mmap(2)), a free-ből pedig még ritkábban... tipikusan a libc illetékes része a free(3) után is megtartja a memóriát későbbi használatra.
Tehát, ha jól értelek, a kernelnek nem is adja vissza a memóriát minden esetben a free(). Tehát legfeljebb RAM-szemét lesz, amit olvas az ember, de a kernel úgy tudja, azt a területet ez a process kérte el, tehát nem cirkuszol, csak akkor, ha másik process területébe próbálnánk belepiszkálni.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
Icipicit módosítottam a kódon:
Aztán elkezdtem hívogatni 10 hatványaival.
10000-ezerig simán elment (noha 255 bájtos volt a foglalás), de végül beteljesedett a sorsa 100000-rel:
13514
13515
Segmentation fault (core dumped)
Vagyis vagy tényleg szabad hülyének lenni a heapen, vagy (ezt sem merem kizárni) mostanában, amikor ANNYI a RAM, a kernel alattomosan provízionál akkor is, ha senki sem kéri.
mire segfaulthoz ert nekem pont akkora lett a heap:
cat /proc/16716/maps
00400000-00401000 r-xp 00000000 00:11 256836 /tmp/test
00600000-00601000 rw-p 00000000 00:11 256836 /tmp/test
00601000-00622000 rw-p 00000000 00:00 0 [heap]
7ffff7a33000-7ffff7bd2000 r-xp 00000000 08:22 5183 /lib/x86_64-linux-gnu/libc-2.19.so
7ffff7bd2000-7ffff7dd2000 ---p 0019f000 08:22 5183 /lib/x86_64-linux-gnu/libc-2.19.so
...
0x622000 - 0x601000 = 0x21000 = 135168
szoval minden kerek.
Stimm.
Örülök, hogy a témába botlottam.
ja, hat ezt en sem tudtam, szoval utananeztem: alapbol heap nelkul indul, aztan
M_TOP_PAD
kvantumokkal noveli:http://www.gnu.org/software/libc/manual/html_node/Malloc-Tunable-Parameters.html#Malloc-Tunable-Parameters
es
man mallopt
-bol kiderul, hogyM_TOP_PAD
...
The default value for this parameter is 128*1024.
(szoval a glibc provizional, es ha fontos meg tudod szabni, hogy mennyit.)
A lenti, avatottabb kollégák kommentjei alapján a "provisioning" verzió áll (a processz kap egy 132k heapet (szépen ki is jön, hogy a 13514. tízbájtos szelet után bukik ki), és csak azon belül lehet heveskedni.
Vagyis én éltem eddig téves feltételezéssel: nem kell más processz útját keresztezni a segfaulthoz.
Aha, így már értem!
Köszönöm a segítséget! :)
Ahogy már többen is leírták, a programod működhet így, mivel felszabadított memóriaterületen nem garantált, hogy mi van éppen.
Én annyit jegyeznék meg, hogy:
- a felszabadított pointereket érdemes azonnal NULL-ra állítani. Akkor biztosan kiderül a hiba dereference-nél. Ha pedig véletlen megpróbálod még egyszer free()-zni, az ártalmatlan! (de nem így egy nem NULL, már felszabadított pointer esetében).
- ha valgrind-al ellenőrzöd a program memóriafelhasználását, akkor ő ki fogja neked dobni hibaként, amikor a felszabadított területekről olvasol.