2010-01-28 114 views
3

我正在研究一个C语法分析器,并想知道如何管理大量的文本/字符串(> 100MB)来存储内存?预计内容将始终以快速的速度访问。 BG:红帽/ GCC/libc的如何在内存中存储大量的文本数据?

一个字符数组会出界导致段错误... 任何想法或经验受到欢迎分享/讨论...

+1

你需要什么样的数据,以保持在一个单一的100MB +块。很可能存在一种自然适合您需要的用途的数据结构,并且会自动简化存储管理。 – 2010-01-28 00:24:43

回答

3

mmap是处理存储在文件中的大量数据的最佳方法,如果要随机访问该数据。

mmap通知虚拟内存系统映射地址空间的连续部分以包含文件中找到的数据。虚拟内存系统将分配一定范围的地址空间,由该文件支持。当您访问该地址空间中的任何位置时,它将分配一页物理内存,从磁盘读取该文件的该部分,并将该虚拟地址空间部分指向其用于读取文件的物理内存。当需要在物理内存中占用更多空间时,它将写出对磁盘的任何更改(如果适用),并删除该部分虚拟地址空间的映射。

你会使用它,像这样:

#include <sys/types.h> 
#include <sys/stat.h> 
#include <sys/mman.h> /* the header where mmap is defined */ 
#include <fcntl.h> 

int file; 
char *contents; 
struct stat statbuf; 
off_t len; 

file = open("path/to/file", O_RDONLY); 
if (file < 0) 
    exit(1); /* or otherwise handle the error */ 

if (fstat(file, &statbuf) < 0) 
    exit(1); 

len = statbuf.st_size; 

contents = mmap(0, len, PROT_READ, MAP_SHARED, file, 0); 
if (contents == MAP_FAILED) 
    exit(1); 

// Now you can use contents as a pointer to the contents of the file 

// When you're done, unmap and close the file. 

munmap(contents, len); 
close(file); 
+0

我相信mmap应该是我的案例的解决方案,我的情况就像是一个编辑器,需要解析匹配任何时间,例如, vim 通常打开一个大文件并解析回来和第四个。 – nikcname 2010-03-08 10:44:20

9

的mmap(2)文件在VM中,并使用它。

+0

我会对此做一个简短的介绍,谢谢! – nikcname 2010-01-28 01:54:55

+0

请注意,如果有人在mmap上截断了文件,您可能会收到一个SIGBUS。但这仍然是最好的选择。 – 2010-01-28 11:44:16

4

“单个字符数组会出现边界导致分段错误” - 我认为这是不对的。分段错误是由访问受保护的内存引起的,而不是分配太大的块。无论如何,你应该能够在32位机器上分配2-3GB,而在64位上分配更多。

你可以使用char数组,但如果你想快速访问,那么你可能需要某种索引。

你能澄清你的用例吗?你是否想为c语言创建一个解析器?你为什么期望有如此长的投入或产出:无论是来源还是二进制文件通常都很大。

+0

如果你尝试'char foo [100000000];你会看到一个段错误。 foo [0] = 0' - OP并非完全错误。 – Cascabel 2010-01-28 00:39:34

+0

我猜他可能是指“页面错误”? – 2010-01-28 00:47:31

+0

@Jefromi:'char * foo; foo = malloc(10000000); foo [0] = 0;',所以我们在堆上分配,而不是堆栈? – 2010-01-28 16:51:28

0

当您从源流(可能是文本文件)中读取令牌时,可以通过压缩令牌来节省大量空间。读取输入文本时消除多余的空白和注释可以将内存需求减少高达50%。

但我很好奇你为什么需要一次存储这么多内存。字符串文字,标识符和符号表条目可以缓存到磁盘上,当您处于解析的一个点时,它们将无法访问或超出范围。

+0

缓存到磁盘并不是您在具有VM的现代系统上手动执行的操作。 – 2010-01-28 11:51:06

1

这种大数据量的更好,如果该数据将是常数被存储为

  1. 全球阵列。
  2. 在堆(动态分配的内存)中,如果您的情况不允许使用全局变量。

但请注意不要将其存储在堆栈中,否则可能会溢出并导致其他问题。

如果你问有关访问这些数据,可以有效地用于存储特定数据结构/那么我建议:

  1. 哈希表
  2. 阵列
  3. 名单。
+0

@goldenmean:在导致缓冲区溢出之前,如何使用数据溢出堆栈? – Rom 2010-01-28 01:28:58

0

对不起,如果它是一个初学者pbm,分段错误出现以下。

int a = 10000000; char content2[a]; content2[0] = 'a';

的使用情况是,该文件是一个每天结构性纯文本格式生成分析(如XML相似)之前 数据本身是相当静态的, 我希望把它作为访问尽可能快,所以我更喜欢在解析后将它保存在内存中

+0

我可能是错的,但我认为这是由Linux的过度使用内存管理策略造成的。如果你问内存,它会成功,如果你使用内存,那么它被分配。如果内核的内存不足以支持进程,则会终止进程以回收内存。请参阅http://opsmonkey.blogspot.com/2007/01/linux-memory-overcommit.html – 2010-01-28 03:58:27

+0

@Justin:很可能它只是数组大小超过默认分配的堆栈大小。在我的Pentium 4 Linux机器上查看'man ld',默认情况下分配4Mb,虽然可以使用'--stack'选项更改。 – 2010-01-28 11:39:00

+0

我的Linux机器上的最大堆栈大小默认为10 MiB。这就是为什么失败。改用malloc代替。与overcommit无关,overcommit导致OOM杀手启动,它不发送SIGSEGV。 – 2010-01-28 11:46:11

1

如果您在堆栈上分配一个> 100Mb的char数组,您很可能会溢出堆栈。尽管可以使用编译器/链接器选项来增加堆栈大小,但这并不一定能解决问题,因为某些操作系统预计会对堆栈页面进行大致线性访问(谷歌“堆栈保护页面”)

相反,如果您知道大小在编译时,尝试分配一个static char数组。更好的是,使用malloc()。 (您发布的代码声明了一个数组,其大小取决于变量a - 这被称为“可变长度数组”,这是一个不是所有编译器都支持的C99扩展。每个C实现的OTOH允许您调用malloc()来分配内存动态地)。

2

这是一个非常不寻常的C语法分析器,它需要源文本(如果这就是你正在谈论的内容)。大多数解析器一次有效地读取源代码,并立即将其转换为一些内部表示。而且他们通常只将一个源文件(加上#include)的表示形式保存下来,这个文件不太可能像100Mb一样大 - 也许这里有一些设计问题?

+0

这只是存储源文本的好C语言分析器 - 它们是那些给出好的错误消息的语法分析器。 – 2010-01-28 11:49:39

+0

@Dietrich你可以从内部代表产生相当不错的错误信息,虽然公认不是确切的间距。这被称为重新创建。即使在解析过程中需要粘贴原始文件,在解析完成后,您也不需要执行该操作。 – 2010-01-28 12:06:12