Perché è necessario il segmento .bss?

Quello che so è che le variabili globali e statiche sono memorizzate nel segmento .data e che i dati non inizializzati si trovano nel segmento .bss . Quello che non capisco è perché abbiamo un segmento dedicato per le variabili non inizializzate? Se una variabile non inizializzata ha un valore assegnato in fase di esecuzione, la variabile esiste ancora solo nel segmento .bss ?

Nel seguente programma, a è nel segmento .data e b è nel segmento .bss ; è corretto? Gentilmente correggimi se la mia comprensione è sbagliata.

 #include  #include  int a[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9}; int b[20]; /* Uninitialized, so in the .bss and will not occupy space for 20 * sizeof (int) */ int main () { ; } 

Inoltre, considera il seguente programma,

 #include  #include  int var[10]; /* Uninitialized so in .bss */ int main () { var[0] = 20 /* **Initialized, where this 'var' will be ?** */ } 

Il motivo è ridurre le dimensioni del programma. Immagina che il tuo programma C funzioni su un sistema embedded, dove il codice e tutte le costanti sono salvati nella vera ROM (memoria flash). In tali sistemi, è necessario eseguire un “copia-down” iniziale per impostare tutti gli oggetti di durata di archiviazione statici, prima che venga chiamato main (). Tipicamente andrà come questo pseudo:

 for(i=0; i 

Dove .data e .bss sono memorizzati nella RAM, ma init_value è memorizzato nella ROM. Se fosse stato un segmento, la ROM doveva essere riempita con un sacco di zeri, aumentando significativamente la dimensione della ROM.

Gli eseguibili basati su RAM funzionano in modo simile, sebbene ovviamente non abbiano una vera ROM.

Inoltre, memset è probabilmente un assemblatore inline molto efficiente, il che significa che la copia di avvio può essere eseguita più velocemente.

Il segmento .bss è un’ottimizzazione. L’intero segmento .bss è descritto da un singolo numero, probabilmente 4 byte o 8 byte, che fornisce le sue dimensioni nel processo in esecuzione, mentre la sezione .data è grande quanto la sum delle dimensioni delle variabili inizializzate. Pertanto, il .bss rende gli eseguibili più piccoli e più veloci da caricare. Altrimenti, le variabili potrebbero essere nel segmento .data con inizializzazione esplicita a zero; il programma sarebbe difficile da dire la differenza. (In dettaglio, l’indirizzo degli oggetti in .bss sarebbe probabilmente diverso dall’indirizzo se fosse nel segmento .data .)

Nel primo programma, a sarebbe nel segmento .data e b sarebbe nel segmento .bss dell’eseguibile. Una volta caricato il programma, la distinzione diventa irrilevante. In fase di esecuzione, b occupa 20 * sizeof(int) byte.

Nel secondo programma, var viene allocato spazio e l’assegnazione in main() modifica quello spazio. Accade che lo spazio per var stato descritto nel segmento .bss piuttosto che nel segmento .data , ma ciò non influisce sul modo in cui il programma si comporta durante l’esecuzione.

Bene, prima di tutto, quelle variabili nel tuo esempio non sono non inizializzate; C specifica che le variabili statiche non diversamente inizializzate vengono inizializzate su 0.

Quindi il motivo di .bss è di avere file eseguibili più piccoli, risparmiando spazio e consentendo un caricamento più veloce del programma, poiché il caricatore può semplicemente allocare una serie di zeri invece di dover copiare i dati dal disco.

Durante l’esecuzione del programma, il programma di caricamento caricherà .data e .bss in memoria. Scrive in oggetti che risiedono in .data o .bss quindi vanno solo in memoria, non vengono scaricati sul binario su disco in nessun punto.

Dal linguaggio Assembly: Programmazione con Linux di Jeff Duntemann, riguardante la sezione .data :

La sezione .data contiene le definizioni dei dati degli elementi di dati inizializzati. I dati inizializzati sono dati che hanno un valore prima che il programma inizi a essere eseguito. Questi valori fanno parte del file eseguibile. Vengono caricati in memoria quando il file eseguibile viene caricato in memoria per l’esecuzione.

La cosa importante da ricordare sulla sezione .data è che più elementi di dati inizializzati vengono definiti, più grande sarà il file eseguibile e più tempo ci vorrà per caricarlo dal disco in memoria quando lo si esegue.

e la sezione .bss :

Non tutti gli elementi di dati devono avere valori prima che il programma inizi a essere eseguito. Ad esempio, quando stai leggendo i dati da un file su disco, devi disporre di un posto dove i dati possano andare dopo che è arrivato dal disco. I buffer di dati come quelli sono definiti nella sezione .bss del tuo programma. Metti da parte un certo numero di byte per un buffer e dai un nome al buffer, ma non dici quali valori devono essere presenti nel buffer.

C’è una differenza cruciale tra gli elementi di dati definiti nella sezione .data e gli elementi di dati definiti nella sezione .bss: gli elementi di dati nella sezione .data si aggiungono alla dimensione del file eseguibile. Gli elementi di dati nella sezione .bss no. Un buffer che occupa 16.000 byte (o più, a volte molto di più) può essere definito in .bss e non aggiungere quasi nulla (circa 50 byte per la descrizione) alla dimensione del file eseguibile.

L’articolo di wikipedia. Bss fornisce una bella spiegazione storica, dato che il termine è dalla metà degli anni ’50 (il mio compleanno è lippee ;-).

Nel corso della giornata, ogni bit era prezioso, quindi qualsiasi metodo per segnalare lo spazio vuoto riservato era utile. Questo ( .bss ) è quello che si è bloccato.

Le sezioni .data sono per lo spazio che non è vuoto, ma che avrà (i) valori definiti inseriti in esso.

Il System V ABI 4.1 (1997) (specifica ELA AKA) contiene anche la risposta:

.bss Questa sezione contiene dati non inizializzati che contribuiscono all’immagine di memoria del programma. Per definizione, il sistema inizializza i dati con zero all’avvio del programma. La sezione non occupa spazio file, come indicato dal tipo di sezione, SHT_NOBITS .

dice che il nome della sezione .bss è riservato e ha effetti speciali, in particolare non occupa spazio per i file , quindi il vantaggio rispetto ai .data .

Lo svantaggio è ovviamente che tutti i byte devono essere impostati su 0 quando il sistema operativo li mette in memoria, che è più restrittivo, ma un caso d’uso comune, e funziona bene per le variabili non inizializzate.

La documentazione del tipo di sezione SHT_NOBITS ripete tale affermazione:

sh_size Questo membro fornisce la dimensione della sezione in byte. A meno che il tipo di sezione non sia SHT_NOBITS , la sezione occupa i byte sh_size nel file. Una sezione di tipo SHT_NOBITS può avere una dimensione diversa da zero, ma non occupa spazio nel file.

Lo standard C non dice nulla sulle sezioni, ma possiamo facilmente verificare dove la variabile è archiviata in Linux con objdump e readelf , e concludere che globali non inizializzate sono di fatto archiviate in .bss , vedi ad esempio questa risposta: https: // stackoverflow .com / a / 36725211/895245