天天看點

Linux下記憶體映射檔案的用法

學習了一下Linux下記憶體映射檔案的用法,在這裡共享一下自己的收獲,希望大家提出寶貴意見,進行交流。

        簡介:

        記憶體映射檔案與虛拟記憶體有些類似,通過記憶體映射檔案可以保留一個位址空間的區域,同時将實體存儲器送出給此區域,隻是記憶體檔案映射的實體存儲器來自一個已經存在于磁盤上的檔案,而非系統的頁檔案,而且在對該檔案進行操作之前必須首先對檔案進行映射,就如同将整個檔案從磁盤加載到記憶體。由此可以看出,使用記憶體映射檔案處理存儲于磁盤上的檔案時,将不必再對檔案執行I/O操作,這意味着在對檔案進行處理時将不必再為檔案申請并配置設定緩存,所有的檔案緩存操作均由系統直接管理,由于取消了将檔案資料加載到記憶體、資料從記憶體到檔案的回寫以及釋放記憶體塊等步驟,使得記憶體映射檔案在處理大資料量的檔案時能起到相當重要的作用。另外,實際工程中的系統往往需要在多個程序之間共享資料,如果資料量小,處理方法是靈活多變的,如果共享資料容量巨大,那麼就需要借助于記憶體映射檔案來進行。實際上,記憶體映射檔案正是解決本地多個程序間資料共享的最有效方法。

Linux提供了記憶體映射函數mmap, 它把檔案内容映射到一段記憶體上(準确說是虛拟記憶體上), 通過對這段記憶體的讀取和修改, 實作對檔案的讀取和修改, 先來看一下mmap的函數聲明:

  • 頭檔案:
    • <unistd.h>
    • <sys/mman.h>
  • 原型: void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offsize);
  • 傳回值: 成功則傳回映射區起始位址, 失敗則傳回MAP_FAILED(-1).
  • 參數:
    • addr: 指定映射的起始位址, 通常設為NULL, 由系統指定.
    • length: 将檔案的多大長度映射到記憶體.
    • prot: 映射區的保護方式, 可以是:
      • PROT_EXEC: 映射區可被執行.
      • PROT_READ: 映射區可被讀取.
      • PROT_WRITE: 映射區可被寫入.
      • PROT_NONE: 映射區不能存取.
    • flags: 映射區的特性, 可以是:
      • MAP_SHARED: 對映射區域的寫入資料會複制回檔案, 且允許其他映射該檔案的程序共享.
      • MAP_PRIVATE: 對映射區域的寫入操作會産生一個映射的複制(copy-on-write), 對此區域所做的修改不會寫回原檔案.
      • 此外還有其他幾個flags不很常用, 具體檢視linux C函數說明.
    • fd: 由open傳回的檔案描述符, 代表要映射的檔案.
    • offset: 以檔案開始處的偏移量, 必須是分頁大小的整數倍, 通常為0, 表示從檔案頭開始映射.

    下面說一下記憶體映射的步驟:

  • 用open系統調用打開檔案, 并傳回描述符fd.
  • 用mmap建立記憶體映射, 并傳回映射首位址指針start.
  • 對映射(檔案)進行各種操作, 顯示(printf), 修改(sprintf).
  • 用munmap(void *start, size_t lenght)關閉記憶體映射.
  • 用close系統調用關閉檔案fd.

注意事項:

在修改映射的檔案時, 隻能在原長度上修改, 不能增加檔案長度, 因為記憶體是已經配置設定好的.

        在網上找了一些代碼,自己加工之後在Linux下編譯通過,可以運作實作相應功能。

#include<stdio.h>

#include<stdlib.h>

#include<string.h>

#include<sys/types.h>

#include<sys/stat.h>

#include<errno.h>

#include<unistd.h>

#include<fcntl.h>

#include<sys/mman.h>

int main()

{

    int outfile;

    char*mapped;

    char*ptr;

    if((outfile=open("test.dat",O_RDWR|O_CREAT|O_TRUNC,0640))==-1)

    {

        printf("couldn't open this file!\n");

        exit(254);

    }

    lseek(outfile,1000,SEEK_SET);//把檔案指針outfile移到從檔案頭開始到1000位元組處

    if(write(outfile,"\0",1)==-1)//在第1001位元組處寫入資料"\0"

    {

        printf("error,write failed!\n");

        exit(254);

    }

 //建立記憶體映像檔案,将檔案從檔案頭開始到1000位元組處映射到記憶體,mapped指向該記憶體的首位址

    mapped=mmap(NULL,1000,PROT_READ|PROT_WRITE,MAP_SHARED,outfile,0);

    if(!mapped)

    {

         printf("error ,write failed!\n");

         exit(254);

    }

    ptr=mapped;

    printf("please enter a number!\n");

    fgets(mapped,80,stdin);//從終端輸入一個數字

    printf("your number times two is:%d\n",atoi(mapped)*2);

    //将該數字與2相乘結果列印出來

    sprintf(ptr,"your number timews two is :%d\n",atoi(mapped)*2);//把資料儲存到記憶體中

    msync(ptr,1000,MS_SYNC);//将記憶體中資料儲存到檔案中

    munmap(ptr,1000); //撤銷記憶體映像檔案

    if(close(outfile))

    {

        printf("possibly serious error,close file failed!\n");

        exit(254);

    }

    return 0;

}

繼續閱讀