2010-05-26 6 views
10

Ho lavorato con file sparsi di grandi dimensioni su openSUSE 11.2 x86_64. Quando provo a mmap() un file sparse da 1TB, fallisce con ENOMEM. Avrei pensato che lo spazio di indirizzamento a 64 bit sarebbe stato adeguato per mappare in un terabyte, ma non sembra. Sperimentando ulteriormente, un file da 1 GB funziona bene, ma un file da 2 GB (e qualcosa di più grande) fallisce. Immagino che ci possa essere un'ambientazione da modificare, ma una ricerca approfondita non rivela nulla.Perché mmap() non riesce con ENOMEM su un file sparse da 1TB?

Ecco un esempio di codice che mostra il problema: qualche indizio?

#include <errno.h> 
#include <fcntl.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <sys/mman.h> 
#include <sys/types.h> 
#include <unistd.h> 

int main(int argc, char *argv[]) { 
    char * filename = argv[1]; 
    int fd; 
    off_t size = 1UL << 40; // 30 == 1GB, 40 == 1TB 

    fd = open(filename, O_RDWR | O_CREAT | O_TRUNC, 0666); 
    ftruncate(fd, size); 
    printf("Created %ld byte sparse file\n", size); 

    char * buffer = (char *)mmap(NULL, (size_t)size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 
    if (buffer == MAP_FAILED) { 
     perror("mmap"); 
     exit(1); 
    } 
    printf("Done mmap - returned 0x0%lx\n", (unsigned long)buffer); 

    strcpy(buffer, "cafebabe"); 
    printf("Wrote to start\n"); 

    strcpy(buffer + (size - 9), "deadbeef"); 
    printf("Wrote to end\n"); 

    if (munmap(buffer, (size_t)size) < 0) { 
     perror("munmap"); 
     exit(1); 
    } 
    close(fd); 

    return 0; 
} 
+0

Come punto di interesse, il programma funziona per me fino a una dimensione di 256 GB ('1 << 38'), con qualcosa di più elevato che restituisce' EINVAL'. Questo è su RHEL4 (kernel 2.6.9-42.0.3.ELsmp). – caf

+4

Cosa dice ulimit -a? – bmargulies

+0

Grazie, bmargulies: era così. ulimit -a ha riferito la memoria virtuale come 1804800 kbyte (poco più di 1,7 GB). ulimit -v 1610612736 (1.5TB) mi consente di mmap il mio file sparse da 1TB. Risponderò alla mia domanda in modo che possa "chiuderla" ... – metadaddy

risposta

12

Il problema era che il limite di memoria virtuale per processo era impostato su solo 1,7 GB. ulimit -v 1610612736 impostato su 1,5 TB e la mia chiamata mmap() è riuscita. Grazie, bmargulies, per il suggerimento di provare ulimit -a!

+2

E, a quanto pare, posso impostare il mio valore desiderato (che può essere 'illimitato') in/etc/profile per renderlo persistente. – metadaddy

2

C'è una sorta di quota per utente, limitando la quantità di memoria disponibile per un processo utente?

+0

Sì, ho provato il suggerimento di bmargulies di provare ulimit -a e questo ha indicato il limite del processo "memoria virtuale" come il colpevole - vedi la mia risposta qui sotto. .. – metadaddy

1

La mia ipotesi è che il kernel stia avendo difficoltà ad allocare la memoria di cui ha bisogno per tenere il passo con questa mappatura della memoria. Non so come le pagine scambiate siano mantenute nel kernel di Linux (e presumo che la maggior parte del file si trovi nello stato di swap per la maggior parte del tempo), ma potrebbe finire per richiedere una voce per ogni pagina di memoria che il file occupa in una tabella. Poiché questo file potrebbe essere stampato da più di un processo, il kernel deve tenere il passo con il mapping dal punto di vista del processo, che verrebbe mappato su un altro punto di vista, che verrebbe mappato allo storage secondario (includendo i campi per dispositivo e posizione).

Questo si adatterà allo spazio indirizzabile, ma potrebbe non adattarsi (almeno in modo contiguo) alla memoria fisica.

Se qualcuno sa di più su come fa Linux, sarei interessato a sentirlo.

+4

Linux non creerà le PTE (voci della tabella di pagina) finché queste pagine non verranno effettivamente toccate. Tutto ciò che fa quando crei la mappatura è creare una singola struttura VMA (area di memoria virtuale), che contiene fondamentalmente le informazioni da 'mmap()'. – caf

+0

@caf: Grazie per le informazioni – nategoose