2012-03-02 26 views
9

Così, lo faccio:Dimensione massima dello stack, ulimit -s, segfault 11 - come funziona?

$ ulimit -s 
8192 

Grande. A quanto mi risulta, il segmento di stack di qualsiasi processo non può superare 8192 kilobyte.

Ora, per contestare tale ..

#include <stdio.h> 

void over_8k(void) { 
    char buf[1024*1024*20]; 
} 

int main(int argc, char** argv) { 
    printf("Starting .. "); 
    over_8k(); 
    printf(" finishing.\nHow did this work?\n"); 
    return 0; 
} 

compilato. Corse. Nessun problema. Beh, questo non è giusto? over_8k da solo dovrebbe avere uno stack frame di, beh, oltre 20 megabyte. Bene, proviamo l'accesso a quei 20 milioni di byte:

#include <stdio.h> 
#include <string.h> 

void over_8k(void) { 
    char buf[1024*1024*20]; 
    memset(buf, 'A', sizeof(buf)); 
} 

int main(int argc, char** argv) { 
    printf("Starting .. "); 
    over_8k(); 
    printf(" finishing.\nHow did this work?\n"); 
    return 0; 
} 

.. rullo di tamburi ..

Segmentation fault: 11 

Grande. Ma non è questo l'errore che mi aspetterei? Accesso alla memoria non valido?

Perché genera un segfault e non esegue l'errore prima? Su chiamata al over_8k forse? Come funziona? Voglio sapere tutto.

+6

La mia ipotesi: l'allocazione dello stack è spesso solo un incremento/decremento del puntatore dello stack. Quello stesso non segfault. È solo quando si tenta di accedere ai dati che entra nella memoria non mappata e si blocca. – Mysticial

+1

@Mysticial: dannatamente giusto. È facile vedere quando il compilatore fornisce il codice assembly. – maverik

+0

Che compilatore e bandiere stai usando per compilare e testare il codice sopra? – Matt

risposta

9

Ampliando il mio commento ...

Ci sono due possibilità mi viene in mente:

Il compilatore sta ottimizzando l'intero buf matrice:

In MSVC, con le ottimizzazioni attivate, l'intero array viene completamente ottimizzato e non viene assegnato affatto. Quindi non sta usando nessuna pila.

allocazione Stack è solo un incremento/decremento per lo stack pointer:

sub rsp, 20971520 

non si segmentation fault. È solo un puntatore. Sarà segfault solo quando si tenta di accedervi nella memoria non mappata.

+1

+1 per l'ipotesi n. 1: "Il compilatore sta ottimizzando l'intero array buf" – ArjunShankar

+0

Indovina n. 1 Non spiega però l'errore di segmentazione nella seconda versione del codice, poiché viene utilizzato 'buf', quindi shouldn ' essere ottimizzato, OPPURE dal momento che 'buf' non viene letto, sia l'allocazione che l'istruzione' memset' dovrebbero essere ottimizzate, quindi non ci dovrebbero essere errori. Ovviamente guardando l'assemblea lo verificheremmo rapidamente. – Matt

+0

Ho eseguito il test in MSVC e l'assieme mostra che è ancora in fase di ottimizzazione. Apparentemente è in grado di riconoscere l'intero 'memset()' come codice morto e quindi di eliminare tutto. Forse GCC è diverso, o il livello di ottimizzazione predefinito è troppo alto. – Mysticial

6

La dichiarazione dell'array buf non implica altro che l'incremento del puntatore dello stack.

L'incremento dello stesso puntatore dello stack oltre il limite dell'area dello stack non provoca l'arresto anomalo del programma: è solo un registro che ha un valore maggiore. Riferendosi alla memoria oltre quell'area, tuttavia, il programma si arresterà sicuramente.

+0

Consiglierei di apportare le seguenti sostituzioni: incremento -> decremento, oltre il limite -> esterno, più grande -> più piccolo, (l'ultimo) oltre -> sotto/fuori. –

+0

Vero, lo stack cresce verso il basso sulla maggior parte delle piattaforme, ma il principio è lo stesso. –

2

Se si utilizza GCC, si potrebbe voler utilizzare il -fstack-check, è possibile vedere alcune delle opzioni per esso su this page.

2

Perché genera un segfault?

Il programma accede alla posizione di memoria non allocata dal sistema operativo. La posizione di memoria non appartiene all'heap né appartiene allo stack. Il messaggio di errore non può essere più dettagliato di Segmentation fault: 11 perché la posizione di memoria non appartiene a nulla.

Perché non errore prima? Perché non risponde per errore a over_8k?

C è un linguaggio non sicuro con enfasi sulle prestazioni del programma. Il controllo della pila all'inizio di ogni funzione rallenterebbe tutti i programmi C.

Come funziona?

In C, tutte le variabili (non inizializzate in modo esplicito) non sono inizializzate al momento della creazione. La seguente riga di codice:

char buf[1024*1024*20]; 

assegna buf in pila, ma non tocca la memoria allocata.

3

Lo stack sulla maggior parte dei sistemi operativi è allocato su richiesta.

Ciò significa che lo stack non viene creato in anticipo sulla sua dimensione completa, ma viene automaticamente esteso la prima volta che si tocca ciascuna pagina oltre la fine corrente dello stack.

Quindi in questo caso, l'accesso causato da memset() tenta di allocare 20 M di stack e questo non riesce a causa del limite di dimensioni dello stack. Quando si verifica un errore nell'allocazione della memoria per soddisfare un errore di pagina, non ci sono molte opzioni per la segnalazione degli errori - tutto ciò che può essere fatto su un sistema simile a UNIX è inviare un segnale al processo. Nel tuo caso, questo è SIGSEGV.

Problemi correlati