Perché è richiesto il segmento .bss?


120

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

Nel programma seguente, aè nel .datasegmento ed bè nel .bsssegmento; è corretto? Gentilmente correggimi se la mia comprensione è sbagliata.

#include <stdio.h>
#include <stdlib.h>

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 <stdio.h>
#include <stdlib.h>
int var[10];  /* Uninitialized so in .bss */
int main ()
{
   var[0] = 20  /* **Initialized, where this 'var' will be ?** */
}

3
Puoi leggere BSS come Better Save Space .
smwikipedia

Risposte:


89

Il motivo è ridurre le dimensioni del programma. Immagina che il tuo programma C venga eseguito su un sistema embedded, dove il codice e tutte le costanti vengono salvate in una vera ROM (memoria flash). In tali sistemi, deve essere eseguito un "copy-down" iniziale per impostare tutti gli oggetti di durata della memorizzazione statica, prima che venga chiamato main (). Tipicamente andrà come questo pseudo:

for(i=0; i<all_explicitly_initialized_objects; i++)
{
  .data[i] = init_value[i];
}

memset(.bss, 
       0, 
       all_implicitly_initialized_objects);

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

Gli eseguibili basati su RAM funzionano in modo simile, anche se ovviamente non hanno una vera ROM.

Inoltre, memset è probabilmente un assemblatore inline molto efficiente, il che significa che il copy-down di avvio può essere eseguito più velocemente.


7
Per chiarire: l'unica differenza tra .data e .bss è che all'avvio il "copy-down" può essere eseguito in sequenza, quindi più veloce. Se non fosse suddiviso nei due segmenti, l'inizializzazione dovrebbe saltare gli spot RAM appartenenti alle variabili non inizializzate, perdendo tempo.
CL22

80

Il .bsssegmento è un'ottimizzazione. L'intero .bsssegmento è descritto da un singolo numero, probabilmente 4 byte o 8 byte, che fornisce la sua dimensione nel processo in esecuzione, mentre la .datasezione è grande quanto la somma delle dimensioni delle variabili inizializzate. Pertanto, .bssrende gli eseguibili più piccoli e più veloci da caricare. In caso contrario, le variabili potrebbero essere nel .datasegmento con inizializzazione esplicita a zero; il programma farebbe fatica a capire la differenza. (In dettaglio, l'indirizzo degli oggetti in .bsssarebbe probabilmente diverso dall'indirizzo se fosse nel .datasegmento.)

Nel primo programma, asarebbe nel .datasegmento e bsarebbe nel .bsssegmento dell'eseguibile. Una volta caricato il programma, la distinzione diventa irrilevante. In fase di esecuzione, boccupa i 20 * sizeof(int)byte.

Nel secondo programma, varviene assegnato lo spazio e l'assegnazione in main()modifica quello spazio. Accade così che lo spazio per sia varstato descritto nel .bsssegmento piuttosto che nel .datasegmento, ma ciò non influisce sul modo in cui il programma si comporta durante l'esecuzione.


16
Ad esempio, si consideri la presenza di molti buffer non inizializzati di 4096 byte di lunghezza. Vorresti che tutti quei buffer 4k contribuissero alla dimensione del binario? Sarebbe un sacco di spazio sprecato.
Jeff Mercado

1
@jonathen killer: Perché l'intero segmento bss è descritto da un unico numero ??
Suraj Jain

@ JonathanLeffler Voglio dire che tutte le variabili statiche inizializzate con zero vanno in bss. Quindi il suo valore non dovrebbe essere solo zero? E anche perché non viene dato spazio nella sezione .data come può farlo rallentare?
Suraj Jain

2
@SurajJain: il numero memorizzato è il numero di byte da riempire con zeri. A meno che non ci siano tali variabili non inizializzate, la lunghezza della sezione bss non sarà zero, anche se tutti i byte nella sezione bss saranno zero una volta caricato il programma.
Jonathan Leffler

1
La sezione .bss nell'eseguibile è semplicemente un numero. La sezione .bss nell'immagine del processo in memoria è normalmente memoria adiacente alla sezione .data e spesso la sezione .data di runtime è combinata con .bss; non viene fatta alcuna distinzione nella memoria di runtime. A volte, puoi trovare dove è iniziato bss ( edata). In termini pratici, il .bss non esiste in memoria una volta completata l'immagine del processo; i dati azzerati sono una parte semplice della sezione .data. Ma i dettagli variano a seconda dell'o / s ecc.
Jonathan Leffler

15

Dal linguaggio Assembly Step-by-Step: Programming with Linux di Jeff Duntemann, per quanto riguarda 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 si definiscono, 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 funzionare. Quando si leggono dati da un file su disco, ad esempio, è necessario disporre di un posto in cui i dati vadano dopo essere entrati dal disco. Buffer di dati come questo 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.

Esiste una differenza fondamentale 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 non lo fanno. Un buffer che occupa 16.000 byte (o più, a volte molto di più) può essere definito in .bss e non aggiunge quasi nulla (circa 50 byte per la descrizione) alla dimensione del file eseguibile.


9

Ebbene, prima di tutto, quelle variabili nel tuo esempio non sono non inizializzate; C specifica che le variabili statiche non altrimenti inizializzate vengono inizializzate a 0.

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

Quando si esegue il programma, il caricatore del programma caricherà .data e .bss in memoria. Scrive in oggetti che risiedono in .data o .bss quindi vanno solo in memoria, non vengono scaricati nel binario su disco in nessun momento.


5

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

.bssQuesta sezione contiene dati non inizializzati che contribuiscono all'immagine della memoria del programma. Per definizione, il sistema inizializza i dati con zero quando il programma inizia a essere eseguito. 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 .data.

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

La SHT_NOBITSdocumentazione relativa al tipo di sezione ripete questa affermazione:

sh_sizeQuesto membro fornisce la dimensione della sezione in byte. A meno che il tipo di sezione non sia SHT_NOBITS, la sezione occupa i sh_size byte nel file. Una sezione di tipo SHT_NOBITSpuò 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 è archiviata la variabile in Linux con objdumpe readelf, e concludere che le variabili globali non inizializzate sono di fatto archiviate nel file .bss. Vedi ad esempio questa risposta: cosa succede a una variabile dichiarata, non inizializzata in C?


3

L'articolo di wikipedia .bss fornisce una bella spiegazione storica, dato che il termine è della metà degli anni '50 (yippee il mio compleanno ;-).

In passato, 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, piuttosto saranno inseriti i valori (tuoi) definiti.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.