2015-08-01 9 views
14

Sto eseguendo un kernel 2.6.27 personalizzato e ho appena notato che i file core prodotti durante un segfault sono più grandi del limite di dimensione del file rigido definito per i processi.ha effetti non deterministici sui processi

E ciò che lo rende più strano è che il file principale è solo a volte troncato (ma non al limite impostato da ulimit).

Per esempio, questo è il programma cercherò e crash di seguito:

int main(int argc, char **argv) 
{ 
    // Get the hard and soft limit from command line 
    struct rlimit new = {atoi(argv[1]), atoi(argv[1])}; 

    // Create some memory so as to beef up the core file size 
    void *p = malloc(10 * 1024 * 1024); 

    if (!p) 
     return 1; 

    if (setrlimit(RLIMIT_CORE, &new)) // Set the hard and soft limit 
     return 2;      // for core files produced by this 
             // process 

    while (1); 

    free(p); 
    return 0; 
} 

Ed ecco l'esecuzione:

Linux# ./a.out 1446462 & ## Set hard and soft limit to ~1.4 MB 
[1] 14802 
Linux# ./a.out 1446462 & 
[2] 14803 
Linux# ./a.out 1446462 & 
[3] 14804 
Linux# ./a.out 1446462 & 
[4] 14807 

Linux# cat /proc/14802/limits | grep core 
Max core file size  1446462    1446462    bytes 

Linux# killall -QUIT a.out 

Linux# ls -l 
total 15708 
-rwxr-xr-x 1 root root  4624 Aug 1 18:28 a.out 
-rw------- 1 root root 12013568 Aug 1 18:39 core.14802   <=== truncated core 
-rw------- 1 root root 12017664 Aug 1 18:39 core.14803 
-rw------- 1 root root 12013568 Aug 1 18:39 core.14804   <=== truncated core 
-rw------- 1 root root 12017664 Aug 1 18:39 core.14807 
[1] Quit     (core dumped) ./a.out 1446462 
[2] Quit     (core dumped) ./a.out 1446462 
[3] Quit     (core dumped) ./a.out 1446462 
[4] Quit     (core dumped) ./a.out 1446462 

Così molteplici cose sono successe qui. Ho impostato il limite rigido per ogni processo di circa 1,4 MB.

  1. I file principali prodotti superano questo limite impostato. Perché?
  2. E 2 dei 4 file core prodotti vengono troncati, ma esattamente da 4096 byte. Cosa sta succedendo qui?

So che il file principale contiene, tra le altre cose, lo stack completo e la memoria heap allocata. Non dovrebbe essere abbastanza costante per un programma così semplice (dare o prendere qualche byte al massimo), quindi produrre un nucleo coerente tra più istanze?

EDITS:

L'uscita richiesta di du

Linux# du core.* 
1428 core.14802 
1428 core.14803 
1428 core.14804 
1428 core.14807 

Linux# du -b core.* 
12013568 core.14802 
12017664 core.14803 
12013568 core.14804 
12017664 core.14807 

Aggiunta memset() dopo malloc() sicuramente regnò cose, dal fatto che il file core sono ora tutti troncati a 1449984 (ancora 3522 byte oltre il limite).

Quindi perché prima erano così grandi i nuclei, cosa contenevano? Qualunque cosa fosse, non era soggetta ai limiti del processo.

Il nuovo programma mostra un comportamento interessante vedere:

Linux# ./a.out 12017664 & 
[1] 26586 
Linux# ./a.out 12017664 & 
[2] 26589 
Linux# ./a.out 12017664 & 
[3] 26590 
Linux# ./a.out 12017663 &  ## 1 byte smaller 
[4] 26653 
Linux# ./a.out 12017663 &  ## 1 byte smaller 
[5] 26666 
Linux# ./a.out 12017663 &  ## 1 byte smaller 
[6] 26667 

Linux# killall -QUIT a.out 

Linux# ls -l 
total .. 
-rwxr-xr-x 1 root root  4742 Aug 1 19:47 a.out 
-rw------- 1 root root 12017664 Aug 1 19:47 core.26586 
-rw------- 1 root root 12017664 Aug 1 19:47 core.26589 
-rw------- 1 root root 12017664 Aug 1 19:47 core.26590 
-rw------- 1 root root 1994752 Aug 1 19:47 core.26653   <== ??? 
-rw------- 1 root root 9875456 Aug 1 19:47 core.26666   <== ??? 
-rw------- 1 root root 9707520 Aug 1 19:47 core.26667   <== ??? 
[1] Quit     (core dumped) ./a.out 12017664 
[2] Quit     (core dumped) ./a.out 12017664 
[3] Quit     (core dumped) ./a.out 12017664 
[4] Quit     (core dumped) ./a.out 12017663 
[5] Quit     (core dumped) ./a.out 12017663 
[6] Quit     (core dumped) ./a.out 12017663 
+0

In primo luogo, cosa significa "du" per il numero effettivo di byte utilizzati dal file principale? Inoltre, 'memset()' la memoria. Semplicemente facendo 'malloc()' non fa in modo che la memoria reale venga mappata nel processo. –

+0

4096 è la dimensione della pagina della memoria virtuale. Quando viene creato il core dump, tutte le pagine VM assegnate al processo verranno scaricate nel file principale. Il file più piccolo è 2933 pagine e il file più grande è 2934 pagine. Quindi è abbastanza coerente. – user3386109

+0

@AndrewHenle L'aggiunta di 'memset()' ha reso sicuramente le cose più coerenti, ma non del tutto. Ho aggiornato la mia domanda con questo e con i risultati del du. – Ram

risposta

7

L'attuazione di un core dumping possono essere trovati in fs/binfmt_elf.c. Seguirò il codice in 3.12 e sopra (è cambiato con commit 9b56d5438) ma la logica è molto simile.

Il codice inizialmente decide quanto scaricare di un VMA (area di memoria virtuale) in vma_dump_size. Per un VMA anonimo come l'heap brk, restituisce l'intera dimensione del VMA. Durante questo passaggio, il limite principale non è coinvolto.

La prima fase di scrittura del core dump scrive quindi un'intestazione PT_LOAD per ciascun VMA. Questo è fondamentalmente un puntatore che dice dove trovare i dati nel resto del file ELF. I dati effettivi sono scritti da un ciclo for ed è in realtà una seconda fase.

Durante la seconda fase, chiama ripetutamente get_dump_page per ottenere un puntatore struct page per ogni pagina dello spazio indirizzo del programma che deve essere scaricato. get_dump_page è una funzione di utilità comune che si trova in mm/gup.c. Il commento di get_dump_page è utile:

* Returns NULL on any kind of failure - a hole must then be inserted into 
* the corefile, to preserve alignment with its headers; and also returns 
* NULL wherever the ZERO_PAGE, or an anonymous pte_none, has been found - 
* allowing a hole to be left in the corefile to save diskspace. 

e infatti elf_core_dump chiama una funzione in fs/coredump.c (dump_seek nel kernel, dump_skip in 3.12+) se get_dump_page rendimenti NULL. Questa funzione chiama lseek a lasciare un buco nel dump (in realtà poiché questo è il kernel chiama file->f_op->llseek direttamente su un puntatore struct file). La differenza principale è che dump_seek non stava effettivamente rispettando l'ulimit, mentre il più recente dump_skip lo fa.

Per quanto riguarda il motivo per cui il secondo programma ha il comportamento strano, è probabilmente a causa di ASLR (randomizzazione dello spazio indirizzo). Quale VMA viene troncato dipende dall'ordine relativo dei VMA, che è casuale. Puoi provare a disabilitarlo con

echo 0 | sudo tee /proc/sys/kernel/randomize_va_space 

e vedere se i risultati sono più omogenei. Per riattivare ASLR, utilizzare

echo 2 | sudo tee /proc/sys/kernel/randomize_va_space 
Problemi correlati