3

我的程序是用 C 语言编写的,我正在使用 gcc 进行编译。我正在读取文件,并将文件的内容存储到缓冲区中。为此,我需要缓冲区与文件一样大。我正在使用 malloc() 为缓冲区分配内存。不幸的是,我遇到了一个 277MB 的文件。这对堆来说太多了吗?我在运行时遇到了段错误,但没有更多信息。它适用于大至 160 MB 的文件,但这个 277MB 文件的单个异常值正在破坏它。

编辑:valgrind 给了我

@0xC0000022L valgrind 给了我

==6380== Warning: set address range perms: large range [0x8851028, 0x190e6102) (undefined)
==6380== Warning: set address range perms: large range [0x8851028, 0x190e6028) (defined)
==6380== Warning: set address range perms: large range [0x190e7028, 0x2997c108) (undefined)
==6380== Warning: set address range perms: large range [0x190e7028, 0x2997c028) (defined)
==6380== Warning: silly arg (-1737565464) to malloc()
==6380== Invalid write of size 4
==6380==    at 0x8048A49: main (newanalyze.c:85)
==6380==  Address 0x4a00 is not stack'd, malloc'd or (recently) free'd
==6380==
==6380==
==6380== Process terminating with default action of signal 11 (SIGSEGV)
==6380==  Access not within mapped region at address 0x4A00
==6380==    at 0x8048A49: main (newanalyze.c:85)

但在第 85 行只是一个不受文件大小影响的小变量。

4

3 回答 3

4

请注意 Valgrind 的输出,

==6380== 警告:愚蠢的 arg (-1737565464) 到 malloc()

-1737565464 是带符号的 int 值,而如果将其视为 unsigned int,则为 2557401832 (>2G)。您将 >2G 参数传递给 malloc 而不是 277M。

从以下信息中,我们知道您正在尝试写入地址 0x4a00,这是一个无效地址,在这种情况下您会期望 SEGV。请检查您的代码中的 newanalyze.c:85 以查看其中的内容。

==6380== 大小为 4 的无效写入

==6380== 在 0x8048A49: 主要 (newanalyze.c:85)

==6380== 地址 0x4a00 没有被堆栈、malloc 或(最近)释放

于 2012-07-12T17:19:14.047 回答
1

跟进其中一条评论,以下是使用mmap(2)打开文件的方法。这是假设您使用的是unix。

int fd;
struct stat S;
const char* file_base;

if ((fd = open(filename, O_RDONLY)) < 0) {
    fprintf(stderr, "Can't open file %s to read\n", filename);
    return NULL;
}
if (fstat(fd, &S) != 0) {
    fprintf(stderr, "Can't stat file %s!\n", filename);
    close(fd);
    return NULL;
}
if ((file_base = mmap(NULL, S.st_size, PROT_READ, MAP_FILE|MAP_PRIVATE, fd, 0)) == MAP_FAILED) {
    fprintf(stderr, "Unable to map file %s\n", filename);
    close(fd);
    return NULL;
}

在此之后,file_base指向包含文件全部内容的内存位。

这种方式的优点是:

  • 它基本上不占用内存(有点)。这里实际发生的事情是您正在使文件成为交换空间以获得一点内存。
  • 它不需要时间(有点)。新识别的内存位开始换出,因此当访问内存时,所要做的就是换入该位文件,这与使用malloc动态分配的速度一样快(3)

这具有标记为只读的内存位;您也可以对文件进行读写操作,但这意味着如果您写入内存,您将同时更改文件。

如果您不在 unix 上,则可能仍有可用的mmap功能。如果没有,将会有一些 Windows 原生的方式来做同样的事情 ( CreateFileMapping)。

于 2012-07-12T14:35:20.460 回答
0

不幸的是,我不能给你一个可靠的“为什么”,但是 mmap2,这似乎是 malloc 在你的系统上调用的,只是报告它内存不足。在这种情况下,Malloc 将返回 NULL 导致段错误。

munmap(0xb7706000, 4096)                = 0
mmap2(NULL, 2557403136, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = -1  ENOMEM (Cannot allocate memory)

作为一个反例,我有一个成功的玩具程序:

mmap(NULL, 283652096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f2d00994000

我会检查系统上可用的内存或程序正在使用的内存。也许它严重泄漏内存?

于 2012-07-12T14:28:31.807 回答