V računalnikih, da je proces izvedljiv, ga je treba shraniti v pomnilnik. Za to je treba procesu v pomnilniku dodeliti polje. Dodeljevanje pomnilnika je pomembno vprašanje, ki se ga je treba zavedati, zlasti v arhitekturi jedra in sistema.

Oglejmo si podrobno razporeditev pomnilnika v Linuxu in razumemo, kaj se dogaja v ozadju.

Kako se izvaja dodeljevanje pomnilnika?

Večina programskih inženirjev ne pozna podrobnosti tega procesa. Če pa ste kandidat za sistemskega programerja, bi morali vedeti več o tem. Ko pogledamo postopek dodeljevanja, se je treba malo poglobiti v Linux in glibc knjižnica.

Ko aplikacije potrebujejo pomnilnik, ga morajo zahtevati od operacijskega sistema. Ta zahteva iz jedra bo seveda zahtevala sistemski klic. V uporabniškem načinu ne morete sami dodeliti pomnilnika.

The malloc() družina funkcij je odgovorna za dodeljevanje pomnilnika v jeziku C. Tukaj je treba zastaviti vprašanje, ali malloc() kot funkcija glibc izvaja neposreden sistemski klic.

V jedru Linuxa ni sistemskega klica, imenovanega malloc. Vendar pa obstajata dva sistemska klica za zahteve po pomnilniku aplikacij, ki sta

brk in mmap.

Ker boste v aplikaciji zahtevali pomnilnik prek funkcij glibc, se morda sprašujete, kateri od teh sistemskih klicev glibc trenutno uporablja. Odgovor je oboje.

Prvi sistemski klic: brk

Vsak proces ima sosednje podatkovno polje. S sistemskim klicem brk se vrednost preloma programa, ki določa mejo podatkovnega polja, poveča in izvede se postopek alokacije.

Čeprav je dodeljevanje pomnilnika s to metodo zelo hitro, ni vedno mogoče vrniti neporabljenega prostora v sistem.

Upoštevajte na primer, da s sistemskim klicem brk prek funkcije malloc() dodelite pet polj, vsako po 16 KB. Ko končate s številko dve od teh polj, ni mogoče vrniti ustreznega vira (deallocation), da bi ga sistem lahko uporabil. Ker če zmanjšate vrednost naslova, da prikažete kraj, kjer se vaše polje številka dve začne, s klicem brk, boste opravili razbremenitev za polja s številkami tri, štiri in pet.

Da bi preprečili izgubo pomnilnika v tem scenariju, implementacija malloc v glibc spremlja mesta, dodeljena v polju podatkov procesa in nato določi, da ga vrne v sistem s funkcijo free(), tako da lahko sistem uporabi prosti prostor za nadaljnji pomnilnik dodelitve.

Z drugimi besedami, po petih dodeljenih območjih velikosti 16 KB, če je drugo območje vrnjeno s funkcijo free() in drugo območje 16 KB se čez nekaj časa znova zahteva, namesto povečanja podatkovnega območja s sistemskim klicem brk, se vrne prejšnji naslov.

Če pa je novo zahtevano območje večje od 16 KB, se bo podatkovno območje povečalo z dodelitvijo novega območja s sistemskim klicem brk, saj območja dva ni mogoče uporabiti. Čeprav območje številka dve ni v uporabi, ga aplikacija ne more uporabiti zaradi razlike v velikosti. Zaradi takšnih scenarijev pride do situacije, imenovane notranja razdrobljenost, in pravzaprav lahko le redko vse dele pomnilnika uporabite v celoti.

Za boljše razumevanje poskusite prevesti in zagnati naslednjo vzorčno aplikacijo:

#vključi <stdio.h>
#vključi <stdlib.h>
#vključi <unistd.h>
intglavni(int argc, char* argv[])
{
char *ptr[7];
int n;
printf("Pid %s: %d", argv[0], getpid());
printf("Začetni premor programa: %p", sbrk (0));
za (n=0; n<5; n++) ptr[n] = malloc (16 * 1024);
printf("Po 5 x 16 kB malloc: %p", sbrk (0));
prost(ptr[1]);
printf("Po prostih drugih 16kB: %p", sbrk (0));
ptr[5] = malloc (16 * 1024);
printf("Po dodelitvi 6. od 16 kB: %p", sbrk (0));
prost(ptr[5]);
printf("Po sprostitvi zadnjega bloka: %p", sbrk (0));
ptr[6] = malloc (18 * 1024);
printf("Po dodelitvi novih 18 kB: %p", sbrk (0));
getchar();
vrnitev0;
}

Ko zaženete aplikacijo, boste dobili rezultat, podoben naslednjemu izhodu:

Pid od ./a.out: 31990
Začetni program zlomiti: 0x55ebcadf4000
Po 5 x 16 kB malloc: 0x55ebcadf4000
Po prostih drugih 16 kB: 0x55ebcadf4000
Po dodelitvi 6. od 16 kB: 0x55ebcadf4000
Po sprostitvi zadnjega bloka: 0x55ebcadf4000
Po dodelitvi a novo18kB: 0x55ebcadf4000

Izhod za brk s strace bo naslednji:

brk(NIČ) = 0x5608595b6000
brk (0x5608595d7000) = 0x5608595d7000

kot lahko vidite, 0x21000 je bil dodan končnemu naslovu podatkovnega polja. To lahko razumete iz vrednosti 0x5608595d7000. Torej približno 0x21000, ali je bilo dodeljenih 132 KB pomnilnika.

Tukaj je treba upoštevati dve pomembni točki. Prvi je dodelitev več kot znesek, določen v vzorčni kodi. Drugo je, katera vrstica kode je povzročila klic brk, ki je zagotovil dodelitev.

Naključna razporeditev naslovnega prostora: ASLR

Ko zaženete zgornji primer aplikacije eno za drugo, boste vsakič videli različne vrednosti naslova. Naključna sprememba naslovnega prostora na ta način močno oteži delo varnostnih napadov in poveča varnost programske opreme.

Vendar pa se v 32-bitnih arhitekturah osem bitov na splošno uporablja za naključno razporeditev naslovnega prostora. Povečanje števila bitov ne bo primerno, saj bo naslovljivo območje nad preostalimi biti zelo majhno. Tudi uporaba samo 8-bitnih kombinacij napadalcu ne oteži stvari dovolj.

Po drugi strani pa je pri 64-bitnih arhitekturah, ker je za delovanje ASLR mogoče dodeliti preveč bitov, zagotovljena veliko večja naključnost in stopnja varnosti se poveča.

Jedro Linuxa je tudi moč Naprave, ki temeljijo na sistemu Android in funkcija ASLR je v celoti aktivirana v sistemu Android 4.0.3 in novejših. Tudi zaradi tega razloga ne bi bilo napačno reči, da 64-bitni pametni telefon zagotavlja znatno varnostno prednost pred 32-bitnimi različicami.

Če začasno onemogočite funkcijo ASLR z naslednjim ukazom, se bo izkazalo, da prejšnja testna aplikacija ob vsakem zagonu vrne enake naslovne vrednosti:

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

Če ga želite obnoviti v prejšnje stanje, bo dovolj, da v isto datoteko zapišete 2 namesto 0.

Drugi sistemski klic: mmap

mmap je drugi sistemski klic, ki se uporablja za dodeljevanje pomnilnika v Linuxu. S klicem mmap se prosti prostor na katerem koli območju pomnilnika preslika v naslovni prostor klicnega procesa.

Pri dodelitvi pomnilnika na ta način, ko želite vrniti drugo 16KB particijo s funkcijo free() v prejšnjem primeru brk, ni mehanizma, ki bi preprečil to operacijo. Ustrezen segment pomnilnika se odstrani iz naslovnega prostora procesa. Označen je kot neuporabljen in vrnjen v sistem.

Ker so dodelitve pomnilnika z mmap zelo počasne v primerjavi s tistimi z brk, je potrebna dodelitev brk.

Z mmap je katero koli prosto območje pomnilnika preslikano v naslovni prostor procesa, tako da se vsebina dodeljenega prostora ponastavi, preden se ta proces zaključi. Če ponastavitev ni bila izvedena na ta način, bi lahko naslednji nepovezan proces dostopal tudi do podatkov, ki pripadajo procesu, ki je prej uporabljal ustrezno pomnilniško področje. Zaradi tega bi bilo nemogoče govoriti o varnosti v sistemih.

Pomen dodeljevanja pomnilnika v Linuxu

Dodelitev pomnilnika je zelo pomembna, zlasti pri optimizaciji in varnostnih vprašanjih. Kot je razvidno iz zgornjih primerov, lahko nerazumevanje tega vprašanja uniči varnost vašega sistema.

Tudi koncepti, podobni push in pop, ki obstajajo v mnogih programskih jezikih, temeljijo na operacijah dodeljevanja pomnilnika. Znati dobro uporabljati in obvladovati sistemski pomnilnik je ključnega pomena tako pri programiranju vgrajenega sistema kot pri razvoju varne in optimizirane sistemske arhitekture.

Če se želite tudi vi potopiti v razvoj jedra Linuxa, najprej razmislite o obvladovanju programskega jezika C.

Kratek uvod v programski jezik C

Preberite Naprej

DelitiTweetDelitiE-naslov

Povezane teme

  • Linux
  • Računalniški pomnilnik
  • Jedro Linuxa

O avtorju

Fatih Küçükkarakurt (7 objavljenih člankov)

Inženir in razvijalec programske opreme, ki je ljubitelj matematike in tehnologije. Od nekdaj je imel rad računalništvo, matematiko in fiziko. Razvil je projekte motorjev za igre, pa tudi strojno učenje, umetne nevronske mreže in knjižnice linearne algebre. Poleg tega še naprej dela na strojnem učenju in linearnih matrikah.

Več od Fatih Küçükkarakurt

Naročite se na naše novice

Pridružite se našemu glasilu za tehnične nasvete, ocene, brezplačne e-knjige in ekskluzivne ponudbe!

Kliknite tukaj, da se naročite