L’espai de memòria d’un procés

Bloc 1 · Desenvolupament de Programari en Espai d’Usuari

Jordi Mateo Fornés

Introducció

Memòria d’un procés

Stack · Heap · Data · Text

Objectius d’aprenentatge

Objectius d'Aprenentatge
  • Descriure el model conceptual de l’espai d’adreces: Text · Data · Heap · Stack
  • Classificar variables i funcions segons la regió de memòria on viuen
  • Explicar què és un stack frame i el lifetime d’una variable automàtica
  • Justificar per què calen reserves al heap per a dades que sobreviuen a la funció que les crea.

Prerequisits

Prerequisits
  • Punters i l’operador & (Pralab 01, Pralab 02)
  • Sintaxi bàsica de definició i crida de funcions en C

L’espai d’adreces

El procés té un espai d’adreces

Procés

Concepte

Aquest és un model conceptual, no el mapa físic de la RAM. Les adreces que fa servir el procés són adreces del seu espai virtual.

Bona Pràctica

Aquest model omet la regió mmap (biblioteques compartides), l’espai de kernel i qualsevol detall d’ASLR (Address Space Layout Randomization). Les adreces reals no són fixes ni predictibles entre execucions. Ho veurem més endavant quan parlem de memory allocators.

TEXT

Què hi ha al TEXT?

  • Instruccions
  • Codi executable
  • Funcions
int suma(int a, int b)
{
    return a + b;
}
Reflexió

La funció suma() és una variable?

Definició

Text segment: la zona de l’espai d’adreces on viuen les instruccions executables del programa.

Codi ≠ dades

void foo(void)
{
    int x = 10;
}
Error Comú
  • La funció està al stack. \(\Rightarrow\) Fals.
  • El codi de foo() viu al TEXT i és la seva activació (la crida en curs) la que utilitza el STACK.

DATA

Variables globals

Aquestes variables viuen a la regió DATA de l’espai d’adreces.

int counter = 0;

int main(void)
{
    ...
}
Concepte

Variables globals i static tenen una vida que s’estén durant tota l’execució del procés \(\Rightarrow\) no depenen de cap crida de funció.

Initialized data i BSS

int initialized = 42;
int uninitialized;
int main(void)
{
    ...
}
Concepte

El model DATA que fem servir simplifica diverses seccions que l’executable real diferencia (.data, .bss…). Ens permetrà parlar d’ELF més endavant sense haver de corregir el model.

Activitat 1

On viu cada cosa? (I)

Exercici — Classifica variables i funcions
int global = 10;

static int total = 20;

void foo(void)
{
    int x = 30;
}
Element On?
global ?
total ?
foo() ?
x ?

On viu cada cosa? (II)

Solució — Classifica variables i funcions
int global = 10;

static int total = 20;

void foo(void)
{
    int x = 30;
}
Element Regió
global DATA
total DATA
foo() TEXT
x STACK

STACK

Què és el stack?

\(\text{Stack} = \text{variables automàtiques} + \text{informació de les crides}\)

Definició

Stack: regió de memòria utilitzada per gestionar les crides de funcions i les dades automàtiques associades a aquestes crides.

Una crida crea una activació

void foo(void)
{
    int x = 10;
}

int main(void)
{
    foo();
}
Concepte

Cada crida de funció necessita informació pròpia, per això s’apila un nou marc cada vegada.

Stack frame

Concepte

La disposició exacta d’un stack frame depèn de l’arquitectura i de la convenció de crida (calling convention).

Error Comú

Els arguments no sempre van a la pila. En x86-64 (System V AMD64 ABI), els sis primers arguments enters es passen per registres (rdi, rsi, rdx, rcx, r8, r9), no s’apilen. El detall complet el veurem a la propera sessió.

Lifetime d’una variable automàtica

void foo(void)
{
    int x = 10;
}

foo() → crea frame → x existeix → foo() retorna → frame desapareix → x deixa d’existir

Concepte

El lifetime d’una variable automàtica està lligat a l’activació de la funció, no al programa sencer. Això serà essencial per entendre el heap.

Aquesta funció és segura?

int *create(void)
{
    int x = 42;

    return &x;
}
Reflexió

create() → x = 42 → return &x → frame desapareix

Error Comú

Estem retornant una adreça que correspon a una variable automàtica que ja no té una activació activa. El punter resultant és penjat (dangling).

Concepte

L’adreça sobreviu, la variable no.

HEAP

Memòria dinàmica

int *create(void)
{
    int *p = malloc(sizeof(int));

    *p = 42;

    return p;
}

Definició

Heap: regió per a objectes amb lifetime controlat manualment, o amb una mida no coneguda en temps de compilació.

Concepte

p és una variable local (al STACK). La memòria que apunta és una reserva dinàmica (al HEAP) \(\Rightarrow\) són dues coses diferents.

malloc() / free()

int *p = malloc(sizeof(int));

*p = 42;

/* use p */

free(p);
Exemple

malloc() reserva memòria al HEAP; free() la retorna com a disponible.

Bona Pràctica

Tot malloc() hauria de tenir el seu free() corresponent. Encara no cal saber com funciona per dins (brk, sbrk, bins, fragmentació) \(\Rightarrow\) hi tornarem quan parlem dels memory allocators.

Síntesi

Conclusions

Resum
  1. En el model tradicional, el STACK creix cap a adreces més baixes i el HEAP cap a adreces més altes.
  2. TEXT i DATA no creixen dinàmicament com el STACK o el HEAP.
  3. Les variables globals i static viuen a DATA; les automàtiques al STACK; les reserves dinàmiques al HEAP.
  4. El lifetime d’una variable automàtica està lligat a l’activació de la funció; el lifetime d’una reserva dinàmica està controlat pel programador.
  5. Un punter i allò que apunta poden viure en regions diferents.

Classifica variables i funcions (III)

int global = 10;

void foo(void)
{
    int x = 20;
    int *p = malloc(sizeof(int));

    *p = 30;
}
Exercici

Classifica: foo(), global, x, p, *p

Classifica variables i funcions (IV)

int global = 10;

void foo(void)
{
    int x = 20;
    int *p = malloc(sizeof(int));

    *p = 30;
}
Solució
Element Regió
foo() TEXT
global DATA
x STACK
p STACK
*p HEAP

Annex · Material addicional

Stack frame en detall: x86-64 vs ARM

  • call apila l’adreça de retorn; ret la desapila.
  • Registres callee-saved (rbx, rbp, r12-r15) es guarden si es fan servir.
Error Comú

El retorn sempre s’apila automàticament. Cert en x86 (call/ret), fals en ARM: bl només escriu lr; és la funció qui decideix si cal desar-lo.

  • No hi ha instrucció call/ret equivalent implícita: bl desa l’adreça de retorn al registre lr (x30); cal desar-lo explícitament a la pila si la funció fa una altra crida.
  • 8 registres per a arguments enters, no 6.
Error Comú

El retorn sempre s’apila automàticament. Cert en x86 (call/ret), fals en ARM: bl només escriu lr; és la funció qui decideix si cal desar-lo.