Arvutites tuleb protsessi käivitamiseks paigutada see mällu. Selleks tuleb mälus olevale protsessile määrata väli. Mälu eraldamine on oluline probleem, millest tuleb teadlik olla, eriti tuuma- ja süsteemiarhitektuurides.
Vaatame üksikasjalikult Linuxi mälujaotust ja mõistame, mis kulisside taga toimub.
Kuidas mälu eraldamine toimub?
Enamik tarkvarainsenere ei tea selle protsessi üksikasju. Kuid kui olete süsteemiprogrammeerija kandidaat, peaksite sellest rohkem teadma. Jaotamisprotsessi vaadeldes on vaja minna Linuxi ja selle kohta pisut üksikasjadesse glibc raamatukogu.
Kui rakendused vajavad mälu, peavad nad seda operatsioonisüsteemilt taotlema. See kerneli päring nõuab loomulikult süsteemikutset. Kasutajarežiimis ei saa te ise mälu eraldada.
The malloc() funktsioonide perekond vastutab mälu eraldamise eest C-keeles. Siin tuleb küsida, kas malloc() kui glibc-funktsioon teeb otsest süsteemikutset.
Linuxi tuumas pole süsteemikutset nimega malloc. Siiski on rakenduste mälunõuete jaoks kaks süsteemikutset, mis on brk ja mmap.
Kuna taotlete oma rakenduses mälu glibc funktsioonide kaudu, võite küsida, millist süsteemikõnedest glibc praegu kasutab. Vastus on mõlemad.
Esimene süsteemikõne: brk
Igal protsessil on külgnev andmeväli. brk süsteemikutsega suurendatakse programmi katkestuse väärtust, mis määrab andmevälja piiri ja teostatakse jaotusprotsess.
Kuigi selle meetodi abil on mälu eraldamine väga kiire, ei ole alati võimalik kasutamata ruumi süsteemi tagastada.
Näiteks mõelge, et eraldate funktsiooni malloc() kaudu süsteemikutsele brk viis välja, millest igaüks on 16 KB. Kui olete nendest väljadest number kaks lõpetanud, ei ole võimalik vastavat ressurssi (eraldamist) tagastada, et süsteem saaks seda kasutada. Sest kui vähendate aadressi väärtust, et näidata koht, kus teie väli number kaks algab, kutsudes välja brk, olete väljade kolm, neli ja viis eraldanud.
Selle stsenaariumi korral mälukaotuse vältimiseks jälgib malloci teostus glibc-s protsessiandmete väljal eraldatud kohti ja seejärel määrab selle tagastamise süsteemile koos funktsiooniga free(), et süsteem saaks kasutada vaba ruumi edasiseks mäluks eraldised.
Teisisõnu, pärast viit 16KB ala eraldamist, kui teine ala tagastatakse funktsiooniga free() ja teine 16KB ala küsitakse mõne aja pärast uuesti, brk süsteemikõne kaudu andmeala suurendamise asemel tagastatakse eelmine aadress.
Kui aga äsja taotletud ala on suurem kui 16 KB, suurendatakse andmeala, eraldades süsteemikutsega brk uue ala, kuna teist piirkonda ei saa kasutada. Kuigi ala number kaks ei ole kasutusel, ei saa rakendus seda suuruse erinevuse tõttu kasutada. Selliste stsenaariumide tõttu tekib olukord, mida nimetatakse sisemiseks killustatuks ja tegelikult saate harva kõiki mälu osi täiel määral kasutada.
Parema mõistmise huvides proovige kompileerida ja käivitada järgmine näidisrakendus:
#kaasa <stdio.h>
#kaasa <stdlib.h>
#kaasa <unistd.h>
intpeamine(int argc, char* argv[])
{
char *ptr[7];
int n;
printf("%s Pid: %d", argv[0], getpid());
printf("Esialgne programmipaus: %p", sbrk (0));
jaoks (n = 0; n<5; n++) ptr[n] = malloc (16 * 1024);
printf("Pärast 5 x 16 kB malloci: %p", sbrk (0));
tasuta(ptr[1]);
printf("Pärast vaba sekundit 16 kB: %p", sbrk (0));
ptr[5] = malloc (16 * 1024);
printf("Pärast 6. 16 kB eraldamist: %p", sbrk (0));
tasuta(ptr[5]);
printf("Pärast viimase ploki vabastamist: %p", sbrk (0));
ptr[6] = malloc (18 * 1024);
printf("Pärast uue 18kB eraldamist: %p", sbrk (0));
getchar();
tagasi0;
}
Rakenduse käivitamisel saate järgmise väljundiga sarnase tulemuse:
Pid of ./a.out: 31990
Esialgne programm murda: 0x55ebcadf4000
Pärast 5 x 16 kB malloci: 0x55ebcadf4000
Pärast teist 16 kB vaba: 0x55ebcadf4000
Pärast 6. 16 kB eraldamist: 0x55ebcadf4000
Pärast viimase ploki vabastamist: 0x55ebcadf4000
Pärast a uus18kB: 0x55ebcadf4000
brk väljund strace'iga on järgmine:
brk(NULL) = 0x5608595b6000
brk (0x5608595d7000) = 0x5608595d7000
Nagu sa näed, 0x21000 on lisatud andmevälja lõpuaadressile. Väärtusest saate sellest aru 0x5608595d7000. Nii umbes 0x21000, ehk eraldati 132KB mälu.
Siin tuleb arvestada kahe olulise punktiga. Esimene on näidiskoodis määratud summast suurema summa eraldamine. Teine on see, milline koodirida põhjustas eraldamise andnud brk-kõne.
Aadressiruumi paigutuse randomiseerimine: ASLR
Kui käivitate ülaltoodud näidisrakenduse üksteise järel, näete iga kord erinevaid aadressiväärtusi. Aadressiruumi juhuslik muutmine sel viisil muudab oluliselt keerulisemaks turvarünnakud ja suurendab tarkvara turvalisust.
Kuid 32-bitistes arhitektuurides kasutatakse aadressiruumi juhuslikuks muutmiseks tavaliselt kaheksat bitti. Bittide arvu suurendamine ei ole asjakohane, kuna ülejäänud bittide adresseeritav ala on väga väike. Samuti ei muuda ainult 8-bitiste kombinatsioonide kasutamine ründaja jaoks asja piisavalt keeruliseks.
Seevastu 64-bitistes arhitektuurides, kuna ASLR-i tööks saab eraldada liiga palju bitte, on palju suurem juhuslikkus ja turvalisuse tase suureneb.
Võimestab ka Linuxi kernel Android-põhised seadmed ja ASLR-i funktsioon on Android 4.0.3 ja uuemates versioonides täielikult aktiveeritud. Juba ainuüksi sel põhjusel poleks vale väita, et 64-bitine nutitelefon annab 32-bitiste versioonide ees olulise turvaeelise.
ASLR-i funktsiooni ajutiselt keelamisel järgmise käsuga näib, et eelmine testrakendus tagastab iga kord, kui seda käivitatakse, samad aadressiväärtused:
kaja0 | sudo tee /proc/sys/kernel/randomize_va_space
Eelmise oleku taastamiseks piisab, kui kirjutada samasse faili 0 asemel 2.
Teine süsteemikõne: mmap
mmap on teine süsteemikutse, mida kasutatakse Linuxis mälu eraldamiseks. MMA-kõnega kaardistatakse mis tahes mälupiirkonna vaba ruum helistamisprotsessi aadressiruumiga.
Sel viisil tehtud mälujaotuse korral, kui soovite tagastada eelmises brk-i näites teise 16KB-se partitsiooni funktsiooniga free(), ei ole selle toimingu takistamiseks mehhanismi. Vastav mälusegment eemaldatakse protsessi aadressiruumist. See märgitakse enam mittekasutatuks ja tagastatakse süsteemi.
Kuna mälu eraldamine mmap-iga on brk-ga võrreldes väga aeglane, on vaja brk-i eraldamist.
Mmapi abil kaardistatakse kõik vabad mälualad protsessi aadressiruumiga, nii et eraldatud ruumi sisu lähtestatakse enne selle protsessi lõppu. Kui lähtestamist sel viisil ei tehtud, pääses ka järgmine mitteseotud protsess juurde varem vastavat mälupiirkonda kasutanud protsessi kuuluvatele andmetele. See muudaks süsteemide turvalisusest rääkimise võimatuks.
Mälu eraldamise tähtsus Linuxis
Mälu eraldamine on väga oluline, eriti optimeerimise ja turvalisuse küsimustes. Nagu ülaltoodud näidetest näha, võib selle probleemi täielik mõistmine tähendada teie süsteemi turvalisuse hävitamist.
Isegi tõuke ja popi sarnased kontseptsioonid, mis eksisteerivad paljudes programmeerimiskeeltes, põhinevad mälu eraldamise operatsioonidel. Süsteemimälu hea kasutamise ja haldamise oskus on ülioluline nii manustatud süsteemi programmeerimisel kui ka turvalise ja optimeeritud süsteemiarhitektuuri väljatöötamisel.
Kui soovite ka Linuxi kerneli arendamisega tegeleda, kaaluge esmalt programmeerimiskeele C valdamist.
C-programmeerimiskeele lühitutvustus
Loe edasi
Seotud teemad
- Linux
- Arvuti mälu
- Linuxi kernel
Autori kohta
Insener ja tarkvaraarendaja, kes on matemaatika ja tehnoloogia fänn. Talle on alati meeldinud arvutid, matemaatika ja füüsika. Ta on arendanud nii mängumootorite projekte kui ka masinõpet, tehisnärvivõrke ja lineaaralgebra teeke. Lisaks jätkab ta tööd masinõppe ja lineaarsete maatriksitega.
Liituge meie uudiskirjaga
Liituge meie uudiskirjaga tehniliste näpunäidete, arvustuste, tasuta e-raamatute ja eksklusiivsete pakkumiste saamiseks!
Tellimiseks klõpsake siin