ipc形式除了管道、fifo、訊號量以外,還有共享記憶體區和訊息佇列。這裡主要堆共享記憶體進行介紹。
共享記憶體區是可用ipc形式中最快的。一旦這樣的記憶體區對映到共享它的程序位址空間,這些程序間資料的傳遞就不再涉及核心。共享記憶體與其他程序通訊方式相比較,不需要複製資料,直接讀寫記憶體,是一種效率非常高的程序通訊方案。但它本身不提供同步訪問機制,需要我們自己控制。在linux中,只要把共享記憶體段連線到程序的位址空間中,這個程序就可以訪問共享記憶體中的位址了。為了實現往該共享記憶體區存放和取出資料的程序間的同步性,防止自己寫入的資料被自己讀出。這就到了我們之前提到的訊號量大顯身手的時候了。
共享記憶體是系統建立的特殊位址空間,允許不相關的多個程序使用這個記憶體空間,即多個程序能夠使用同一塊記憶體中的資料。
linux系統提供的共享記憶體操作函式與訊號量、訊息佇列等類似,主要有以下幾個:
(1)int shmget(key_t key,int shmsz,int shm***);
shmget()函式分配一塊新的共享記憶體。key是指ftok所得到的鍵值。shmsz指明共享記憶體的大小,以位元組為單位,shm***的設定是標誌資訊
如果shmget()函式呼叫成功則返回共享記憶體的id;否則返回-1.
(2)void *shmat(int shmid,const void *shmaddr, int shm***);
shmat()函式的作用是連線共享記憶體與某個程序的位址空間。
shmid是shmget()函式返回的共享記憶體id。shmaddr是共享記憶體連線到程序中的存放位址,一般設定為空指標,表示交由系統完成這個工作。
如果shmaddr為0 則此段連線到由核心選擇的第乙個可用位址上,這是推薦的使用方式
如果shmaddr非零,並且沒有指定shm_rnd,則此段鏈結到addr所指的位址上
如果shmaddr非零且指定shm_rnd,則此段鏈結到shmaddr - (addr mod ulus shmlba)所表示的位址上。shm_rnd的意思是低邊界位址倍數,它總是2的乘方。該算式是將位址向下取最近的乙個shmlba的倍數
shm***設定共享記憶體的控制選項,有兩個可能取值:shm_rnd(與shmaddr引數相關)與shm_rdonly(只允許讀)。如果shmat()函式呼叫成功則返回指向共享記憶體的指標;否則返回-1.
(3)int shmdt (const void *shmaddr);
shmdt()函式用來解除程序與共享記憶體區域的關聯,使當前程序不能繼續訪問共享記憶體。引數shmaddr是shmat()函式返回的指標。如果操作成功則返回0;失敗則返回-1.
(4)int shmctl(int shmid, int cmd,struct shmid_ds *buf);
shmctl()函式實現對共享記憶體區域的控制操作。
cmd:指明所要執行的操作,常用的有以下命令:
ipc_stat:呼叫shmctl函式,執ipc_stat命令,獲取乙個已存在記憶體區的大小
ipc_set :呼叫shmctl函式,執行ipc_set命令,設定乙個已存在記憶體區中的值
ipc_rmid:呼叫shmctl函式,執行ipc_rmid命令,刪出共享記憶體區物件。
下面是乙個用訊號量和共享記憶體聯合一起實現的乙個伺服器與客戶端之間通訊的例子,思路是這樣的伺服器和客戶端根據兩個訊號量進行溝通,訊號量1負責伺服器的寫和客戶端的讀,而訊號量2負責伺服器的讀和客戶端的寫(因為半雙工管道的原因,不能同時控制讀寫)。而讀寫的資訊存放在共享記憶體中,伺服器寫進資料到共享記憶體,並發出訊息給客戶端,客戶端在記憶體中取出資料讀。而訊號量的作用就在於同步控制,根據0阻塞程序的特性,防止伺服器自己寫的資料被自己讀的錯誤發生。
伺服器端**:
ser.cpp
#include "utili.h"
int main(int argc, char *argv)
//用上面建立的鍵值建立一塊大小為1024的共享記憶體。要麼建立要麼返回eexist錯誤,許可權為所數使用者、所陣列、其他使用者可讀可寫。並判斷它是否建立成功
key_t shm_id = shmget(key_id, 1024, ipc_creat|ipc_excl|0666);
if(shm_id == -1)
//在建立的共享記憶體中建立連線,位址由系統分配,並檢測連線是否成功
char* addr = (char *)shmat(shm_id, null, 0);
if(addr == (char *)-1)
//重新建立乙個新的鍵值並判斷是否建立成功
key_t sem_key, sem_id;
sem_key = ftok(argv[1], id1);
if(sem_key == -1)
//用上面建立的鍵值建立兩個訊號量集合,並判斷訊號量集合是否建立成功
sem_id = semget(sem_key, 2, ipc_creat|ipc_excl|0666);
if(sem_id == -1)
//給兩個訊號量都賦初值0
union semun init;
init.val = 0;
semctl(sem_id, 0, setval, init);
semctl(sem_id, 1, setval, init);
//定義兩個sembuf的結構體,用來控制訊號量資源數
struct sembuf p = ;
struct sembuf v = ;
while(1)
//如果輸入資料部為quit,用結果體v對相應的訊號量進行設定。保證資訊可以令客戶端讀到
semop(sem_id, &v, 1);
//伺服器讀資料【此時1號訊號量的值為0,客戶端讀操作處於阻塞狀態】
semop(sem_id, &p, 1);//改變0號訊號量的值,讓伺服器的讀操作處於執行狀態
printf("cli:>%s\n", addr);
//比較客戶端傳來的資料是不是quit,如果是quit則斷開連線,並將值前建立的共享記憶體區和訊號量刪除掉然後退出。
if(strncmp(addr, "quit", 4) == 0)
}return
0;}
客戶端程式:
cli.cpp:
#include "utili.h"
int main(int argc, char * argv)
//利用該鍵值查詢伺服器端所建立的共享記憶體的id號,並判斷是否查詢成功
key_t shm_id = shmget(key_id, 0, 0);
if(shm_id == -1)
//與共享記憶體建立連線,並保證建立連線成功
char *addr = (char *)shmat(shm_id,null,0);
if(addr == (void *)-1)
//重新建立乙個與伺服器中相同的sem_key,用來建立訊號量集
key_t sem_key = ftok(argv[1],id1);
if(sem_key == -1)
//查詢伺服器建立的兩個訊號量集的id號
key_t sem_id = semget(sem_key, 0, 0);
if(sem_id == -1)
//建立兩個sembuf型的結構體,p、v對應操作的訊號量下標量semnum(第乙個引數)應該與伺服器端相反,這樣才能實現同步
struct sembuf p = ;
struct sembuf v = ;
while(1)
//所讀到的資料不為quit,客戶端進行寫資料【此時1號訊號量的值為0,客戶端的讀操作阻塞】
printf("cli:>");
gets(addr);
//如果客戶端傳送的資料為quit,則將0號訊號量的值加1,然後在退出程式,保證伺服器端可以讀到資料,從而斷開連線,防止伺服器端一直等待客戶端的資料
if(strncmp(addr, "quit", 4) == 0)
//如果不為quit,則將0號訊號量的值加1,讓伺服器的讀操作處於執行狀態
semop(sem_id, &v, 1);
}
return
0;}
標頭檔案:
utili.h:
#pragma once
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using
namespace
std;
#define id 0xff
#define id1 0xfe
union semun
;
共享記憶體區
一 什麼是共享記憶體區 共享記憶體區是最快的可用ipc形式。它允許多個不相關的程序去訪問同一部分邏輯記憶體。如果需要在兩個執行中的程序之間傳輸資料,共享記憶體將是一種效率極高的解決方案。一旦這樣的記憶體區對映到共享它的程序的位址空間,這些程序間資料的傳輸就不再涉及核心。這樣就可以減少系統呼叫時間,提...
共享記憶體區
共享記憶體區是最快的ipc形式。一旦這樣的記憶體對映到共享它的程序的位址空間,這些程序間資料傳遞不再涉及到核心,換句話說是程序不再通過執行進入核心的系統呼叫來傳遞彼此的資料。mmap 函式 功能 將檔案或者裝置空間對映到共享記憶體區。原型void mmap void addr,size t len,...
unix共享記憶體要點
共享記憶體優點 1.在程序之間不通過核心傳遞資料,即不通過系統呼叫拷貝資料,達到快速,高效的資料傳輸。2.隨核心持續 nix的共享記憶體有兩套api posix和system v 兩者的主要差別是共享記憶體的大小 1.posix共享記憶體大小可通過函式ftruncate隨時修改 2.system v...