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

程序間通訊之共享記憶體

一、共享記憶體的定義和原理

1、共享記憶體的定義

  顧名思義,共享記憶體就是允許兩個不相關的程序訪問同一個邏輯記憶體。共享記憶體是在兩個正在執行的程序之間共享和傳遞資料的一種非常有效的方式。不同程序之間共享的記憶體通常安排為同一段實體記憶體程序可以將同一段共享記憶體連線到它們自己的地址空間中,所有程序都可以訪問共享記憶體中的地址,就好像它們是由用C語言函式malloc分配的記憶體一樣。而如果某個程序向共享記憶體寫入資料,所做的改動將立即影響到可以訪問同一段共享記憶體的任何其他程序。   特別提醒:共享記憶體並未提供同步機制,也就是說,在第一個程序結束對共享記憶體的寫操作之前,並無自動機制可以阻止第二個程序開始對它進行讀取。所以我們通常需要用其他的機制來同步對共享記憶體的訪問,例如前面說到的訊號量

2、共享記憶體的原理

1. 呼叫函式shmget()建立一個新共享記憶體段或者取得一個既有的共享記憶體段的識別符號;

2. 呼叫函式shmat()將共享記憶體附加到程序的虛擬地址空間中;

3. 為了引用共享記憶體,程式需要使用由shmat()函式返回的addr值,它是一個指向程序的虛擬地址空間中該共享記憶體段起點的指標;

4. 呼叫函式shmdt()分離共享記憶體段,呼叫之後,程序無法再引用這段共享記憶體。

5. 呼叫函式shmctl()刪除共享記憶體段。只有一個程序需要執行這一步。

二、共享記憶體的使用

1、shmget函式

#include <sys/ipc.h>
#include <sys/shm.h> int shmget(key_t key, size_t size, int shmflg);
  • 功能:建立共享記憶體或者取得一個既有的共享記憶體段的識別符號。
  • 引數
    • key:程式需要提供一個引數key(非0整數),它有效地為共享記憶體段命名;
    • size:size以位元組為單位指定需要共享的記憶體容量,如果正在建立一個新段,則必須指定size。如果正在引用一個現存的段,則size指定為0.當建立一個新段,段內的內容初始化為0;
    • shmflg:許可權標誌,它的作用與open函式的mode引數一樣,如果要想在key標識的共享記憶體不存在時,建立它的話,可以與IPC_CREAT做或操作。共享記憶體的許可權標誌與檔案的讀寫許可權一樣,舉例來說,0644,它表示允許一個程序建立的共享記憶體被記憶體建立者所擁有的程序向共享記憶體讀取和寫入資料,同時其他使用者建立的程序只能讀取共享記憶體。
  • 返回值:成功:返回一個與key相關的共享記憶體識別符號(非負整數),用於後續的共享記憶體函式。失敗:返回-1。

2、shmat函式

#include <sys/types.h>
#include <sys/shm.h>

void *shmat(int shm_id, const void *shm_addr, int shmflg);
  • 功能:啟動對該共享記憶體的訪問,並把共享記憶體附加到當前程序的地址空間。
  • 引數
    • shm_id:由shmget函式返回的共享記憶體標識;
    • shm_addr:指定共享記憶體連線到當前程序中的地址位置,通常為空,表示讓系統來選擇共享記憶體的地址;
    • shm_flg:一組標誌位,通常為0。
  • 返回值:成功:返回一個指向共享記憶體第一個位元組的指標。失敗:返回-1。

3、shmdt函式

#include <sys/types.h>
#include <sys/shm.h>

int shmdt(const void *shmaddr);
  • 功能:將共享記憶體從當前程序中分離。注意,將共享記憶體分離並不是刪除它,只是使該共享記憶體對當前程序不再可用。
  • 引數
    • shmaddr:shmat函式返回的地址指標。
  • 返回值:成功:返回0。失敗:返回-1。

4、shmctl函式

#include <sys/shm.h>

int shmctl(int shm_id, int command, struct shmid_ds *buf);
  • 功能:用來控制共享記憶體。
  • 引數
    • shm_id:shmget函式返回的共享記憶體識別符號;
    • command:要採取的操作,它可以取下面的三個值 :
      • IPC_STAT:把shmid_ds結構中的資料設定為共享記憶體的當前關聯值,即用共享記憶體的當前關聯值覆蓋shmid_ds的值;
      • IPC_SET:如果程序有足夠的許可權,就把共享記憶體的當前關聯值設定為shmid_ds結構中給出的值;
      • IPC_RMID:刪除共享記憶體段。
    • buf:一個結構指標,它指向共享記憶體模式和訪問許可權的結構。
struct shmid_ds
{
    uid_t shm_perm.uid;
    uid_t shm_perm.gid;
    mode_t shm_perm.mode;
};
  • 返回值:成功:返回0。失敗:返回-1。

三、共享記憶體的demo

// ShmWrite.cpp
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/shm.h>
#include <sys/ipc.h>

#define BUFFER 4096
#define PERM S_IRUSR|S_IWUSR

int main(int argc, char *argv[])
{
    // 建立一個新的共享記憶體段或者取得一個已有共享記憶體段的識別符號
    int shmid = shmget((key_t)123456, BUFFER, 
    					PERM | IPC_CREAT | IPC_EXCL);
    if (shmid == -1)
    {
        perror("shmget");
        exit(1);
    }
    // 將共享記憶體段載入到呼叫程序的虛擬地址空間中
    char* shmaddr = (char *)shmat(shmid, NULL, 0);
    if (shmaddr == (void*)-1)
    {
        perror ("shmat");
        exit(1);
    }
    // 從標準輸入讀資料
    fgets(shmaddr, 100, stdin);
    // 分離共享記憶體段
    if (shmdt(shmaddr) == -1)
    {
        perror("shmdt");
        exit(1);
    }
    sleep(10); // 睡眠十秒後共享記憶體失效
    // 刪除這塊共享記憶體
    if (shmctl(shmid, IPC_RMID, NULL) == -1)
    {
        perror("shmctl");
        exit(1);
    }
    return 0;
}
// ShmRead.cpp
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/stat.h>
#include <sys/shm.h>

#define BUFFER 4096
#define PERM S_IRUSR|S_IWUSR

int main(int argc,char **argv)
{
    int shmid = shmget((key_t)123456, BUFFER, PERM);
    if (shmid == -1)
    {
        perror("shmget");
        exit(1);
    }
    char* shmaddr = (char *)shmat(shmid, NULL, 0);
    if (shmaddr == (void*)-1)
    {
        perror("shmat");
        exit(1);
    }
    // 輸出從另一個程序傳過來的資料
    printf("%s\n", shmaddr);
    if (shmdt(shmaddr) == -1)
    {
        perror("shmdt");
        exit(1);
    }
    return 0;
}

四、共享記憶體總結

  共享記憶體就是對映一段能被其他程序所訪問的記憶體,這段共享記憶體由一個程序建立,但多個程序都可以訪問。共享記憶體是最快的IPC(程序間通訊)方式,它是針對其它程序間通訊方式執行效率低而專門設計的。它往往與其他通訊機制,如訊號量,配合使用,來實現程序間的同步與通訊。

【優點】:

  • 使用共享記憶體進行程序間的通訊真的是非常方便,而且函式的介面也簡單,資料的共享還使程序間的資料不用傳送,而是直接訪問記憶體,也加快了程式的效率;
  • 不像匿名管道那樣要求通訊的程序有一定的父子關係,可用於任意兩個程序之間通訊。

【缺點】:

  • 共享記憶體沒有提供同步的機制,這使得我們在使用共享記憶體進行程序間通訊時,往往要藉助其他的手段來進行程序間的同步工作。
  • 利用記憶體緩衝區直接交換資訊,記憶體的實體存在於計算機中,只能同一個計算機系統中的諸多程序共享,不方便網路通訊。