Linux 內存映射 mmap 原理分析

      網友投稿 1012 2022-05-28

      本文轉自博客,我修改了一些筆誤,并劃了一些我自認為的重點。

      原理

      首先,“映射”這個詞,就和數學課上說的“一一映射”是一個意思,就是建立一種一一對應關系,在這里主要是指?硬盤上文件?的位置與 進程邏輯地址空間?中一塊大小相同的區域之間的一一對應,如?圖1中過程1?所示。這種對應關系純屬是邏輯上的概念,物理上是不存在的,原因是進程的邏輯地址空間本身就是不存在的。在內存映射的過程中,并沒有實際的數據拷貝,文件沒有被載入內存,只是邏輯上被放入了內存,具體到代碼,就是建立并初始化了相關的數據結構(struct?address_space),這個過程有系統調用mmap()實現,所以建立內存映射的效率很高。

      圖1.內存映射原理

      既然建立內存映射沒有進行實際的數據拷貝,那么進程又怎么能最終直接通過內存操作訪問到硬盤上的文件呢?那就要看內存映射之后的幾個相關的過程了。

      mmap() 會返回一個指針 ptr,它指向進程邏輯地址空間中的一個地址,這樣以后,進程無需再調用 read 或 write 對文件進行讀寫,而只需要通過 ptr 就能夠操作文件。但是 ptr 所指向的是一個邏輯地址,要操作其中的數據,必須通過 MMU 將邏輯地址轉換成物理地址,如 圖1中過程2 所示。這個過程與內存映射無關。

      前面講過,建立內存映射并沒有實際拷貝數據,這時,MMU 在地址映射表中是無法找到與 ptr 相對應的物理地址的,也就是MMU失敗,將產生一個缺頁中斷,缺頁中斷的中斷響應函數會在 swap 中尋找相對應的頁面,如果找不到(也就是該文件從來沒有被讀入內存的情況),則會通過 mmap() 建立的映射關系,從硬盤上將文件讀取到物理內存中,如 圖1中過程3 所示。這個過程與內存映射無關。

      如果在拷貝數據時,發現物理內存不夠用,則會通過虛擬內存機制(swap)將暫時不用的物理頁面交換到硬盤上,如 圖1中過程4 所示。這個過程也與內存映射無關。

      效率

      從代碼層面上看,從硬盤上將文件讀入內存,都要經過文件系統進行數據拷貝,并且數據拷貝操作是由文件系統和硬件驅動實現的,理論上來說,拷貝數據的效率是一樣的。但是通過內存映射的方法訪問硬盤上的文件,效率要比 read 和 write 系統調用高,這是為什么呢?原因是 read() 是系統調用,其中進行了數據拷貝,它首先將文件內容從硬盤拷貝到內核空間的一個緩沖區,如 圖2中過程1 ,然后再將這些數據拷貝到用戶空間,如 圖2中過程2 ,在這個過程中,實際上完成了?兩次數據拷貝?;而mmap() 雖然也是系統調用,如前所述,mmap() 中沒有進行數據拷貝,真正的數據拷貝是在缺頁中斷處理時進行的,由于 mmap() 將文件直接映射到用戶空間,所以中斷處理函數根據這個映射關系,直接將文件從硬盤拷貝到用戶空間,只進行了?一次數據拷貝?。因此,內存映射的效率要比 read/write 效率高

      下面這個程序,通過 read 和 mmap 兩種方法分別對硬盤上一個名為 “mmap_test” 的文件進行操作,文件中存有 10000 個整數,程序兩次使用不同的方法將它們讀出,加1,再寫回硬盤。通過對比可以看出,read 消耗的時間將近是 mmap 的兩到三倍

      #include

      #include

      #include

      #include

      #include

      #include

      #include

      #include

      #include

      #define MAX 10000

      int main()

      {

      int i = 0;

      int count = 0, fd = 0;

      struct timeval tv1, tv2;

      int *array = (int *) malloc( sizeof(int) * MAX );

      /*read*/

      gettimeofday( &tv1, NULL );

      fd = open( "mmap_test", O_RDWR );

      if ( sizeof(int) * MAX != read( fd, (void *) array, sizeof(int) * MAX ) )

      {

      printf( "Reading data failed.../n" );

      return(-1);

      }

      for ( i = 0; i < MAX; ++i )

      ++array[i];

      if ( sizeof(int) * MAX != write( fd, (void *) array, sizeof(int) * MAX ) )

      {

      printf( "Writing data failed.../n" );

      return(-1);

      }

      free( array );

      close( fd );

      gettimeofday( &tv2, NULL );

      printf( "Time of read/write: %dms/n", tv2.tv_usec - tv1.tv_usec );

      /*mmap*/

      gettimeofday( &tv1, NULL );

      fd = open( "mmap_test", O_RDWR );

      array = mmap( NULL, sizeof(int) * MAX, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0 );

      for ( i = 0; i < MAX; ++i )

      ++array[i];

      munmap( array, sizeof(int) * MAX );

      msync( array, sizeof(int) * MAX, MS_SYNC );

      free( array );

      close( fd );

      gettimeofday( &tv2, NULL );

      printf( "Time of mmap: %dms/n", tv2.tv_usec - tv1.tv_usec );

      return(0);

      }

      輸出結果:

      Linux 內存映射 mmap 原理分析

      Time?of?read/write:?154ms

      Time?of?mmap:?68ms

      linux 任務調度

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      上一篇:Centos7.6系統重置root用戶密碼
      下一篇:AM335x評估板快速測試(3)
      相關文章
      4480yy私人影院亚洲| 亚洲AV永久无码区成人网站| 亚洲丝袜美腿视频| 国产亚洲精品a在线无码| 在线A亚洲老鸭窝天堂| 久久久久国产亚洲AV麻豆| 亚洲一区二区三区乱码A| 亚洲男人av香蕉爽爽爽爽| 亚洲黄片手机免费观看| 一区二区三区亚洲视频| www.亚洲精品.com| 亚洲精品在线视频| 久久精品国产亚洲AV不卡| 国产成人A亚洲精V品无码| 国产亚洲日韩在线三区| 亚洲精品国产字幕久久不卡| 国产AV无码专区亚洲AVJULIA| 久久亚洲国产欧洲精品一| 亚洲免费在线视频| 97亚洲熟妇自偷自拍另类图片| 337p日本欧洲亚洲大胆精品555588| 亚洲伊人久久大香线蕉苏妲己| 91亚洲导航深夜福利| 亚洲日本香蕉视频| 亚洲婷婷第一狠人综合精品| 亚洲AV日韩综合一区尤物| 亚洲码欧美码一区二区三区| 精品国产亚洲AV麻豆| 亚洲日韩国产精品乱| 亚洲日韩精品无码一区二区三区 | 亚洲欧洲日本精品| jlzzjlzz亚洲jzjzjz| 亚洲色大成网站www尤物| 蜜芽亚洲av无码一区二区三区| 亚洲AV无码一区二区三区国产 | 亚洲欧洲日本国产| 亚洲一区二区观看播放| 国产青草亚洲香蕉精品久久| 亚洲人成影院在线观看| 久久精品国产69国产精品亚洲| 亚洲一区二区三区夜色|