(轉)mmap和shm共享內存的區別和聯系


共享內存的創建

根據理論:

 1. 共享內存允許兩個或多個進程共享一給定的存儲區,因為數據不需要來回復制,所以是最快的一種進程間通信機制。共享內存可以通過mmap()映射普通文件 (特殊情況下還可以采用匿名映射)機制實現,也可以通過系統V共享內存機制實現。應用接口和原理很簡單,內部機制復雜。為了實現更安全通信,往往還與信號 燈等同步機制共同使用。

mmap的機制如:就是在磁盤上建立一個文件,每個進程存儲器里面,單獨開辟一個空間來進行映射。如果多進程的話,那么不會對實際的物理存儲器(主存)消耗太大。

shm的機制:每個進程的共享內存都直接映射到實際物理存儲器里面。


結論:

1、mmap保存到實際硬盤,實際存儲並沒有反映到主存上。優點:儲存量可以很大(多於主存)(這里一個問題,需要高手解答,會不會太多拷貝到主存里面???);缺點:進程間讀取和寫入速度要比主存的要慢。

2、shm保存到物理存儲器(主存),實際的儲存量直接反映到主存上。優點,進程間訪問速度(讀寫)比磁盤要快;缺點,儲存量不能非常大(多於主存)

使用上看:如果分配的存儲量不大,那么使用shm;如果存儲量大,那么使用shm。


參看百度:http://baike.baidu.com/view/1499209.htm

mmap就是一個文件操作


看這些百度的描述:

mmap()系統調用使得進程之間通過映射同一個普通文件實現共享內存。普通文件被映射到進程地址空間后,進程可以向訪問普通內存一樣對文件進行訪問,不 必再調用read(),write()等操作。 成功執行時,mmap()返回被映射區的指針,munmap()返回0。失敗時,mmap()返回MAP_FAILED[其值為(void *)-1],munmap返回-1。errno被設為以下的某個值 EACCES:訪問出錯EAGAIN:文件已被鎖定,或者太多的內存已被鎖定EBADF:fd不是有效的文件描述詞EINVAL:一個或者多個參數無效 ENFILE:已達到系統對打開文件的限制ENODEV:指定文件所在的文件系統不支持內存映射ENOMEM:內存不足,或者進程已超出最大內存映射數量 EPERM:權能不足,操作不允許ETXTBSY:已寫的方式打開文件,同時指定MAP_DENYWRITE標志SIGSEGV:試着向只讀區寫入 SIGBUS:試着訪問不屬於進程的內存區參數fd為即將映射到進程空間的文件描述字,


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


相關文章參考:

 

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

mmap系統調用並不是完全為了用於共享內存而設計的。它本身提供了不同於一般對普通文件的訪問方式,進程可以像讀寫內存一樣對普通文件的操作。而Posix或系統V的共享內存IPC則純粹用於共享目的,當然mmap()實現共享內存也是其主要應用之一。
          mmap系統調用使得進程之間通過映射同一個普通文件實現共享內存。普通文件被映射到進程地址空間后,進程可以像訪問普通內存一樣對文件進行訪問,不必再 調用read(),write()等操作。mmap並不分配空間, 只是將文件映射到調用進程的地址空間里, 然后你就可以用memcpy等操作寫文件, 而不用write()了.寫完后用msync()同步一下, 你所寫的內容就保存到文件里了. 不過這種方式沒辦法增加文件的長度, 因為要映射的長度在調用mmap()的時候就決定了.

簡單說就是把一個文件的內容在內存里面做一個映像,內存比磁盤快些。
基本上它是把一個檔案對應到你的virtual memory 中的一段,並傳回一個指針。





重寫總結:

1、mmap實際就是操作“文件”。

2、映射文件,除了主存的考慮外。shm的內存共享,效率應該比mmap效率要高(mmap通過io和文件操作,或“需要寫完后用msync()同步一下”);當然mmap映射操作文件,比直接操作文件要快些;由於多了一步msync應該可以說比shm要慢了吧???

3、另一方面,mmap的優點是,操作比shm簡單(沒有調用比shm函數復雜),我想這也是許多人喜歡用的原因,包括nginx。


缺點,還得通過實際程序測試,確定!!!

 

修正理解(這也真是的,這個網站沒辦法附加;只能重寫了):

今天又細心研究了一下,發現百度這么一段說明:

2、系統調用mmap()用於共享內存的兩種方式:
(1)使用普通文件提供的內存映射:適用於任何進程之間;此時,需要打開或創建一個文件,然后再調用mmap();典型調用代碼如下:
fd=open(name, flag, mode);
if(fd<0)
...
ptr=mmap(NULL, len , PROT_READ|PROT_WRITE, MAP_SHARED , fd , 0); 通過mmap()實現共享內存的通信方式有許多特點和要注意的地方,我們將在范例中進行具體說明。
(2)使用特殊文件提供匿名內存映射:適用於具有親緣關系的進程之間;由於父子進程特殊的親緣關系,在父進程中先調用mmap(),然后調用 fork()。那么在調用fork()之后,子進程繼承父進程匿名映射后的地址空間,同樣也繼承mmap()返回的地址,這樣,父子進程就可以通過映射區 域進行通信了。注意,這里不是一般的繼承關系。一般來說,子進程單獨維護從父進程繼承下來的一些變量。而mmap()返回的地址,卻由父子進程共同維護。

看了一下windows“內存映射文件”:http://baike.baidu.com/view/394293.htm

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

 

這里再總結一次:

1、mmap有兩種方式,一種是映射內存,它把普通文件映射為實際物理內存頁,訪問它就和訪問物理內存一樣(這也就和shm的功能一樣了)(同時不用刷新到文件)

2、mmap可以映射文件,不確定會不會像windows“內存映射文件”一樣的功能,如果是,那么他就能映射好幾G甚至好幾百G的內存數據,對大數據處理將提供強大功能了???

3、shm只做內存映射,和mmap第一個功能一樣!只不過不是普通文件而已,但都是物理內存。


希望大家出意見!!

 

Linux給我們提供了豐富的內部進程通信機制,包括共享內存、內存映射文件、先入先出(FIFO)、接口(sockets)以及多種用於同步的標識。在本文中,我們主要討論一下共享內存和內存映射文件技術。

一般來說,內部進程通信(interprocess communication)也就是IPC,是指兩個或兩個以上進程以及兩個或者兩個以上線程之間進行通信聯系。每個IPC機制都有不同的強項或者弱點, 不過沒有一個IPC機制包含內建的同步方法。因此程序員不但需要自己在程序中實現同步,而且還需要為了利用IPC機制而自己開發通信協議。

共享內存

使用共享內存和使用malloc來分配內存區域很相似。使用共享內存的方法是:

1.對一個進程/線程使用shmget分配內存區域。

2.使用shmat放置一個或多個進程/線程在共享內存中,你也可以用shmctl來獲取信息或者控制共享區域。

3.使用shmdt從共享區域中分離。

4.使用shmctl解除分配空間

下面是個例子:

  
  //建立共享內存區域
  intshared_id;
  char *region;
  const intshm_size = 1024;
  
  shared_id = shmget(IPC_PRIVATE,//保證使用唯一ID
            shm_size,
            IPC_CREAT | IPC_EXCL |//創建一個新的內存區域
            S_IRUSR | S_IWUSR);//使當前用戶可以讀寫這個區域
  
  //交叉進程或生成進程.
  
  //將新建的內存區域放入進程/線程
  region = (char*) shmat(segment_id, 0, 0);
  
  //其他程序代碼
  ...
  
  //將各個進程/線程分離出來
  shmdt(region);
  
  //破壞掉共享內存區域
  shmctl(shared_id, IPC_RMID, 0);
  

共享內存是Linux中最快速的IPC方法。他也是一個雙向過程,共享區域內的任何進程都可以讀寫內存。這個機制的不利方面是其同步和協議都不受程序員控制,你必須確保將句柄傳遞給了子進程和線程。

內存映射文件

內存映射文件不僅僅用於IPC,在其他進程中它也有很大作用。如果你需要將一個分配的緩沖區初始化為零,只要記住/dev/zero 。你也可以通過將文件映射到內存中以提高其性能。它使你可以像讀寫字符串一樣讀寫文件。下面是個例子:

  
  const char filename[] = "testfile";
  intfd;
  char *mapped_mem;
  const intflength = 1024;
  fd = open(filename, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
  lseek(fd, flength + 1, SEEK_SET);
  write(fd, "\0", 1);
  lseek(fd, 0, SEEK_SET);
  
  mapped_mem = mmap(0,
           flength,
           PROT_WRITE, //允許寫入
           MAP_SHARED,//寫入內容被立即寫入到文件
           fd,
           0);
  
  close(fd);
  
  //使用映射區域.
  ...
  
  munmap(file_memory, flength);
  

利用內存映射來處理IPC的好處是在整個過程中你不需要處理句柄:只要打開文件並把它映射在合適的位置就行了。你可以在兩個不相關的進程間使用內存映射文件。

使用內存映射的缺點是速度不如共享內存快。如果湊巧文件很大,所需要的虛擬內存就會很大,這樣會造成整體性能下降。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM