1. 程式人生 > >程序間通訊方式——共享記憶體

程序間通訊方式——共享記憶體

1.什麼是共享記憶體?

共享記憶體就是允許兩個或多個程序共享一定的儲存區。就如同 malloc() 函式向不同程序返回了指向同一個實體記憶體區域的指標。當一個程序改變了這塊地址中的內容的時候,其它程序都會察覺到這個更改。因為資料不需要在客戶機和伺服器端之間複製,資料直接寫到記憶體,不用若干次資料拷貝,所以這是最快的一種IPC。 注:共享記憶體沒有任何的同步與互斥機制,所以要使用訊號量來實現對共享記憶體的存取的同步。

2.與共享記憶體有關的資料結構

system V版本的通訊方式都具有相似的資料結構,參考見: 注:其中的同只是將資料結構中的訊息佇列msg換成shm而已

3.與共享記憶體有關的函式

所有的函式共用標頭檔案
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
3.1建立共享記憶體——>shmget() 函式
int shmget(key_t key, size_t size, int shmflg);
				//成功返回共享記憶體的ID,出錯返回-1        
(1)第一個引數key是長整型(唯一非零),系統建立IPC通訊 ( 訊息佇列、 訊號量和 共享記憶體) 時必須指定一個ID值。通常情況下,該id值通過ftok函式得到,由核心變成識別符號,要想讓兩個程序看到同一個訊號集,只需設定key值不變就可以。
 (2)第二個引數size指定共享記憶體的大小,它的值一般為一頁大小的整數倍(未到一頁,作業系統向上對齊到一頁,但是使用者實際能使用只有自己所申請的大小)。  (3)第三個引數shmflg是一組標誌,建立一個新的共享記憶體,將shmflg 設定了IPC_CREAT標誌後,共享記憶體存在就開啟。而IPC_CREAT | IPC_EXCL則可以建立一個新的,唯一的共享記憶體,如果共享記憶體已存在,返回一個錯誤。一般我們會還或上一個檔案許可權
3.2操作共享記憶體———>shmctl()函式
int shmctl(int shm_id, int cmd, struct shmid_ds *buf); 
				//成功返回0,出錯返回-1

(1)第一個引數,shm_id是shmget函式返回的共享記憶體識別符號。 (2)第二個引數,cmd是要採取的操作,它可以取下面的三個值 :     IPC_STAT:把shmid_ds結構中的資料設定為共享記憶體的當前關聯值,即用共享記憶體的當前關聯值覆蓋shmid_ds的值。     IPC_SET:如果程序有足夠的許可權,就把共享記憶體的當前關聯值設定為shmid_ds結構中給出的值     IPC_RMID:刪除共享記憶體段 (3)第三個引數,buf是一個結構指標,它指向共享記憶體模式和訪問許可權的結構。 shmid_ds結構至少包括以下成員 
struct shmid_ds  
{  
    uid_t shm_perm.uid;  
    uid_t shm_perm.gid;  
    mode_t shm_perm.mode;  
};        

3.3掛接操作———>shmat()函式

建立共享儲存段之後,將程序連線到它的地址空間
void *shmat(int shm_id, const void *shm_addr, int shmflg); 
					//成功返回指向共享儲存段的指標,出錯返回-1

(1)第一個引數,shm_id是由shmget函式返回的共享記憶體標識。

(2)第二個引數,shm_addr指定共享記憶體連線到當前程序中的地址位置,通常為空,表示讓系統來選擇共享記憶體的地址。

(3)第三個引數,shm_flg是一組標誌位,通常為0

3.4分離操作———>shmdt()函式

該操作不從系統中刪除識別符號和其資料結構,要顯示呼叫shmctl(帶命令IPC_RMID)才能刪除它
int shmdt(const void *shmaddr); 
			//成功返回0,出錯返回-1


(1)addr引數是以前呼叫shmat時的返回值

4.模擬實現程序間的通訊方式———>共享記憶體

Server作為傳送方,每次向共享記憶體中,寫入A,Client作為接收方,每次讀取共享記憶體中的資料

comm.h

#ifndef __COMM_H__
#define __COMM_H__
#include <stdio.h>
#include <error.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#define PATHNAME "."
#define PROJ_ID 066
int CreatShmid(int size);
int GetShmid(int size);
int Destory(int shmid);
#endif

comm.c

#include "comm.h"
static int commShmid(int size,int flag)
{
    key_t key=ftok(PATHNAME,PROJ_ID);
    if(key>0)
    {   
        return shmget(key,size,flag);
    }   
    else
    {   
        perror("ftok");
        return -1; 
    }   
}

int CreatShmid(int size)
{
    return commShmid(size,IPC_CREAT|IPC_EXCL|0666);
}
int GetShmid(int size)
{
    return commShmid(size,IPC_CREAT);
}
int Destory(int shmid)
{
    return shmctl(shmid,0,IPC_RMID);
}

server.c

#include "comm.h"
int main()
{
    int shmid=CreatShmid(4097);
    if(shmid>0)
    {   
        int i=0;
        char *addr=shmat(shmid,NULL,0);
        while(i<20)
        {   
            addr[i++]='A';
            addr[i]='\0';
            sleep(1);
        }   
        if(shmdt(addr)==-1)
        {   
            perror("shmat");
            return -3; 
        }   

    }   
   else
    {   
        perror("CreatShmid");
        return -1;
    }
    if(Destory(shmid)<0)
    {
        perror("Destory");
        return -2;
    }
    return 0;
}
 

client.c

#include "comm.h"
int main()
{
    int shmid=GetShmid(4097);
    if(shmid>0)
    {   
        int i=0;
        char *addr=shmat(shmid,NULL,0);
        while(i<20)
        {   
            printf("%s\n",addr);
            sleep(1);
            i++;
        }   
        if(shmdt(addr)==-1)
        {   
            perror("shmdt");
            return-1;
        }   
    }   
    else
    {   
        perror("GetShmid");
      return -2;
    }
    return 0;
}


通過檢視共享記憶體狀態可以知道作業系統提供給使用者的共享記憶體為4097個位元組,實際上作業系統開闢了8K個位元組,而且由於接收方client還沒掛接,所以掛接數為1。 執行結果:

執行結果分析:

共享記憶體中的資料並不會像管道或者訊號量等被一端讀取之後就不存在。

5.使用共享記憶體的優缺點

優點:我們可以看到使用共享記憶體進行程序間的通訊真的是非常方便,而且函式的介面也簡單,資料的共享還使程序間的資料不用傳送,而是直接訪問記憶體,也加快了程式的效率。同時,它也不像匿名管道那樣要求通訊的程序有一定的父子關係(system V版本共有)。

缺點:共享記憶體沒有提供互斥同步的機制,這使得我們在使用共享記憶體進行程序間通訊時,往往要藉助其他的手段比如訊號量等來進行程序間的同步工作。
 
其他程序間通訊方式詳解: