2010-01-28 108 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