În computere, pentru ca un proces să fie executabil, acesta trebuie să fie plasat în memorie. Pentru aceasta, un câmp trebuie să fie atribuit unui proces din memorie. Alocarea memoriei este o problemă importantă de care trebuie să fiți conștienți, în special în arhitecturile de nucleu și de sistem.
Să aruncăm o privire la alocarea memoriei Linux în detaliu și să înțelegem ce se întâmplă în culise.
Cum se face alocarea memoriei?
Majoritatea inginerilor de software nu cunosc detaliile acestui proces. Dar dacă ești un candidat programator de sistem, ar trebui să știi mai multe despre asta. Când ne uităm la procesul de alocare, este necesar să intrăm în puține detalii despre Linux și glibc bibliotecă.
Când aplicațiile au nevoie de memorie, trebuie să o solicite de la sistemul de operare. Această solicitare de la nucleu va necesita în mod natural un apel de sistem. Nu vă puteți aloca memorie în modul utilizator.
The malloc() familia de funcții este responsabilă pentru alocarea memoriei în limbajul C. Întrebarea de pus aici este dacă malloc(), ca funcție glibc, efectuează un apel direct de sistem.
Nu există un apel de sistem numit malloc în nucleul Linux. Cu toate acestea, există două solicitări de sistem pentru cereri de memorie pentru aplicații, care sunt brk și mmap.
Deoarece veți solicita memorie în aplicația dvs. prin intermediul funcțiilor glibc, este posibil să vă întrebați care dintre aceste apeluri de sistem folosește glibc în acest moment. Răspunsul este ambele.
Primul apel de sistem: brk
Fiecare proces are un câmp de date contiguu. Cu apelul de sistem brk, valoarea pauzei de program, care determină limita câmpului de date, este mărită și se realizează procesul de alocare.
Deși alocarea memoriei cu această metodă este foarte rapidă, nu este întotdeauna posibil să returnați spațiul neutilizat sistemului.
De exemplu, luați în considerare că alocați cinci câmpuri, fiecare cu dimensiunea de 16 KB, cu apelul de sistem brk prin intermediul funcției malloc(). Când ați terminat cu numărul doi dintre aceste câmpuri, nu este posibil să returnați resursa relevantă (dealocare), astfel încât sistemul să o poată utiliza. Pentru că dacă reduceți valoarea adresei pentru a afișa locul de unde începe câmpul dvs. numărul doi, cu un apel la brk, veți fi făcut dealocarea pentru câmpurile cu numărul trei, patru și cinci.
Pentru a preveni pierderea memoriei în acest scenariu, implementarea malloc în glibc monitorizează locurile alocate în câmpul de date de proces și apoi specifică să-l returneze sistemului cu funcția free(), astfel încât sistemul să poată folosi spațiul liber pentru memorie suplimentară alocările.
Cu alte cuvinte, după ce sunt alocate cinci zone de 16KB, dacă a doua zonă este returnată cu funcția free() și o altă zonă de 16KB este solicitat din nou după un timp, în loc să se mărească zona de date prin apelul de sistem brk, se returnează adresa anterioară.
Cu toate acestea, dacă zona nou solicitată este mai mare de 16 KB, atunci zona de date va fi mărită prin alocarea unei noi zone cu apelul de sistem brk, deoarece zona a doua nu poate fi utilizată. Deși zona numărul doi nu este utilizată, aplicația nu o poate folosi din cauza diferenței de dimensiune. Din cauza unor scenarii ca acesta, există o situație numită fragmentare internă și, de fapt, rareori puteți folosi toate părțile memoriei la maximum.
Pentru o mai bună înțelegere, încercați să compilați și să rulați următorul exemplu de aplicație:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
intprincipal(int argc, char* argv[])
{
char *ptr[7];
int n;
printf("Pid de %s: %d", argv[0], getpid());
printf("Pauza inițială a programului: %p", sbrk (0));
pentru (n=0; n<5; n++) ptr[n] = malloc (16 * 1024);
printf("După 5 x 16 kB malloc: %p", sbrk (0));
liber(ptr[1]);
printf("După eliberarea secundei 16 kB: %p", sbrk (0));
ptr[5] = malloc (16 * 1024);
printf("După alocarea a 6-a din 16 kB: %p", sbrk (0));
liber(ptr[5]);
printf("După eliberarea ultimului bloc: %p", sbrk (0));
ptr[6] = malloc (18 * 1024);
printf("După alocarea unui nou 18kB: %p", sbrk (0));
getchar();
întoarcere0;
}
Când rulați aplicația, veți obține un rezultat similar cu următorul rezultat:
Codul ./a.out: 31990
Programul inițial pauză: 0x55ebcadf4000
După 5 x 16 kB malloc: 0x55ebcadf4000
După eliberarea a doua 16kB: 0x55ebcadf4000
După alocarea a 6-a din 16 kB: 0x55ebcadf4000
După eliberarea ultimului bloc: 0x55ebcadf4000
După alocarea unui nou18kB: 0x55ebcadf4000
Ieșirea pentru brk cu strace va fi după cum urmează:
brk(NUL) = 0x5608595b6000
brk (0x5608595d7000) = 0x5608595d7000
După cum puteți vedea, 0x21000 a fost adăugat la adresa finală a câmpului de date. Puteți înțelege acest lucru din valoare 0x5608595d7000. Deci aproximativ 0x21000, sau au fost alocate 132 KB de memorie.
Există două puncte importante de luat în considerare aici. Prima este alocarea a mai mult decât suma specificată în codul eșantionului. Un altul este ce linie de cod a provocat apelul brk care a furnizat alocarea.
Randomizare aspect spațiu de adrese: ASLR
Când rulați exemplul de aplicație de mai sus unul după altul, veți vedea de fiecare dată valori diferite ale adresei. Modificarea aleatorie a spațiului de adrese în acest fel complică semnificativ munca atacuri de securitate și crește securitatea software-ului.
Cu toate acestea, în arhitecturile pe 32 de biți, opt biți sunt în general folosiți pentru a randomiza spațiul de adrese. Creșterea numărului de biți nu va fi adecvată deoarece zona adresabilă peste biții rămași va fi foarte scăzută. De asemenea, utilizarea doar a combinațiilor de 8 biți nu face lucrurile destul de dificile pentru atacator.
În arhitecturile pe 64 de biți, pe de altă parte, deoarece există prea mulți biți care pot fi alocați pentru funcționarea ASLR, este oferită o aleatorie mult mai mare, iar gradul de securitate crește.
De asemenea, nucleul Linux are putere Dispozitive bazate pe Android iar caracteristica ASLR este complet activată pe Android 4.0.3 și versiuni ulterioare. Chiar și din acest motiv, nu ar fi greșit să spunem că un smartphone pe 64 de biți oferă un avantaj semnificativ de securitate față de versiunile pe 32 de biți.
Prin dezactivarea temporară a caracteristicii ASLR cu următoarea comandă, va apărea că aplicația de testare anterioară returnează aceleași valori de adresă de fiecare dată când este rulată:
ecou0 | sudo tee /proc/sys/kernel/randomize_va_space
Pentru a-l readuce la starea anterioară, va fi suficient să scrieți 2 în loc de 0 în același fișier.
Al doilea apel de sistem: mmap
mmap este al doilea apel de sistem folosit pentru alocarea memoriei pe Linux. Cu apelul mmap, spațiul liber din orice zonă a memoriei este mapat la spațiul de adresă al procesului de apelare.
Într-o alocare de memorie făcută astfel, când doriți să returnați a doua partiție de 16KB cu funcția free() din exemplul anterior brk, nu există niciun mecanism care să împiedice această operațiune. Segmentul de memorie relevant este eliminat din spațiul de adrese al procesului. Este marcat ca nu mai este folosit și returnat la sistem.
Deoarece alocările de memorie cu mmap sunt foarte lente în comparație cu cele cu brk, este necesară alocarea brk.
Cu mmap, orice zonă liberă de memorie este mapată la spațiul de adrese al procesului, astfel încât conținutul spațiului alocat este resetat înainte ca acest proces să fie finalizat. Dacă resetarea nu a fost efectuată în acest fel, datele aparținând procesului care a folosit anterior zona de memorie relevantă ar putea fi accesate și de următorul proces fără legătură. Acest lucru ar face imposibil să vorbim despre securitatea sistemelor.
Importanța alocării memoriei în Linux
Alocarea memoriei este foarte importantă, mai ales în probleme de optimizare și securitate. După cum se vede în exemplele de mai sus, a nu înțelege pe deplin această problemă poate însemna distrugerea securității sistemului.
Chiar și concepte similare cu push și pop care există în multe limbaje de programare se bazează pe operațiuni de alocare a memoriei. A fi capabil să utilizeze și să stăpânească bine memoria sistemului este vital atât în programarea sistemului încorporat, cât și în dezvoltarea unei arhitecturi de sistem sigure și optimizate.
Dacă, de asemenea, doriți să vă înmuiați degetele în dezvoltarea nucleului Linux, luați în considerare mai întâi să stăpâniți limbajul de programare C.
O scurtă introducere în limbajul de programare C
Citiți în continuare
Subiecte asemănătoare
- Linux
- Memoria computerului
- Kernel Linux
Despre autor
Un inginer și dezvoltator de software care este un fan al matematicii și al tehnologiei. Întotdeauna i-au plăcut computerele, matematica și fizica. El a dezvoltat proiecte de motoare de jocuri, precum și învățare automată, rețele neuronale artificiale și biblioteci de algebră liniară. În plus, continuă să lucreze la învățarea automată și la matrice liniare.
Aboneaza-te la newsletter-ul nostru
Alăturați-vă buletinului nostru informativ pentru sfaturi tehnice, recenzii, cărți electronice gratuite și oferte exclusive!
Click aici pentru a te abona