共享記憶體可以說是最有用的程序間通訊方式,也是最快的ipc形式。兩個不同程序a、b共享記憶體的意思是,同一塊物理記憶體被對映到程序a、b各自的程序位址空間。程序a可以即時看到程序b對共享記憶體中資料的更新,反之亦然。由於多個程序共享同一塊記憶體區域,必然需要某種同步機制,互斥鎖和訊號量都可以。
採用共享記憶體通訊的乙個顯而易見的好處是效率高,因為程序可以直接讀寫記憶體,而不需要任何資料的拷貝。對於像管道和訊息佇列等通訊方式,則需要在核心和使用者空間進行四次的資料拷貝,而共享記憶體則只拷貝兩次資料[1]:一次從輸入檔案到共享記憶體區,另一次從共享記憶體區到輸出檔案。實際上,程序之間在共享記憶體時,並不總是讀寫少量資料後就解除對映,有新的通訊時,再重新建立共享記憶體區域。而是保持共享區域,直到通訊完畢為止,這樣,資料內容一直儲存在共享記憶體中,並沒有寫回檔案。共享記憶體中的內容往往是在解除對映時才寫回檔案的。因此,採用共享記憶體的通訊方式效率是非常高的。
linux的2.2.x核心支援多種共享記憶體方式,如mmap()系統呼叫,posix共享記憶體,以及系統v共享記憶體。linux發行版本如redhat 8.0支援mmap()系統呼叫及系統v共享記憶體,但還沒實現posix共享記憶體,本文將主要介紹mmap()系統呼叫及系統v共享記憶體api的原理及應用。
3、程序呼叫mmap()時,只是在程序空間內新增了一塊相應大小的緩衝區,並設定了相應的訪問標識,但並沒有建立程序空間到物理頁面的對映。因此,第一次訪問該空間時,會引發乙個缺頁異常。
4、對於共享記憶體對映情況,缺頁異常處理程式首先在swap cache中尋找目標頁(符合address_space以及偏移量的物理頁),如果找到,則直接返回位址;如果沒有找到,則判斷該頁是否在交換區(swap area),如果在,則執行乙個換入操作;如果上述兩種情況都不滿足,處理程式將分配新的物理頁面,並把它插入到page cache中。程序最終將更新程序頁表。
注:對於對映普通檔案情況(非共享對映),缺頁異常處理程式首先會在page cache中根據address_space以及資料偏移量尋找相應的頁面。如果沒有找到,則說明檔案資料還沒有讀入記憶體,處理程式會從磁碟讀入相應的頁面,並返回相應位址,同時,程序頁表也會更新。
5、所有程序在對映同乙個共享記憶體區域時,情況都一樣,在建立線性位址與實體地址之間的對映之後,不論程序各自的返回位址如何,實際訪問的必然是同乙個共享記憶體區域對應的物理頁面。
注:乙個共享記憶體區域可以看作是特殊檔案系統shm中的乙個檔案,shm的安裝點在交換區上。
上面涉及到了一些資料結構,圍繞資料結構理解問題會容易一些。
mmap()系統呼叫使得程序之間通過對映同乙個普通檔案實現共享記憶體。普通檔案被對映到程序位址空間後,程序可以向訪問普通記憶體一樣對檔案進行訪問,不必再呼叫read(),write()等操作。
注:實際上,mmap()系統呼叫並不是完全為了用於共享記憶體而設計的。它本身提供了不同於一般對普通檔案的訪問方式,程序可以像讀寫記憶體一樣對普通檔案的操作。而posix或系統v的共享記憶體ipc則純粹用於共享目的,當然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則不推薦使用。offset引數一般設為0,表示從檔案頭開始對映。引數addr指定檔案應被對映到程序空間的起始位址,一般被指定乙個空指標,此時選擇起始位址的任務留給核心來完成。函式的返回值為最後檔案對映到程序空間的位址,程序可直接操作起始位址為該值的有效位址。這裡不再詳細介紹mmap()的引數,讀者可參考mmap()手冊頁獲得進一步的資訊。2
int munmap( void * addr, size_t len )
該呼叫在程序位址空間中解除乙個對映關係,addr是呼叫mmap()時返回的位址,len是對映區的大小。當對映關係解除後,對原來對映位址的訪問將導致段錯誤發生。 3
int msync ( void * addr , size_t len, int flags)
一般說來,程序在對映空間的對共享內容的改變並不直接寫回到磁碟檔案中,往往在呼叫munmap()後才執行該操作。可以通過呼叫msync()實現磁碟上檔案內容與共享記憶體區的內容一致。
4、系統呼叫mprotect()
int mprotect(void *addr,size_t len,int prot)
呼叫mprotect可以更改乙個現存對映儲存區的許可權。prot的許可值與mmap中prot引數一樣。位址引數addr的值必須是系統頁長的整數倍。
5、系統呼叫mmap()用於訪問普通檔案
6、系統呼叫mmap()用於實現共享記憶體的兩種方式
三、對mmap()返回位址的訪問
linux程序間通訊之共享記憶體
共享記憶體塊提供了在任意數量的程序之間進行高效雙向通訊的機制。每個使用者都可以讀取寫入資料,但是所有程式之間必須達成並遵守一定的協議,以防止諸如在讀取資訊之前覆寫記憶體空間等競爭狀態的出現。不幸的是,linux無法嚴格保證提供對共享記憶體塊的獨佔訪問,甚至是在您通過使用ipc private建立新的...
Linux 程序間通訊之共享記憶體
1.什麼是共享記憶體?2.共享記憶體優缺點 優點 採用共享記憶體的乙份好處是效率高,因為程序可以直接讀寫空間,不需要資料拷貝,而訊息佇列與管道都經過了2次拷貝,寫端寫的時候往佇列拷貝乙份,讀端讀的時候又拷貝了乙份。缺點 以前的程序間通訊都自帶同步互斥機制,但共享記憶體並沒有,需要自己加鎖。3.實現 ...
Linux 程序間通訊之共享記憶體
一 共享記憶體的概念 a int a 10 printf d n a b int a 10 printf d n a 這兩個程序中的a是沒有關係的。那麼共享記憶體就是記憶體上開闢的一塊區域,能使得ptra和ptrb都指向這個空間,這樣ptra可以在這個空間寫東西,ptrb去檢視的時候會看到a所寫的東...