2010-07-20 42 views
0

好吧,我一直在读fread()[它返回一个类型size_t],并看到有关大型文件和其他一些问题的一些问题 - 但我仍然有一些问题。这个函数传入一个文件指针和一个long long int。该lld是从我使用另一个函数获取实际文件大小为6448619520字节的主要位置。fread()在6GB文件失败

char *getBuffer(FILE *fptr, long long size) { 
    char *bfr; 
    size_t result; 

    printf("size of file in allocate buffer: %lld\n", size); 
     //size here is 6448619520 


    bfr = (char*) malloc(sizeof(char) * size); 
    if (bfr == NULL) { 
     printf("Error, malloc failed..\n"); 
     exit(EXIT_FAILURE); 
    } 
     //positions fptr to offset location which is 0 here. 
    fseek(fptr, 0, SEEK_SET); 
     //read the entire input file into bfr 
    result = fread(bfr, sizeof(char), size, fptr); 
    printf("result = %lld\n", (long long) result); 


    if(result != size) 
    { 
     printf("File failed to read\n"); 
     exit(5); 
    } 
    return (bfr); 

} 

我已经测试过它的面积约为1-2GB的文件,它工作正常,但是,当我测试它6GB的文件中,没有到缓冲区中读取。忽略其他结果(注重结果的粗体),问题在于读取数据bfr。这里是我得到的一些结果。一个文件的

第一即735844352个字节(700 + MB)

根@红盒子:/数据/项目/ C /存根/#./testrun -x 45004E00 -i /数据/ Helix2008R1 .ISO

图像文件是/data/Helix2008R1.iso
十六进制字符串= 45004E00
>文件的总大小:735844352
在GET缓冲文件尺寸:735844352
结果= 735844352

** 开始解析所述命令行十六进制值:45004E00
在十六进制字符串的总字节数:
十六进制字符串45004E00被发现在字节位置:4个

十六进制字符串搜索结果:37441
十六进制字符串45004E00被发现在字节位置:524768
....

试验#2针对一个6GB的文件: 根@redbox:/数据/项目/ C /存根/#./testrun -x BF1B0650 -i /data/images/sixgbimage.img

图片文件/data/images/sixgbimage.img
十六进制字符串= BF1B0650
文件的总大小:在文件6448619520
大小分配缓冲区:6448619520
结果= 0
文件无法读取

我仍然不知道为什么它是大文件,而不是失败小一些,这是一个> 4GB的问题。我使用以下内容:

/* Support Large File Use */ 
#define _LARGEFILE_SOURCE 1 
#define _LARGEFILE64_SOURCE 1 
#define _FILE_OFFSET_BITS 64 

顺便说一句,我使用的是Ubuntu 9.10盒(2.6.x内核)。 TIA。

+1

它是一个32位函数吗? 32位,你只能访问4 GB的字节寻址内存。如果您无法访问超过4GB的任何内容,我怀疑这至少是部分责任。 – 2010-07-20 00:23:08

+0

是的,它是一个32位函数。我猜测这可能是一个> 4GB的问题,仍然在研究如何解决这个问题.. – labgeek 2010-07-20 00:54:36

+1

我正在阅读这个权利吗?你试图一次性读取6GB的文件到RAM中?即使你认为自己有足够的记忆,也不要这样做。像sarnold建议使用mmap。 – 2010-07-20 01:02:26

回答

0

在收到所有人的建议之后,我将6GB文件分解为4K块,分析了十六进制字节,并且能够获得什么字节位置,稍后我将从VMFS分区中取出MBR被dd镜像了。这里是一个阅读它每块的快速和肮脏的方式:

的#define DEFAULT_BLOCKSIZE 4096
...

while((bytes_read = fread(chunk, sizeof(unsigned char), sizeof(chunk), fptr)) > 0) { 
    chunkptr = chunk; 
    for(z = 0; z < bytes_read; z++) { 
     if (*chunkptr == pattern_buffer[current_search]) { 
      current_search++; 
      if (current_search > (counter - 1)) { 
       current_search = 0; 
       printf("Hex string %s was found at starting byte location: %lld\n", 
         hexstring, (long long int) (offsetctr-1)); 
       matches++; 
      } 
     } else { 
      current_search = 0; 
     } 
     chunkptr++; 
     //printf("[%lld]: %02X\n", offsetctr, chunk[z] & 0xff); 
     offsetctr++; 
    } 
    master_counter += bytes_read; 
} 

...

这里是我得到的结果。 ..

[email protected]:~/workspace/bytelocator/Debug# ./bytelocator -x BF1B0650 -i /data/images/sixgbimage.img 

Total size of /data/images/sixgbimage.img file: 6448619520 bytes 
Parsing the hex string now: BF1B0650 

Hex string BF1B0650 was found at starting byte location: 18 
Hex string BF1B0650 was found at starting byte location: 193885738 
Hex string BF1B0650 was found at starting byte location: 194514442 
Hex string BF1B0650 was found at starting byte location: 525033370 
Hex string BF1B0650 was found at starting byte location: 1696715251 
Hex string BF1B0650 was found at starting byte location: 1774337550 
Hex string BF1B0650 was found at starting byte location: 2758859834 
Hex string BF1B0650 was found at starting byte location: 3484416018 
Hex string BF1B0650 was found at starting byte location: 3909721614 
Hex string BF1B0650 was found at starting byte location: 3999533674 
Hex string BF1B0650 was found at starting byte location: 4018701866 
Hex string BF1B0650 was found at starting byte location: 4077977098 
Hex string BF1B0650 was found at starting byte location: 4098838010 


Quick stats: 
================ 
Number of bytes that have been read: 6448619520 
Number of signature matches found: 13 
Total number of bytes in hex string: 4 
+0

我觉得你的新计划将错过跨越两个区块的字节串的副本:说,'BF1B'块0,'0650'块1 – sarnold 2010-07-23 00:53:41

+0

是的,你是正确的。这是目前的限制之一,十六进制块之间的十六进制模式搜索仅搜索那些4k块内的那些模式,而不是跨越两个连续块的模式 - 这是必须解决的! – labgeek 2010-07-23 10:40:02

2

fread失败时,它会设置errno来指示失败的原因。拨打fread返回零后errno的值是多少?

更新: 您是否需要一举读取整个文件?如果您一次读入512MB文件,会发生什么情况?

根据以上评论,您正在使用32位操作系统。在这种情况下,您将无法一次处理6 GB(其中之一,size_t将无法​​保存大量的数字)。但是,您应该能够以较小的块读入和处理文件。

我会争辩说,即使在64位操作系统上,将6GB文件读入内存也可能不是解决问题的最佳解决方案。你试图完成什么,需要你缓冲一个6GB的文件?可能有更好的方法来解决这个问题。

+0

的fread()返回0之后,它返回的错误值是:22个 结果: 文件的总大小:在GET缓冲6448619520 尺寸文件的:6448619520 结果= 0 错误号= 22 文件未能读取 – labgeek 2010-07-20 00:39:12

+0

一次读入一个行业就可以。它只是我已经习惯了使用小测试文件,当我在更大的文件上测试它时,我没有考虑到我不会提供的内存...... ty。 – labgeek 2010-07-20 01:25:16

+0

你的问题是一个很好的问题,我应该尽早意识到,阅读一些大的内容将是不可行和低效的。所以是的,一个小的重新设计是为了将读取分成小块,处理每个块,直到文件结束。此程序的总体原因是在dd映像或VMFS分区内获取十六进制偏移量模式。一些VMFS分区往往会变得非常大,例如200 + GB或更大。再次,不是最好的方式来处理它,我现在看到了。 – labgeek 2010-07-20 01:49:47

4

如果你只是要通读文件,而不是修改它,我建议使用mmap(2)而不是fread(3)。这应该更有效率,虽然我还没有尝试过大文件。如果这是你想要的,你需要改变我非常简单的找到/未找到的报告偏移量,但我不确定你想要的指针。 :)

#define _GNU_SOURCE 
#include <string.h> 

#include <fcntl.h> 
#include <sys/mman.h> 
#include <stdio.h> 
#include <sys/types.h> 
#include <sys/stat.h> 
#include <unistd.h> 


int main(int argc, char* argv[]) { 
    char *base, *found; 
    off_t len; 
    struct stat sb; 
    int ret; 
    int fd; 
    unsigned int needle = 0x45004E00; 

    ret = stat(argv[1], &sb); 
    if (ret) { 
      perror("stat"); 
      return 1; 
    } 

    len = sb.st_size; 

    fd = open(argv[1], O_RDONLY); 
    if (fd < 0) { 
      perror("open"); 
      return 1; 
    } 

    base = mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0); 
    if (!base) { 
      perror("mmap"); 
      return 1; 
    } 

    found = memmem(base, len, &needle, sizeof(unsigned int)); 
    if (found) 
      printf("Found %X at %p\n", needle, found); 
    else 
      printf("Not found"); 
    return 0; 
} 

一些测试:

$ ./mmap ./mmap 
Found 45004E00 at 0x7f8c4c13a6c0 
$ ./mmap /etc/passwd 
Not found 
+0

我一直使用malloc之前,从未使用mmap之前...在这里看到了一个很好的解释:http://stackoverflow.com/questions/1739296/malloc-vs-mmap-in-c – labgeek 2010-07-20 01:06:49

+0

在我的观点使用'fread'进入如此大的缓冲区是一种滥用。 即使你想读你应该使用'mmap'后更改缓冲:在国旗给了''MAP_PRIVATE' mmap'确保您的副本保留在内存中的所有变化,并没有被重新写入文件。 'mmap'效率更高,因为只要不修改它们就不需要换出任何'bfr'页面。所有页面都直接在页面缓存中处理。如果您真的不想更改内容,请将其映射为只读,然后即使您的程序的多个实例可以使用相同的物理页面共存。 – 2010-07-20 06:28:34

4

如果这是一个32位的过程,就像你说的,然后size_t是32位的,你根本可以在进程的地址大于4GB不储存更多空间(实际上,实际上,少于3GB)。在这条线的位置:

bfr = (char*) malloc(sizeof(char) * size); 

相乘的结果将模SIZE_MAX + 1,这意味着它只会尽量和周围2GB分配减少。类似的,同样的事情发生在size参数在这条线:

result = fread(bfr, sizeof(char), size, fptr); 

如果你想在32位进程具有较大的文件工作,你只有其中的一部分在时间上工作(例如,读取前100 MB,读取下100 MB的过程,...)。您无法一次读取整个文件 - 您的流程没有足够的内存来执行此操作。

+0

好点,这是我最感兴趣的。看起来我必须读取每个文件读取x MB直到达到文件大小的末尾。 – labgeek 2010-07-20 01:20:56

0

您是否已验证mallocfread实际上是否采用了正确的参数类型?您可能需要使用-Wall选项进行编译,并检查您的64位值是否真的被截断。在这种情况下,malloc将不会报告错误,但最终的分配将远远小于您所要求的。