linux為共享記憶體提供了四種操作。
1. 共享記憶體物件的建立或獲得。與其它兩種ipc機制一樣,程序在使用共享記憶體區域以前,必須通過系統呼叫sys_ipc (call值為shmget)建立乙個鍵值為key的共享記憶體物件,或獲得已經存在的鍵值為key的某共享記憶體物件的引用識別符號。以後對共享記憶體物件的訪問都通過該引用識別符號進行。對共享記憶體物件的建立或獲得由函式sys_shmget完成,其定義如下:
int sys_shmget (key_t key, int size, int shm***)
這裡key是表示該共享記憶體物件的鍵值,size是該共享記憶體區域的大小(以位元組為單位),shm***是標誌(對該共享記憶體物件的特殊要求)。
它所做的工作如下:
1) 如果key == ipc_private,則總是會建立乙個新的共享記憶體物件。
但是 (the name choice ipc_private was perhaps unfortunate, ipc_new would more clearly show its function)
* 算出size要占用的頁數,檢查其合法性。
* 申請一塊記憶體用於建立shmid_kernel資料結構,注意這裡申請的記憶體區域大小不包括真正的共享記憶體區,實際上,要等到第乙個程序試圖訪問它的時候才真正建立共享記憶體區。
* 根據該共享記憶體區所占用的頁數,為其申請一塊空間用於建立頁表(每頁4個位元組),將頁表清0。
* 搜尋向量表shm_segs,為新建立的共享記憶體物件找乙個空位置。
* 填寫shmid_kernel資料結構,將其加入到向量表shm_segs中為其找到的空位置。
* 返回該共享記憶體物件的引用識別符號。
2) 在向量表shm_segs中查詢鍵值為key的共享記憶體物件,結果有三:
* 如果沒有找到,而且在操作標誌shm***中沒有指明要建立新共享記憶體,則錯誤返回,否則建立乙個新的共享記憶體物件。
* 如果找到了,但該次操作要求必須建立乙個鍵值為key的新物件,那麼錯誤返回。
* 否則,合法性、認證檢查,如有錯,則錯誤返回;否則,返回該記憶體物件的引用識別符號。
共享記憶體物件的建立者可以控制對於這塊記憶體的訪問許可權和它的key是公開還是私有。如果有足夠的許可權,它也可以把共享記憶體鎖定在物理記憶體中。
參見include/linux/shm.h
2. 關聯。在建立或獲得某個共享記憶體區域的引用識別符號後,還必須將共享記憶體區域對映(粘附)到程序的虛擬位址空間,然後才能使用該共享記憶體區域。系統呼叫 sys_ipc(call值為shmat)用於共享記憶體區到程序虛擬位址空間的對映,而真正完成粘附動作的是函式sys_shmat,
其定義如下:
#include
#include
void *shmat(int shmid, const void *shmaddr, int shm***);
其中:shmid是shmget返回的共享記憶體物件的引用識別符號;
shmaddr用來指定該共享記憶體區域在程序的虛擬位址空間對應的虛擬位址;
shm***是對映標誌;
返回的是在程序中的虛擬位址
該函式所做的工作如下:
1) 根據shmid找到共享記憶體物件。
2) 如果shmaddr為0,即使用者沒有指定該共享記憶體區域在它的虛擬空間中的位置,則由系統在程序的虛擬位址空間中為其找一塊區域(從1g開始);否則,就用shmaddr作為對映的虛擬位址。
(if shmaddr is null, the system chooses a suitable (unused) address a他 which to attach the segment)
3) 檢查虛擬位址的合法性(不能超過程序的最大虛擬空間大小—3g,不能太接近堆疊棧頂)。
4) 認證檢查。
5) 申請一塊記憶體用於建立資料結構vm_area_struct,填寫該結構。
6) 檢查該記憶體區域,將其加入到程序的mm結構和該共享記憶體物件的vm_area_struct佇列中。
共享記憶體的粘附只是建立乙個vm_area_struct資料結構,並將其加入到相應的佇列中,此時並沒有建立真正的共享記憶體頁。
當程序第一次訪問共享虛擬記憶體的某頁時,因為所有的共享記憶體頁還都沒有分配,所以會發生乙個page fault異常。當linux處理這個page fault的時候,它找到發生異常的虛擬位址所在的vm_area_struct資料結構。在該資料結構中包含有這類共享虛擬記憶體的一組處理程式,其中的 nopage操作用來處理虛擬頁對應的物理頁不存在的情況。對共享記憶體,該操作是shm_nopage(定義在ipc/shm.c中)。該操作在描述這個共享記憶體的shmid_kernel資料結構的頁表shm_pages中查詢發生page fault異常的虛擬位址所對應的頁表條目,看共享頁是否存在(頁表條目為0,表示共享頁是第一次使用)。如果不存在,它就分配乙個物理頁,並為它建立乙個頁表條目。這個條目不但進入當前程序的頁表,同時也存到shmid_kernel資料結構的頁表shm_pages中。
當下乙個程序試圖訪問這塊記憶體並得到乙個page fault的時候,經過同樣的路徑,也會走到函式shm_nopage。此時,該函式檢視shmid_kernel資料結構的頁表shm_pages時,發現共享頁已經存在,它只需把這裡的頁表項填到程序頁表的相應位置即可,而不需要重新建立物理頁。所以,是第乙個訪問共享記憶體頁的程序使得這一頁被建立,而隨後訪問它的其它程序僅把此頁加到它們的虛擬位址空間。
3. 分離。當程序不再需要共享虛擬記憶體的時候,它們與之分離(detach)。只要仍舊有其它程序在使用這塊記憶體,這種分離就只會影響當前的程序,而不會影響其它程序。當前程序的vm_area_struct資料結構被從shmid_ds中刪除,並被釋放。當前程序的頁表也被更新,共享記憶體對應的虛擬記憶體頁被標記為無效。當共享這塊記憶體的最後乙個程序與之分離時,共享記憶體頁被釋放,同時,這塊共享記憶體的shmid_kernel資料結構也被釋放。
int sys_shmdt (char *shmaddr)
其中shmaddr是程序要分離的共享頁的開始虛擬位址。
該函式搜尋程序的記憶體結構中的所有vm_area_struct資料結構,找到位址shmaddr對應的乙個,呼叫函式do_munmap將其釋放。
在函式do_munmap中,將要釋放的vm_area_struct資料結構從程序的虛擬記憶體中摘下,清除它在程序頁表中對應的頁表項(可能佔多個頁表項).
如果共享的虛擬記憶體沒有被鎖定在物理記憶體中,分離會更加複雜。因為在這種情況下,共享記憶體的頁可能在系統大量使用記憶體的時候被交換到系統的交換磁碟。為了避免這種情況,可以通過下面的控制操作,將某共享記憶體頁鎖定在物理記憶體不允許向外交換。共享記憶體的換出和換入,已在第3章中討論。
4. 控制。linux在共享記憶體上實現的第四種操作是共享記憶體的控制(call值為shmctl的sys_ipc呼叫),它由函式sys_shmctl實現。控制操作包括獲得共享記憶體物件的狀態,設定共享記憶體物件的引數(如uid、gid、mode、ctime等),將共享記憶體物件在記憶體中鎖定和釋放(在物件的mode上增加或去除shm_locked標誌),釋放共享記憶體物件資源等。
共享記憶體提供了一種快速靈活的機制,它允許程序之間直接共享大量的資料,而無須使用拷貝或系統呼叫。共享記憶體的主要侷限性是它不能提供同步,如果兩個程序企圖修改相同的共享記憶體區域,由於核心不能序列化這些動作,因此寫的資料可能任意地互相混合。所以使用共享記憶體的程序必須設計它們自己的同步協議,如用訊號燈等。
以下是使用共享記憶體機制進行程序間通訊的基本操作:
需要包含的標頭檔案:
#include
#include
#include
1.建立共享記憶體:
int shmget(key_t key,int size,int shm***);
引數說明:
key:用來表示新建或者已經存在的共享記憶體去的關鍵字。
size:建立共享記憶體的大小。
shm***:可以指定的特殊標誌。ipc_create,ipc_excl以及低九位的許可權。
eg:int shmid;
shmid=shmget(ipc_private,4096,ipc_create|ipc_excl|0660);
if(shmid==-1)
perror("shmget()");
2.連線共享記憶體
char *shmat(int shmid,char *shmaddr,int shm***);
引數說明
shmid:共享記憶體的關鍵字
shm***:制定特殊的標誌位。
eg:int shmid;
char *shmp;
shmp=shmat(shmid,0,0);
if(shmp==(char *)(-1))
perror("shmat()\n");
3.使用共享記憶體
在使用共享記憶體是需要注意的是,為防止記憶體訪問衝突,我們一般與訊號量結合使用。
4.分離共享記憶體:當程式不再需要共享內後,我們需要將共享記憶體分離以便對其進行釋放,分離共享記憶體的函式原形如下:
int shmdt(char *shmaddr);
5. 釋放共享記憶體
int shmctl(int shmid,int cmd,struct shmid_ds *buf);
*****************示例**********************
int *__accept_socketfd;
int shmid = shmget(sharememid,sizeof(int),ipc_creat|0666);
if (( __accept_socketfd = (int *)shmat(shmid,null,0 )) == (int *)-1 )
printf("error:shmat\n");
return;
*__accept_socketfd = 0;
共享記憶體 shmget()
shmget include include int shmget key t key,size t size,int shm key t key key標識共享記憶體的鍵值 0 ipc private。當key的取值為ipc private,則函式shmget 將建立一塊新的共享記憶體 如果key...
程序間通訊 共享記憶體(shmget)
資料出處 當然只有mmap是可以的,不過由於各種不同的系統的架構不一樣,後來又經過整合,所以我們現在的linux有多種記憶體共享方案,下面在介紹一種非常常用的系統v記憶體方案。本人首先再在上次的基礎之上介紹乙個 國防科大的仁兄 本人在自己理解的基礎上一步一步去深入 include include i...
程序間通訊 共享記憶體 shmget
介紹這一部分主要從它的幾個函式入手 概念 共享記憶體是在物理記憶體上開闢一塊區域,這段被多個程序對映到自己程序的虛擬位址空間上,這些程序就可以直接訪問該共享記憶體區域,從而通過該區域實現各程序間的通訊。共享記憶體是程序間最快的一種通訊方式,乙個程序向共享記憶體上面寫資料,共享這塊記憶體的所有程序都可...