2014-04-01 26 views
2

我正嘗試使用MAP_SHARED創建一個內存映射文件。當文件大小達到2GB時遇到問題。下面粘貼的代碼是我正在使用的(作爲測試)。mmap總線錯誤通過2Gb寫入MAP_SHARED文件

#include <stdio.h> 
#include <stdlib.h> 
#include <stdint.h> 
#include <sys/mman.h> 
#include <unistd.h> 
#include <fcntl.h> 

#define MY_FILE "/dev/shm/mmap_test" 
#define BLOCK_SIZE (1024*1024) 
#define NUM_FILES 1 

void mk_file(int f_num) 
{ 
    uint64_t len = 0; 
    int fd, j, k; 
    char tmp_file[1024], *x, *rr_addr; 

    // Open file in /dev/shm 
    sprintf(tmp_file, "%s%d", MY_FILE, f_num); 
    fd = open(tmp_file, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); 

    if (fd == -1) 
    { 
     perror("file open"); 
     exit(-1); 
    } 

    // 16Gb file 
    len = 16UL * 1024 * 1024 * 1024; 
    printf("len: %ld Gb\n", len/(1024*1024*1024)); 

    printf("Mapping %ld blocks\n", len/BLOCK_SIZE); 

    for (j = 0; j < len/BLOCK_SIZE; j++) { 
     // Increase the file size 
     ftruncate(fd, ((j + 1) * BLOCK_SIZE)); 

     // Just mmap memory... don't have file backing 
     //rr_addr = mmap(NULL, BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE|MAP_NORESERVE, -1, 0); 

     // MMAP a region to the file at a given offset 
     rr_addr = mmap(NULL, BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_NORESERVE, fd, (j * BLOCK_SIZE)); 

     if (rr_addr == MAP_FAILED) { 
      perror("mmap error"); 
      printf("INDEX: %d\n", j); 
      exit(-1); 
     } 

     // Write to every byte of allocated memory 
     x = (char *) rr_addr; 

     for (k = 0; k < BLOCK_SIZE; k++) 
     { 
      *x = '1'; 
      x++; 
     } 
    } 

    return; 
} 

int main(int argc, char **argv) 
{ 
    uint64_t i; 

    for (i = 0; i < NUM_FILES; i++) 
     mk_file(i); 

    return 0; 
} 

在上面的代碼中,當文件中的偏移量達到2GB時,出現總線錯誤。這些是我已經嘗試過的東西:

  1. 如果我將NUM_FILES更改爲16和len到1GB,我沒有任何問題。
  2. 如果我刪除寫入內存(僅限mmap)的for循環,程序不會崩潰(即使len大於2gb)b/c linux內核實際上不會將頁面映射到文件,直到您讀取/寫入mmap'ed區域。
  3. 如果我將mmap調用從MAP_SHARED更改爲MAP_ANON(取消註釋第一個mmap調用並註釋掉第二個調用並且不鏈接到文件),則不存在問題(甚至寫入成功)。
  4. /dev/shm(30gb)上有足夠的空間,我在這裏只用了16gb。
  5. 我不必寫入每個分配的字節。我只需要寫入最後一個mmap'ed區域(將內部for循環移到外部),如果偏移+ BLOCK_SIZE> = 2gb,則會發生總線錯誤。
  6. 我在Ubuntu 13.10和CentOS 6.4上試過這個,兩者都有同樣的問題。

我在想這是否是linux內核中的問題?有沒有人嘗試使用大於2GB的MAP_SHARED對單個文件進行mmap處理併成功使用(讀取/寫入)它?

+3

我假設你在64位系統上? –

+0

是的,我是。正如在下面選定的答案中提到的那樣,問題在於'ftruncate'的文件長度已經溢出,並且沒有擴展文件。將'j'的定義更改爲'int'的'uint64_t'解決了我的問題。由於'ftruncate'失敗,添加文件到該偏移量也失敗了,導致了一個'SIGBUS'。 –

+0

如果這是物理內存的問題:http://stackoverflow.com/questions/19391933/how-to-handle-bus-error-for-accessing-mmap-ed-memory-when-underlying-physical-me –

回答

2

我認爲問題在於jint。當j達到較大值時,(j + 1) * BLOCK_SIZE溢出並且您的ftruncate調用不符合您的要求。檢查ftruncate的返回值應該證實這一點。

mmap man page明確呼籲SIGBUS表示試圖訪問不受文件支持。

0

我不確定這是否有助於您的情況。當我們遇到大文件問題時,以下方面會有所幫助。我們已經把宏:

#define _FILE_OFFSET_BITS 64 

之前,包括標準頭。您可以在調用gcc時在命令行上定義它。

+1

This僅適用於32位系統。 – alk