2016-04-12 17 views
4

ich eine große Datei lese pread wie folgt verwendet:pread für sehr große Dateien

ssize_t s = pread(fd, buff, count, offset); 
if (s != (ssize_t) count) 
    fprintf(stderr, "s = %ld != count = %ld\n", s, count); 
assert(s == (ssize_t) count); 

Der obige Code ist für kleine Dateien (bis zu 1,5 GB) fein gearbeitet. Bei großen Dateigrößen weicht die zurückgegebene Anzahl der Bytes jedoch von der erwarteten Anzahl ab.

Insbesondere für 2,4 GB Dateigröße, meine count wird auf 2520133890 und die Behauptung nicht mit den fprintf sagen:

s = 2147479552 != count = 2520133890

Was diese rätselhaft macht, ist, dass ich auf einem 64- arbeiten Bit-System und daher sizeof(ssize_t) = 8.

Was ist die Ursache für diesen Fehler und wie behebe ich dies, damit ich die gesamte Datei auf einmal lesen kann?

+0

Was ist mit "Offset"? Sind Sie sicher, dass es Null enthält? –

+1

Welche Art von 'count' und' offset'? Verwenden Sie die richtige Version von 'pread' aus' libc', zum Beispiel unter Linux gibt es 'pread' und' pread64', und Sie müssen sicher sein, dass Sie 'pread64' Funktion von' glibc' verwenden? – fghj

+0

@GiuseppeGuerrini Offset ist immer auf 0 gesetzt. –

Antwort

4

Sieht aus wie Sie Linux verwenden, und magische Zahl Rückkehr von pread ist 2147479552 = 0x7ffff000, so lautet die Antwort in man 2 read:

Unter Linux read() (und ähnliche Systemaufrufe) übertragen bei die meisten 0x7ffff000 (2.147.479.552) Bytes und geben die Anzahl der tatsächlich übertragenen Byte zurück. (Dies trifft sowohl auf 32-Bit- und 64-Bit- Systemen.)

Sie müssen also mindestens zweimal pread rufen Sie Ihre Daten zu erhalten, diese Einschränkung nicht im Zusammenhang mit _FILE_OFFSET_BITS=64, O_LARGEFILE, sizeof(off_t) und etc Dinge, diese Einschränkung wird durch rw_verify_area in linux-Kernel erstellen:

/* 
* rw_verify_area doesn't like huge counts. We limit 
* them to something that fits in "int" so that others 
* won't have to do range checks all the time. 
*/ 
int rw_verify_area(int read_write, struct file *file, const loff_t *ppos, size_t count) 
... 
return count > MAX_RW_COUNT ? MAX_RW_COUNT : count; 

#define MAX_RW_COUNT (INT_MAX & PAGE_CACHE_MASK)

+0

Wow, lerne jeden Tag etwas Neues. FWIW, auf meinem System enthält die man-Seite von read (2) diesen Text nicht, z. http://man7.org/linux/man-pages/man2/read.2.html tut es. – janneb

+1

Das Update in Man-Seite wurde eingeführt ~ Februar 2016, neulich genug, um über Linux-Distributionen zu verbreiten (https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=630029) – fghj

+0

Kurzes Lesen kann trotzdem passieren. Außerdem würde das E/A-Subsystem die großen Anforderungen aufteilen. Typische Empfehlungen, die ich kürzlich gehört habe, waren maximal 8-16MB pro I/O. – Dummy00001

1

Aus Ihrer Beschreibung geht hervor, dass Sie einen 32-Bit-Build erstellen und die Large File Support (LFS) nicht aktiviert haben. Dazu müssen Sie das Makro _FILE_OFFSET_BITS auf den Wert 64 setzen.

Also, überprüfen Sie bitte, dass Sie wirklich eine 64-Bit-Build wie Sie sagen .. EDIT: Ok, ich glaube dir verwenden tatsächlich ein 64-Bit-System.

Ich denke, die richtige Ursache für Ihr Problem, wie in der Antwort https://stackoverflow.com/a/36568630/75652 hingewiesen, wird in der gelesen (2) man-Seite erläutert: http://man7.org/linux/man-pages/man2/read.2.html. Um dies zu umgehen, müssen Sie Code wie


    bytes_left = count; 
    while (bytes_left > 0) 
    { 
     trans = pread (fd, buff, bytes_left, offset); 
     if (trans == -1) 
     { 
      if (errno == EINTR) 
      continue; 
      else 
      return trans; 
     } 
     buf += trans; 
     bytes_left -= trans; 
     offset += trans; 
    } 

    return count - bytes_left; 
+0

Beim laufen 'ldd', es sieht aus wie ein 64-Bit-Build: ' $ ldd Programm \t linux-vdso.so.1 => (0x00007fff4e18f000) \t libboost_system.so.1.54. 0 => /usr/lib/x86_64-linux-gnu/libboost_system.so.1.54.0 (0x00007fe57440b000) \t libboost_filesystem.so.1.54 .0 => /usr/lib/x86_64-linux-gnu/libboost_filesystem.so.1.54.0 (0x00007fe5741f5000) \t libstdC++. So.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007fe573ef0000) \t libgomp.so.1 => /usr/lib/x86_64-linux-gnu/libgomp.so.1 (0x00007fe573ce1000) ' –

+0

' libgcc_s.so.1 =>/lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007fe573acb000) \t libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007fe5738ac000) \t libc.so.6 =>/lib/x86_64-linux- GNU/libc.so.6 (0x00007fe5734e7000) \t libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007fe5731e1000) \t /lib64/ld-linux-x86-64.so .2 (0x00005562e9fca000) ' –