2011-12-07 131 views
2

我寫了一個代碼,用於將內容寫入映射的緩衝區,該緩衝區使用mmap()系統調用進行映射。 當我在映射的緩衝區中做了一些更改後,我調用了msync(),它應該更新到磁盤上的文件。內存映射文件

但是,它並未對磁盤上的文件進行任何更改。

#include<stdio.h> 
#include<stdlib.h> 
#include<string.h> 
#include<unistd.h> 
#include <sys/types.h> 
#include <sys/stat.h> 
#include<sys/mman.h> 
#include<fcntl.h> 
#define FILEMODE S_IRWXU | S_IRGRP | S_IROTH 
#define MAX 150 

main(int argc,char *argv[]) 
{ 
int fd,ret,len; 
long int len_file; 
struct stat st; 
char *addr; 
char buf[MAX]; 


if(argc > 1) 
{ 
    if((fd = open(argv[1],O_RDWR | O_APPEND | O_CREAT ,FILEMODE)) < 0) 
     perror("Error in file opening"); 

    if((ret=fstat(fd,&st)) < 0) 
     perror("Error in fstat"); 

    len_file = st.st_size; 

      /*len_file having the total length of the file(fd).*/ 


    if((addr=mmap(NULL,len_file,PROT_READ|PROT_WRITE,MAP_PRIVATE,fd,0)) == MAP_FAILED) 
     perror("Error in mmap"); 

    len = len_file; 

    while((fgets(buf,MAX,stdin)) != NULL) 
    { 
     strcat(addr+len,buf); 
     printf("Val:%s\n",addr) ; //Checking purpose 
     len = len + (strlen(buf)); 
    } 
    if((msync(addr,len,MS_SYNC)) < 0) 
     perror("Error in msync"); 

    if(munmap(addr,len) == -1) 
     printf("Error:\n"); 
    printf("addr %p\n",addr); 
} 
else 
{ 
    printf("Usage a.out <filename>\n"); 
} 
} 

回答

14

如果您想更改在磁盤上的文件中反映出來,必須將文件映射爲MAP_SHARED,不MAP_PRIVATE

此外,您不能簡單地通過寫入超出映射的末尾來擴展文件。您必須使用ftruncate()將文件擴展爲新大小,然後更改映射以包含文件的新部分。可改變映射的可移植方式是取消映射映射,然後用新大小重新創建它;在Linux上,您可以改爲使用mremap()

lenlen_file變量應該是size_t類型的,你應該使用memcpy()而非strcat(),因爲你知道該字符串的準確長度,正是要複製它,你希望複製空終止符。

代碼的下面的修改工作在Linux(使用mremap()):

#define _GNU_SOURCE 
#include<stdio.h> 
#include<stdlib.h> 
#include<string.h> 
#include<unistd.h> 
#include <sys/types.h> 
#include <sys/stat.h> 
#include<sys/mman.h> 
#include<fcntl.h> 
#define FILEMODE S_IRWXU | S_IRGRP | S_IROTH 
#define MAX 150 

int main(int argc,char *argv[]) 
{ 
    int fd, ret; 
    size_t len_file, len; 
    struct stat st; 
    char *addr; 
    char buf[MAX]; 

    if (argc < 2) 
    { 
     printf("Usage a.out <filename>\n"); 
     return EXIT_FAILURE; 
    } 

    if ((fd = open(argv[1],O_RDWR | O_CREAT, FILEMODE)) < 0) 
    { 
     perror("Error in file opening"); 
     return EXIT_FAILURE; 
    } 

    if ((ret = fstat(fd,&st)) < 0) 
    { 
     perror("Error in fstat"); 
     return EXIT_FAILURE; 
    } 

    len_file = st.st_size; 

    /*len_file having the total length of the file(fd).*/ 

    if ((addr = mmap(NULL,len_file,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0)) == MAP_FAILED) 
    { 
     perror("Error in mmap"); 
     return EXIT_FAILURE; 
    } 

    while ((fgets(buf,MAX,stdin)) != NULL) 
    { 
     len = len_file; 
     len_file += strlen(buf); 
     if (ftruncate(fd, len_file) != 0) 
     { 
      perror("Error extending file"); 
      return EXIT_FAILURE; 
     } 
     if ((addr = mremap(addr, len, len_file, MREMAP_MAYMOVE)) == MAP_FAILED) 
     { 
      perror("Error extending mapping"); 
      return EXIT_FAILURE; 
     } 
     memcpy(addr+len, buf, len_file - len); 
     printf("Val:%s\n",addr) ; //Checking purpose 
    } 
    if((msync(addr,len,MS_SYNC)) < 0) 
     perror("Error in msync"); 

    if(munmap(addr,len) == -1) 
     perror("Error in munmap"); 

    printf("addr %p\n",addr); 

    return 0; 
} 
+0

我試過也。它不工作。 – sat

+0

很好..它工作正常..謝謝。 – sat

2

請注意,您所提供的映射,這正是該文件的大小的文件。如果你在調用open(2)時創建了這個文件,它的長度將會是0,如果內核沒有設置長度映射的任何一種內存映射,我都不會感到驚訝。 (也許它呢?我從來沒有試過......)

我建議使用ftruncate(2)延長文件的長度之前執行映射。 (請注意,使用ftruncate(2)擴展文件是不是很便攜;不是所有的平臺提供了擴展功能,而不是所有的文件系統驅動程序支持的擴展功能,請參閱系統的詳細信息手冊頁。)

必須使用MAP_SHARED映射文件將修改保存到磁盤。

您對perror(3)的使用不太正確; perror(3)終止程序,因此它會繼續以不正確的假設執行:

if((ret=fstat(fd,&st)) < 0) 
    perror("Error in fstat"); 

應改爲:

if((ret=fstat(fd,&st)) < 0) { 
    perror("Error in fstat"); 
    exit(1); 
} 

(或者exit(EXIT_FAILURE)如果你想成爲更便攜 - 我發現,有點困難的眼睛,但我住在Linux的土地。)

strcat(3)期望找到一個ASCII NUL字符(字節值0x00,C代表'\0') - 通常的C末尾字符串標記 - 在dest字符串的末尾。如果你在這個程序中創建它,你的文件將不會包含ASCII NUL - 它的長度畢竟是零 - 而且我不知道通過mmap(2)讀取零字節文件的後果。如果文件已經存在並且有數據,那麼可能是沒有在文件中編碼的ASCII NUL。 strcat(3)幾乎肯定是寫入文件的錯誤工具。 (無論如何,沒有人希望在他們的文件中使用ASCII NUL。)嘗試使用memcpy(3)

+0

來自匿名評論者:_原作者將「file is append-only」與「fd以追加模式打開」混淆。文件變成附加文件,例如在shell中運行'chattr + THEFILE'。 'O_APPEND'指示寫入系統調用的行爲,但不指定內存映射。 - 因此編輯以去除「MAP_SHARED」位。 :) – sarnold