Számítógépekben ahhoz, hogy egy folyamat végrehajtható legyen, a memóriába kell helyezni. Ehhez egy mezőt kell hozzárendelni egy folyamathoz a memóriában. A memóriafoglalás egy fontos kérdés, amellyel tisztában kell lenni, különösen a kernel- és rendszerarchitektúrákban.

Vessünk egy pillantást a Linux memóriakiosztására, és értsük meg, mi történik a színfalak mögött.

Hogyan történik a memóriakiosztás?

A legtöbb szoftvermérnök nem ismeri ennek a folyamatnak a részleteit. De ha Ön rendszerprogramozó jelölt, többet kell tudnia róla. Ha megnézzük az allokációs folyamatot, egy kis részletre kell menni a Linuxon és a glibc könyvtár.

Ha az alkalmazásoknak memóriára van szükségük, azt az operációs rendszertől kell kérniük. Ez a kernel kérése természetesen rendszerhívást igényel. Felhasználói módban nem tud saját maga lefoglalni memóriát.

A malloc() függvénycsalád felelős a memóriafoglalásért a C nyelvben. Itt az a kérdés, hogy a malloc(), mint glibc függvény, végrehajt-e közvetlen rendszerhívást.

A Linux kernelben nincs malloc nevű rendszerhívás. Az alkalmazások memóriaigényéhez azonban két rendszerhívás létezik, amelyek igen

brk és mmap.

Mivel a glibc függvényeken keresztül memóriát kér az alkalmazásban, felmerülhet a kérdés, hogy a glibc melyik rendszerhívást használja jelenleg. A válasz mindkettő.

Az első rendszerhívás: brk

Minden folyamatnak van egy összefüggő adatmezője. A brk rendszerhívással az adatmező határát meghatározó programtörés értéke megemelkedik, és megtörténik az allokációs folyamat.

Bár a memóriafoglalás ezzel a módszerrel nagyon gyors, nem mindig lehetséges a fel nem használt terület visszajuttatása a rendszerbe.

Tegyük fel például, hogy öt, egyenként 16 KB méretű mezőt foglal le a brk rendszerhívással a malloc() függvényen keresztül. Ha végzett ezen mezők közül a kettes számmal, nem lehet visszaadni a megfelelő erőforrást (felosztást), hogy a rendszer használni tudja azt. Mert ha csökkenti a cím értékét, hogy megmutassa azt a helyet, ahol a kettes számú mező kezdődik, a brk hívásával, akkor a harmadik, négyes és ötös mezők felosztását végezzük el.

A memóriavesztés elkerülése érdekében ebben a forgatókönyvben a glibc malloc implementációja figyeli a folyamatadat-mezőben lefoglalt helyeket és majd megadja, hogy visszaküldje a rendszernek a free() függvénnyel, hogy a rendszer felhasználhassa a szabad helyet további memóriára kiutalások.

Más szavakkal, öt 16 KB-os terület lefoglalása után, ha a második területet a free() függvénnyel és egy másik 16 KB-os területtel adjuk vissza. A rendszer egy idő után újra kéri, ahelyett, hogy a brk rendszerhívással bővítené az adatterületet, az előző cím kerül visszaadásra.

Ha azonban az újonnan kért terület nagyobb, mint 16 KB, akkor az adatterület kibővül egy új terület kiosztásával a brk rendszerhívással, mivel a kettes terület nem használható. Bár a második számú terület nincs használatban, az alkalmazás a méretkülönbség miatt nem tudja használni. Az ehhez hasonló forgatókönyvek miatt belső töredezettségnek nevezett helyzet áll fenn, és valójában ritkán tudja a memória minden részét a lehető legteljesebb mértékben használni.

A jobb megértés érdekében próbálja meg lefordítani és futtatni a következő példaalkalmazást:

#beleértve <stdio.h>
#beleértve <stdlib.h>
#beleértve <unistd.h>
intfő-(int argc, char* argv[])
{
char *ptr[7];
int n;
printf("%s Pid: %d", argv[0], getpid());
printf("Kezdeti programszünet: %p", sbrk (0));
for (n=0; n<5; n++) ptr[n] = malloc (16 * 1024);
printf("5 x 16 kB malloc után: %p", sbrk (0));
ingyenes(ptr[1]);
printf("A második 16kB felszabadítása után: %p", sbrk (0));
ptr[5] = malloc (16 * 1024);
printf("A 16 kB 6. részének lefoglalása után: %p", sbrk (0));
ingyenes(ptr[5]);
printf("Az utolsó blokk felszabadítása után: %p", sbrk (0));
ptr[6] = malloc (18 * 1024);
printf("Új 18kB kiosztása után: %p", sbrk (0));
getchar();
Visszatérés0;
}

Az alkalmazás futtatásakor a következő kimenethez hasonló eredményt kap:

Pid of ./a.out: 31990
Kezdeti program szünet: 0x55ebcadf4000
5 x 16 kB malloc után: 0x55ebcadf4000
A második 16 kB felszabadítása után: 0x55ebcadf4000
A 16 kB 6. részének lefoglalása után: 0x55ebcadf4000
Az utolsó blokk felszabadítása után: 0x55ebcadf4000
Kiosztása után a új18kB: 0x55ebcadf4000

A brk kimenete a strace-vel a következő lesz:

brk(NULLA) = 0x5608595b6000
brk (0x5608595d7000) = 0x5608595d7000

Amint látod, 0x21000 hozzá lett adva az adatmező záró címéhez. Ezt az értékből meg lehet érteni 0x5608595d7000. Tehát hozzávetőlegesen 0x21000, vagy 132KB memóriát foglaltak le.

Itt két fontos szempontot kell figyelembe venni. Az első a mintakódban meghatározott összegnél nagyobb összeg kiosztása. Egy másik kérdés, hogy melyik kódsor okozta a brk hívást, amely az elosztást biztosította.

Címtér-elrendezés véletlenszerűsítése: ASLR

Amikor egymás után futtatja a fenti példaalkalmazást, minden alkalommal más-más címértéket fog látni. A címtér véletlenszerű megváltoztatása így jelentősen megnehezíti a munkáját biztonsági támadásokat és növeli a szoftverbiztonságot.

A 32 bites architektúrákban azonban általában nyolc bitet használnak a címtér véletlenszerű beállítására. A bitek számának növelése nem megfelelő, mivel a fennmaradó bitek címezhető területe nagyon alacsony lesz. Ezenkívül a mindössze 8 bites kombinációk használata nem nehezíti meg eléggé a támadó dolgát.

A 64 bites architektúrákban viszont, mivel túl sok bit allokálható az ASLR működéséhez, sokkal nagyobb a véletlenszerűség, és nő a biztonság mértéke.

A Linux kernel is hatalmat gyakorol Android alapú eszközök és az ASLR funkció teljesen aktiválva van az Android 4.0.3 és újabb verzióin. Már ezért sem lenne rossz azt állítani, hogy egy 64 bites okostelefon jelentős biztonsági előnyt jelent a 32 bites verziókkal szemben.

Ha ideiglenesen letiltja az ASLR funkciót a következő paranccsal, akkor úgy tűnik, hogy az előző tesztalkalmazás ugyanazokat a címértékeket adja vissza minden egyes futtatáskor:

visszhang0 | sudo tee /proc/sys/kernel/randomize_va_space

Az előző állapot visszaállításához elég lesz ugyanabba a fájlba 0 helyett 2-t írni.

A második rendszerhívás: mmap

Az mmap a második rendszerhívás, amelyet memóriakiosztásra használnak Linuxon. Az mmap hívásnál a memória tetszőleges területén lévő szabad terület a hívási folyamat címteréhez van rendelve.

Egy ilyen módon végrehajtott memóriafoglalásnál, amikor az előző brk példában a free() függvényt szeretné visszaadni a második 16 KB-os partíciónak, akkor nincs olyan mechanizmus, amely megakadályozná ezt a műveletet. A megfelelő memóriaszegmens eltávolításra kerül a folyamat címteréből. Már nem használtként jelöli meg, és visszaküldi a rendszerbe.

Mivel az mmap-pal a memóriafoglalás nagyon lassú a brk-hez képest, brk-lefoglalásra van szükség.

Az mmap használatával a memória minden szabad területe a folyamat címteréhez van rendelve, így a lefoglalt terület tartalma visszaállításra kerül a folyamat befejezése előtt. Ha az alaphelyzetbe állítás nem így történt volna, akkor a korábban az érintett memóriaterületet használó folyamathoz tartozó adatokhoz a következő nem kapcsolódó folyamat is hozzáférhetett. Ez lehetetlenné tenné, hogy a rendszerek biztonságáról beszéljünk.

A memóriafoglalás jelentősége Linuxban

A memóriafoglalás nagyon fontos, különösen az optimalizálási és biztonsági kérdésekben. Ahogy a fenti példákban is látható, a probléma teljes megértésének hiánya a rendszer biztonságának tönkretételét jelentheti.

Még a push és pop-hoz hasonló fogalmak is, amelyek sok programozási nyelvben léteznek, memóriafoglalási műveleteken alapulnak. A rendszermemória megfelelő használatának és elsajátításának képessége létfontosságú mind a beágyazott rendszer programozásában, mind a biztonságos és optimalizált rendszerarchitektúra kialakításában.

Ha a Linux kernelfejlesztésbe is bele akar merülni, először fontolja meg a C programozási nyelv elsajátítását.

Rövid bevezetés a C programozási nyelvbe

Olvassa el a következőt

Ossza megCsipogOssza megEmail

Kapcsolódó témák

  • Linux
  • Számítógép memória
  • Linux Kernel

A szerzőről

Fatih Küçükkarakurt (7 cikk megjelent)

Mérnök és szoftverfejlesztő, aki a matematika és a technológia rajongója. Mindig is szerette a számítógépeket, a matematikát és a fizikát. Játékmotor-projekteket, valamint gépi tanulást, mesterséges neurális hálózatokat és lineáris algebra-könyvtárakat fejlesztett ki. Továbbá továbbra is dolgozik a gépi tanuláson és a lineáris mátrixokon.

Továbbiak Fatih Küçükkarakurttól

Iratkozzon fel hírlevelünkre

Csatlakozzon hírlevelünkhöz műszaki tippekért, ismertetőkért, ingyenes e-könyvekért és exkluzív ajánlatokért!

Kattintson ide az előfizetéshez