• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            isware

            Linux的mmap文件內存映射機制

              在講述文件映射的概念時, 不可避免的要牽涉到虛存(SVR 4的VM). 實際上, 文件映射是虛存的中心概念, 文件映射一方面給用戶提供了一組措施, 好似用戶將文件映射到自己地址空間的某個部分, 使用簡單的內存訪問指令讀寫文件;另一方面, 它也可以用于內核的基本組織模式, 在這種模式種, 內核將整個地址空間視為諸如文件之類的一組不同對象的映射. 中的傳統文件訪問方式是, 用 open系統調用打開文件, 然后使用read, write以及lseek等調用進行順序或者隨即的I/O. 這種方式是非常低效的, 每一次I/O操作都需要一次系統調用. 另外, 如果若干個進程訪問同一個文件, 每個進程都要在自己的地址空間維護一個副本, 浪費了內存空間. 而如果能夠通過一定的機制將頁面映射到進程的地址空間中, 也就是說通過簡單的產生某些內存管理數據結構完成映射的創建. 當進程訪問頁面時產生一個缺頁中斷, 內核將頁面讀入內存并且更新頁表指向該頁面. 這種方式非常方便于同一副本的共享.

              VM是面向對象的方法設計的, 這里的對象是指內存對象: 內存對象是一個軟件抽象的概念, 它描述內存區與后備存儲之間的映射. 系統可以使用多種類型的后備存儲, 比如交換空間, 本地或者遠程文件以及幀緩存等等. VM系統對它們統一處理, 采用同一操作集操作, 比如讀取頁面或者回寫頁面等. 每種不同的后備存儲都可以用不同的方法實現這些操作. 這樣, 系統定義了一套統一的接口, 每種后備存儲給出自己的實現方法. 這樣, 進程的地址空間就被視為一組映射到不同數據對象上的的映射組成. 所有的有效地址就是那些映射到數據對象上的地址. 這些對象為映射它的頁面提供了持久性的后備存儲. 映射用戶可以直接尋址這些對象.

              值得提出的是, VM體系結構獨立于Unix系統, 所有的Unix系統語義, 如正文, 數據及堆棧區都可以建構在基本VM系統之上. 同時, VM體系結構也是獨立于存儲管理的, 存儲管理是由操作系統實施的, 如: 究竟采取什么樣的對換和請求調頁算法, 究竟是采取分段還是分頁機制進行存儲管理, 究竟是如何將虛擬地址轉換成為物理地址等等(Linux中是一種叫Three Level Page Table的機制), 這些都與內存對象的概念無關.

              下面介紹Linux中 VM的實現.

              一個進程應該包括一個mm_struct(memory manage struct), 該結構是進程虛擬地址空間的抽象描述, 里面包括了進程虛擬空間的一些管理信息: start_code, end_code, start_data, end_data, start_brk, end_brk等等信息. 另外, 也有一個指向進程虛存區表(vm_area_struct: virtual memory area)的指針, 該鏈是按照虛擬地址的增長順序排列的. 在Linux進程的地址空間被分作許多區(vma), 每個區(vma)都對應虛擬地址空間上一段連續的區域, vma是可以被共享和保護的獨立實體, 這里的vma就是前面提到的內存對象. 下面是vm_area_struct的結構, 其中, 前半部分是公共的, 與類型無關的一些數據成員, 如: 指向mm_struct的指針, 地址范圍等等, 后半部分則是與類型相關的成員, 其中最重要的是一個指向vm_operation_struct向量表的指針vm_ops, vm_pos向量表是一組虛函數, 定義了與vma類型無關的接口. 每一個特定的子類, 即每種vma類型都在向量表中實現這些操作. 這里包括了: open, close, unmap, protect, sync, nopage, wppage, swapout這些操作.

              struct vm_area_struct {

              /*公共的, 與vma類型無關的 */

              struct mm_struct * vm_mm;

              unsigned long vm_start;

              unsigned long vm_end;

              struct vm_area_struct *vm_next;

              pgprot_t vm_page_prot;

              unsigned long vm_flags;

              short vm_avl_height;

              struct vm_area_struct * vm_avl_left;

              struct vm_area_struct * vm_avl_right;

              struct vm_area_struct *vm_next_share;

              struct vm_area_struct **vm_pprev_share;

              /* 與類型相關的 */

              struct vm_operations_struct * vm_ops;

              unsigned long vm_pgoff;

              struct file * vm_file;

              unsigned long vm_raend;

              void * vm_private_data;

              };

              vm_ops: open, close, no_page, swapin, swapout……

              介紹完VM的基本概念后, 我們可以講述mmap和munmap系統調用了. mmap調用實際上就是一個內存對象vma的創建過程, mmap的調用格式是:

              void * mmap(void *start, size_t length, int prot , int flags, int fd, off_t offset);

              其中start是映射地址, length是映射長度, 如果flags的MAP_FIXED不被置位, 則該參數通常被忽略, 而查找進程地址空間中第一個長度符合的空閑區域;Fd是映射文件的文件句柄, offset是映射文件中的偏移地址;prot是映射保護權限, 可以是PROT_EXEC, PROT_READ, PROT_WRITE, PROT_NONE, flags則是指映射類型, 可以是MAP_FIXED, MAP_PRIVATE, MAP_SHARED, 該參數被 指定為MAP_PRIVATE和MAP_SHARED其中之一, MAP_PRIVATE 是創建一個寫時拷貝映射(copy-on-write), 也就是說如果有多個進程同時映射到一個文件上, 映射建立時只是共享同樣的存儲頁面, 但是某進程企圖修改頁面內容, 則復制一個副本給該進程私用, 它的任何修改對其它進程都不可見. 而MAP_SHARED則無論修改與否都使用同一副本, 任何進程對頁面的修改對其它進程都是可見的.

              mmap系統調用的實現過程是:

              1.先通過文件系統定位要映射的文件;

              2.權限檢查, 映射的權限不會超過文件打開的方式, 也就是說如果文件是以只讀方式打開, 那么則不允許建立一個可寫映射;

              3.創建一個vma對象, 并對之進行初始化;

              4.調用映射文件的mmap函數, 其主要工作是給vm_ops向量表賦值;

              5.把該vma鏈入該進程的vma鏈表中, 如果可以和前后的vma合并則合并;

              6.如果是要求VM_LOCKED(映射區不被換出)方式映射, 則發出缺頁請求, 把映射頁面讀入內存中.

              munmap(void * start, size_t length):

              該調用可以看作是 mmap的一個逆過程. 它將進程中從start開始length長度的一段區域的映射關閉, 如果該區域不是恰好對應一個vma, 則有可能會分割幾個或幾個vma.

              msync(void * start, size_t length, int flags):

              把映射區域的修改回寫到后備存儲中. munmap 時并不保證頁面回寫, 如果不調用msync, 那么有可能在munmap后丟失對映射區的修改. 其中flags可以是MS_SYNC, MS_ASYNC, MS_INVALIDATE, MS_SYNC要求回寫完成后才返回, MS_ASYNC發出回寫請求后立即返回, MS_INVALIDATE使用回寫的內容更新該文件的其它映射. 該系統調用是通過調用映射文件的sync函數來完成工作的.

              brk(void * end_data_segement):

              將進程的數據段擴展到 end_data_segement指定的地址, 該系統調用和mmap的實現方式十分相似, 同樣是產生一個vma, 然后指定其屬性. 不過在此之前需要做一些合法性檢查, 比如該地址是否大于mm->end_code, end_data_segement和mm->brk之間是否還存在其它vma等等. 通過brk產生的vma映射的文件為空, 這和匿名映射產生的vma相似, 關于匿名映射不做進一步介紹. 庫函數malloc就是通過brk實現的.

              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-mmap函數介紹

              mmap函數是unix/linux下的系統調用,來看《Unix Netword programming》卷二12.2節對mmap的介紹:

              The mmap function maps either a file or a Posix shared memory object into the address space of a process.We use this function for three purposes:

              1. with a regular file to provide memory-mapped I/O

              2. with special files to provide anonymous memory mappings

              3. with shm_open to provide Posix shared memory between unrelated processes

              mmap系統調用并不是完全為了用于共享內存而設計的.它本身提供了不同于一般對普通文件的訪問方式,進程可以像讀寫內存一樣對普通文件的操作.而 Posix或系統V的共享內存IPC則純粹用于共享目的,當然mmap()實現共享內存也是其主要應用之一.

              mmap系統調用進程之間通過映射同一個普通文件實現共享內存.普通文件被映射到進程地址空間后,進程可以像訪問普通內存一樣對文件進行訪問,不必再調用read(),write()等操作.

              我們的程序中大量運用了mmap,用到的正是mmap的這種“像訪問普通內存一樣對文件進行訪問”的功能.實踐證明,當要對一個文件頻繁的進行訪問,并且指針來回移動時,調用mmap比用常規的方法快很多.

              來看看mmap的定義:

              void *mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset);

              參數fd為即將映射到進程空間的文件描述字,一般由open()返回,同時,fd可以指定為-1,此時須指定flags參數中的MAP_ANON,表明進行的是匿名映射(不涉及具體的文件名,避免了文件的創建及打開,很顯然只能用于具有親緣關系的進程間通信).

              len是映射到調用進程地址空間的字節數,它從被映射文件開頭offset個字節開始算起.

              prot參數指定共享內存的訪問權限.可取如下幾個值的或:PROT_READ(可讀),PROT_WRITE(可寫),PROT_EXEC(可執行),PROT_NONE(不可訪問).

              flags由以下幾個常值指定:MAP_SHARED, MAP_PRIVATE, MAP_FIXED.其中,MAP_SHARED,MAP_PRIVATE必選其一,而MAP_FIXED則不推薦使用.

              如果指定為MAP_SHARED,則對映射的內存所做的修改同樣影響到文件.如果是MAP_PRIVATE,則對映射的內存所做的修改僅對該進程可見,對文件沒有影響.

              offset參數一般設為0,表示從文件頭開始映射.

              參數addr指定文件應被映射到進程空間的起始地址,一般被指定一個空指針,此時選擇起始地址的任務留給內核來完成.函數的返回值為文件映射到進程空間的地址,進程可直接操作起始地址為該值的有效地址.

              ,舉個例子來結束本節.4.2節說過,Fileinformation數組是以二進制的形式寫進一個叫inforindex的文件中.那么,當要訪問 Fileinformation數組時,代碼類似這樣:

              struct stat st;

              char buffer=” inforindex”;

              Fileinformation *_fileinfoIndexptr = NULL;

              if(stat(buffer,&st)<0)

             

              fprintf(stderr,"error to stat %sn",buffer);

              exit(-1);

              }

              // mmap the inforindex to _fileinfoIndexptr

              int fd=open(buffer, O_RDONLY);

              if(fd<0)

              {

              printf("error to open %sn",buffer);

              exit(-1);

              }

              _fileinfoIndexptr = (Fileinformation*)mmap(NULL,st.st_size, PROT_READ,MAP_SHARED,fd,0);

              if(MAP_FAILED == _fileinfoIndexptr)

              {

              printf("error to mmap %sn",buffer);

              close(fd);

              exit(-1);

              }

              close(fd);

              下面這個例子顯示了把文件映射到內存的方法

              源代碼是:

              /************關于本文檔********************************************

              *filename: mmap.c

              *purpose: 說明調用mmap把文件映射到內存的方法

              *wrote by: zhoulifa(zhoulifa@163.com) 周立發(http://zhoulifa.bokee.com)

              Linux愛好者 Linux知識傳播者 SOHO族 開發者 最擅長C語言

              *date time:2008-01-27 18:59 上海大雪天,據說是多年不遇

              *Note: 任何人可以任意復制代碼并運用這些文檔,當然包括你的商業用途

              * 但請遵循GPL

              *Thanks to:

              * Ubuntu 本程序在Ubuntu 7.10系統上測試完全正常

              * Google.com 我通常通過google搜索發現許多有用的資料

              *Hope:希望越來越多的人貢獻自己的力量,為科學技術發展出力

              * 科技站在巨人的肩膀上進步更快!感謝有開源前輩的貢獻!

              *********************************************************************/

              #include <sys/mman.h> /* for mmap and munmap */

              #include <sys/types.h> /* for open */

              #include <sys/stat.h> /* for open */

              #include <fcntl.h> /* for open */

              #include <unistd.h> /* for lseek and write */

              #include <stdio.h>

              int main(int argc, char **argv)

              {

              int fd;

              char *mapped_mem, * p;

              int flength = 1024;

              void * start_addr = 0;

              fd = open(argv[1], O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);

              flength = lseek(fd, 1, SEEK_END);

              write(fd, "", 1); /* 在文件添加一個空字符,以便下面printf正常工作 */

              lseek(fd, 0, SEEK_SET);

              mapped_mem = mmap(start_addr, flength, PROT_READ, //允許讀

              MAP_PRIVATE, //不允許其它進程訪問此內存區域

              fd, 0);

              /* 使用映射區域. */

              printf("%sn", mapped_mem); /* 為了保證這里工作正常,參數傳遞的文件名最好是一個文本文件 */

              close(fd);

              munmap(mapped_mem, flength);

              return 0;

              }

              編譯運行此程序:

              gcc -Wall mmap.c

              ./a.out text_filename

              上面的方法用了PROT_READ,只能讀取文件里的內容,不能修改,如果換成PROT_WRITE就可以修改文件的內容了.又 用了MAAP_PRIVATE只能此進程使用此內存區域,如果換成MAP_SHARED,則可以被其它進程訪問,比如下面的:

              #include <sys/mman.h> /* for mmap and munmap */

              #include <sys/types.h> /* for open */

              #include <sys/stat.h> /* for open */

              #include <fcntl.h> /* for open */

              #include <unistd.h> /* for lseek and write */

              #include <stdio.h>

              #include <string.h> /* for memcpy */

              int main(int argc, char **argv)

              {

              int fd;

              char *mapped_mem, * p;

              int flength = 1024;

              void * start_addr = 0;

              fd = open(argv[1], O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);

              flength = lseek(fd, 1, SEEK_END);

              write(fd, "", 1); /* 在文件添加一個空字符,以便下面printf正常工作 */

              lseek(fd, 0, SEEK_SET);

              start_addr = 0x80000;

              mapped_mem = mmap(start_addr, flength, PROT_READ|PROT_WRITE, //允許寫入

              MAP_SHARED, //允許其它進程訪問此內存區域

              fd, 0);

              /* 使用映射區域. */

              printf("%sn", mapped_mem); /* 為了保證這里工作正常,參數傳遞的文件名最好是一個文本文 */

              while((p = strstr(mapped_mem, "Hello"))) { /* 此處來修改文件 內容 */

              memcpy(p, "Linux", 5);

              p = 5;

              }

              close(fd);

              munmap(mapped_mem, flength);

              return 0;

              }

              man -a mmap 看更詳細的信息

            posted on 2011-07-20 15:51 艾斯維亞 閱讀(1351) 評論(0)  編輯 收藏 引用

            国产91色综合久久免费| 久久男人Av资源网站无码软件| 国产成人精品久久亚洲高清不卡 国产成人精品久久亚洲高清不卡 国产成人精品久久亚洲 | 久久无码AV一区二区三区| 久久久久久免费视频| 精品国产乱码久久久久久郑州公司 | 久久国产高清字幕中文| 国产精品亚洲美女久久久| 热久久视久久精品18| 久久精品国产99国产精偷| 久久久久久亚洲精品无码| 国产激情久久久久久熟女老人| 精品无码久久久久国产| 久久99精品国产麻豆蜜芽| 欧洲精品久久久av无码电影| 国产精品欧美久久久久天天影视 | 热久久视久久精品18| 久久91亚洲人成电影网站| 伊人久久一区二区三区无码| 久久福利青草精品资源站免费| 亚洲日本久久久午夜精品| 国产精品热久久毛片| 久久大香香蕉国产| 色88久久久久高潮综合影院| 久久精品无码一区二区三区免费| 久久久噜噜噜久久熟女AA片| 久久天天躁狠狠躁夜夜躁2014| 九九久久精品国产| 国产精品久久久久久久午夜片| 九九久久99综合一区二区| 久久棈精品久久久久久噜噜| 成人午夜精品无码区久久| 亚洲午夜精品久久久久久浪潮| 青青热久久综合网伊人| 久久99国产精品久久99果冻传媒| 亚洲AV日韩精品久久久久久| 一本久久a久久精品vr综合| 亚洲乱码精品久久久久..| 伊人久久大香线蕉精品不卡| 久久人人添人人爽添人人片牛牛| 久久青青草视频|