LINUX 中的mmap淺析

gaopengtttt發表於2017-07-21

原創LINUX系統程式設計水平有限,參考UNIX系統程式設計手冊

LINUX 中的mmap淺析

一、mmap基本原理和分類
在LINUX中我們可以使用mmap用來在程式虛擬地址空間中分配建立一片虛擬記憶體地址對映
其可以是
1、檔案對映
   使用檔案內容初始化記憶體
2、匿名對映
   初始化全為0的記憶體空間(calloc也可以)
下面配圖來自UNIX系統程式設計手冊


而對於是否共享又分為
1、私有對映(MAP_PRIVATE)
   多程式間資料共享,修改不反應到磁碟實際檔案,
   私有寫時複製實現
2、共享對映(MAP_SHARED)
   多程式間資料共享,修改反應到磁碟實際檔案中。
那麼總結起來有4種組合
1、私有檔案對映
   多個程式使用同樣的實體記憶體頁進行初始化,但是各個程式
   對記憶體檔案的修改不會共享,也不會反應到物理檔案中,比如
   我們LINUX .so動態庫檔案就採用這種方式對映到各個程式虛擬
   地址空間中
2、私有匿名對映
   mmap會建立一個新的對映,各個程式不共享,這種使用主要用於
   分配記憶體(malloc分配大記憶體會呼叫mmap)。
3、共享檔案對映
   多個程式透過虛擬記憶體技術共享同樣的實體記憶體空間,對記憶體檔案
   的修改會反應到實際物理檔案中,他也是程式間通訊(IPC)的一種機制
4、共享匿名對映
   這種機制在進行fork的時候不會採用寫時複製,父子程式完全共享
   同樣的實體記憶體頁,這也就實現了父子程式通訊(IPC).
下面也是UNIX系統程式設計手冊截圖

在/proc/PID/maps下我們可以找到一個當前程式使用mmap建立的對映比如:
379a000000-379a016000 r-xp 00000000 08:03 12320771                       /lib64/libgcc_s-4.4.7-20120601.so.1
379a016000-379a215000 ---p 00016000 08:03 12320771                       /lib64/libgcc_s-4.4.7-20120601.so.1
379a215000-379a216000 rw-p 00015000 08:03 12320771                       /lib64/libgcc_s-4.4.7-20120601.so.1
379a400000-379a4e8000 r-xp 00000000 08:03 9700201                        /usr/lib64/libstdc++.so.6.0.13
379a4e8000-379a6e8000 ---p 000e8000 08:03 9700201                        /usr/lib64/libstdc++.so.6.0.13
379a6e8000-379a6ef000 r--p 000e8000 08:03 9700201                        /usr/lib64/libstdc++.so.6.0.13
379a6ef000-379a6f1000 rw-p 000ef000 08:03 9700201                        /usr/lib64/libstdc++.so.6.0.13
對於解釋可以參考UNIX系統程式設計手冊如下描述

   
二、mmap函式原型

void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);
引數有點多
addr:對映放到哪裡(虛擬地址),一般傳NULL,讓核心自己決定
length:對映的大小(直接),最小是系統頁的整數倍(4K)
port:點陣圖掩碼 
     PROT_NONE   不能訪問
     PROT_READ   可讀取
     PROT_WRITE  可修改
     PROT_EXEC   可執行
     非法訪問或報SIGSEGV段錯誤訊號
flags:點陣圖掩碼 
      MAP_ANONYMOUS:建立一個匿名對映
      MAP_PRIVATE:私有對映
      MAP_SHARED:共享對映(注意並不能保證一定實際寫入物理磁碟(MSYNC))
      MAP_FIXED:addr必須是頁對齊地址
      其他標示不做解釋
fd:對映檔案的檔案描述符
offset:從檔案的哪個位置開始對映,必須是系統頁的整數倍(4K)

返回值:
成功返回對映的虛擬記憶體地址的起始地址,失敗返回MAP_FAILED

三、建立匿名對映
1、指標MAP_ANONYMOUS,並且fd指定為0
2、開啟/dev/zero檔案將檔案描述符傳遞給mmap()
匿名對映會分配初始化全為0的虛擬記憶體空間

四、其他函式
int msync(void *addr, size_t length, int flags);
用於將kener buffer的資料同步到磁碟
int munmap(void *addr, size_t length);
用於解除對映

五、程式例項
下面我們透過mmap做私有匿名對映來完成一個小的執行緒間同步問題程式,用這片
記憶體區域來做執行緒間通訊

點選(此處)摺疊或開啟

  1. #include<iostream>
  2. #include<sys/mman.h>
  3. #include<pthread.h>
  4. #include<string.h>
  5. #define uint unsigned int
  6. #define MMSIZE (uint)(1<<23)
  7. #define MSIZE (uint)(1<<20)
  8. #define MPRT (uint)(1<<16)
  9. using namespace std;



  10. class tc
  11. {
  12.         private:
  13.                 uint a;
  14.         public:
  15.                 tc():a(1)
  16.         {
  17.                 ;
  18.         }
  19.                 ~tc()
  20.                 {
  21.                         ;
  22.                 }
  23.                 void add()
  24.                 {
  25.                         a=a+1;
  26.                 }
  27.                 void set()
  28.                 {
  29.                         a=1;
  30.                 }
  31.                 void prt(int i)
  32.                 {
  33.                         if(!(i%(MPRT)))
  34.                         {
  35.                                 cout<<":"<<a;
  36.                         }
  37.                 }

  38. };

  39. struct tt
  40. {
  41.         tc* p1;
  42.         pthread_mutex_t* p2;
  43. };

  44. void* test(void* arg)
  45. {
  46.         int i = 0;
  47.         tt* s = NULL;
  48.         s = (tt*)arg;
  49.         int maxloop = 50;
  50.         while(maxloop--)
  51.         {
  52.                 i = MSIZE;
  53.                 pthread_mutex_lock(s->p2);//MUTEX保護臨界區
  54.                 cout<<"Thread:"<<pthread_self()<<" work now!!!\n";
  55.                 for(;i--;)
  56.                 {
  57.                         (s->p1+i)->prt(i);
  58.                         (s->p1+i)->add();
  59.                 }
  60.                 cout<<"\n";
  61.                 pthread_mutex_unlock(s->p2);//解鎖
  62.         }
  63. }



  64. int main(void)
  65. {
  66.         pthread_t tid[3];
  67.         pthread_mutex_t pmut;
  68.         tt s1;
  69.         tc* p = (tc*)mmap(NULL,MMSIZE,PROT_READ|PROT_WRITE,MAP_ANONYMOUS|MAP_PRIVATE,-1,0);//MMAP分配一個匿名私有虛擬記憶體用於執行緒間通訊
  70.         pthread_mutex_init(&pmut,NULL);
  71.         s1.p1 = p;
  72.         s1.p2 = &pmut;

  73.         int i = MSIZE+1;

  74.         for(;i--;)
  75.         {
  76.                 (p+i)->set();//初始化所有的a=1
  77.         }


  78.         for(i=0;i<3;i++)
  79.         {
  80.                 pthread_create(tid+i,NULL,test,(void*)&s1);//建立3個執行緒
  81.         }

  82.         for(i = 0;i<3;i++)
  83.         {
  84.                 pthread_join( *(tid+i) , NULL);//堵塞回收執行緒
  85.         }
  86.         pthread_mutex_destroy(&pmut);
  87.         munmap(p,MMSIZE);

  88. }
同時我們也觀察到了執行緒由於失去CPU而放棄執行其他執行緒得到CPU繼續執行,由於
我們使用MUTEX保護臨界區這個數數還是正常進行。最後正常數到了150
Thread:140545405572864 work now!!!
:32:32:32:32:32:32:32:32:32:32:32:32:32:32:32:32
Thread:140545397180160 work now!!! (執行緒140545405572864 失去CPU執行緒140545397180160執行)
:33:33:33:33:33:33:33:33:33:33:33:33:33:33:33:33
..................
Thread:140545397180160 work now!!!
:58:58:58:58:58:58:58:58:58:58:58:58:58:58:58:58
Thread:140545405572864 work now!!!(執行緒140545405572864重新獲得CPU)
:59:59:59:59:59:59:59:59:59:59:59:59:59:59:59:59
............
Thread:140545388787456 work now!!!
:150:150:150:150:150:150:150:150:150:150:150:150:150:150:150:150

作者微信:

LINUX 中的mmap淺析

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/7728585/viewspace-2142411/,如需轉載,請註明出處,否則將追究法律責任。

相關文章