標籤:
共用記憶體的建立
根據理論:
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的好處是在整個過程中你不需要處理控制代碼:只要開啟檔案並把它映射在合適的位置就行了。你可以在兩個不相關的進程間使用記憶體對應檔。
使用記憶體映射的缺點是速度不如共用記憶體快。如果湊巧檔案很大,所需要的虛擬記憶體就會很大,這樣會造成整體效能下降。
(轉)mmap和shm共用記憶體的區別和聯絡