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

程序間通訊之Linux共享記憶體程式設計

共享記憶體

共享記憶體(Shared Memory)是指多個程序共享一段指定的記憶體空間進行資料互動,三種System V IPC機制(另外兩種是訊號量訊息佇列)中共享記憶體是速度最快的一種。

共享記憶體機制是最快的一種程序間通訊(Interprocess Communication,IPC)機制,因為沒有中間介質(如訊息佇列、管道等的延遲),資料直接由記憶體對映到程序空間。通常,共享記憶體段由一個程序建立,接下來的讀寫操作就由多個程序參與,以此達到通訊目的。

現代計算機的工作環境都是保護模式,加上多級頁表的設計使得每一個程序都擁有自己的虛擬地址空間,因此可以讓一段實體記憶體同時分配給不同的程序,這就是共享記憶體機制的實現原理。要實現共享記憶體通訊,就要將同一塊實體記憶體對映到參與通訊的每一個程序各自的虛擬地址空間。共享記憶體只是提供資料的傳送,如何控制伺服器端和客戶端的讀寫操作互斥,還需要其他的輔助工具(例如訊號量)。

採用共享記憶體通訊的一個顯而易見的好處就是效率高,因為程序可以直接讀寫記憶體,而不需要任何緩衝區。對於像管道和訊息佇列等通訊方式,需要在核心和使用者空間進行4次資料拷貝,而共享記憶體則只需要2次:一次從輸入檔案到共享記憶體區,另一次從共享記憶體區到輸出檔案。實際上,在程序之間共享記憶體時並不總是讀寫少量資料後就解除對映,當有新的通訊時,再重新建立共享記憶體,而是保持共享區域,直到通訊完畢。這樣,資料內容就一直儲存在共享記憶體中,直到解除對映時才寫回檔案。

共享記憶體機制的不足在於,需要一定的同步機制控制多個程序對同一塊記憶體的讀寫,當一個程序在寫資料時,不允許其他的程序寫資料或讀資料,這可以通過訊號量的控制實現。

Linux共享記憶體定義

同另外兩種System V IPC機制一樣,每個共享記憶體段都對應一個shmid_ds結構,定義如下:

struct shmid_ds
{
    struct ipc_perm shm_perm;    //指向共享記憶體相對應的ipc_perm結構
    int shm_segsz;               //共享記憶體的大小,單位為bype
    ushort shm_lkcnt;            //共享記憶體被鎖定的時間
    pid_t shm_cpid;              //最近一次呼叫shmop函式的程序的PID
    pid_t shm_lpid;              //建立這個共享記憶體段的程序程序的PID
    ulong shm_nattach;           //當前把這個記憶體段附加到地址空間的程序數
    time_t shm_atime;            //最近一次附加操作的時間
    time_t shm_dtime;            //最近一次分離操作的時間
    time_t shm_ctime;            //最近一次修改的時間
};

共享記憶體的操作

建立或開啟

shmget函式用於建立一塊新的共享記憶體或開啟一塊已經存在的記憶體,其標準呼叫格式如下:

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

int shmget(key_t key, size_t size, int shmflg);

引數key表示共享記憶體識別符號;size表示共享記憶體的大小(單位為位元組);shmflg則表示呼叫函式的操作型別,也可用於設定共享記憶體的訪問許可權,兩者通過邏輯或表示。 函式的具體動作由key和shmflg共同決定,參考訊息佇列訊號量

shmget函式呼叫成功時返回共享記憶體的識別符號;呼叫失敗時返回-1,並設定相應的error值。

 連線共享記憶體

當一個共享記憶體被建立或開啟後,使用該共享記憶體的程序必須將此記憶體區域附加到它的地址空間,Linux提供了shmat函式用於將程序和共享記憶體連線起來。

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

void *shmat(int shmid, const void *shmaddr, int shmflg);

引數shmid為要連線的共享記憶體的識別符號;引數shmflg指明函式的操作方式,如果shmflg設定了SHM_RDONLY位,則該記憶體區域被設定為只讀,否則為可讀寫;引數shmaddr和shmflg共同決定共享記憶體要連線到的地址,詳細說明如下:

·如果引數shmaddr為NULL,系統將自動查詢程序地址空間,將共享記憶體區域連線到第一塊有效記憶體上,此時shmflg無效。

·如果引數shmaddr不為NULL,而引數shmflg未設定SHM_RDONLY位,則共享記憶體連線到由shmaddr指定的地址處。

·如果引數shmaddr不為NULL,且引數shmflg設定了SHM_RDONLY位,則共享記憶體連線到由shmaddr-(shmaddr%SHMLBA)指定的地址處。

函式呼叫成功則返回指向共享記憶體線上程中的地址的指標;呼叫失敗則返回“-1”,同時設定error引數。

脫離共享記憶體

在使用完共享記憶體後,應該呼叫shmdt函式將指定的共享記憶體段從程序空間中脫離出去。

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

int shmdt(const void *shmaddr);

shmdt函式僅將共享記憶體區域程序的地址空間分離,並不刪除共享記憶體。函式呼叫成功返回0,否則返回-1並設定相應的error值。

屬性設定

shmctl用於設定共享記憶體。

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

int shmctl(int shmid, int cmd, struct shmid_ds *buf);

與訊號量的用法相似,共享記憶體通過cmd和shmid_ds的結構體來指明shmctl函式的操作。

引數cmd的說明
cmd的值 含義
IPC_STAT 取shmid所指的共享記憶體段的shmid_ds結構,對引數buf指向的結構賦值
IPC_SET 使用buf指向的結構對shmid段的相關結構賦值,只對以下幾個成員有用:uid、gid以及mode,使用此引數要求程序的uid等於shm_perm.cuid或者shm_perm.uid或者擁有root許可權
IPC_RMID 刪除shmid所指的共享記憶體,只有當shmid_ds結構的shm_nattch為0時才會真正執行刪除
SHM_LOCK 鎖定共享記憶體,此命令只能由超級使用者請求
SHM_UNLOCK 解鎖共享記憶體,此命令只能由超級使用者請求