1. 程式人生 > >Linux系統程式設計——程序間通訊:共享記憶體

Linux系統程式設計——程序間通訊:共享記憶體

概述

共享記憶體是程序間通訊中最簡單的方式之一。共享記憶體允許兩個或更多程序訪問同一塊記憶體,就如同 malloc() 函式向不同程序返回了指向同一個實體記憶體區域的指標。當一個程序改變了這塊地址中的內容的時候,其它程序都會察覺到這個更改。

共享記憶體的特點:

1)共享記憶體是程序間共享資料的一種最快的方法。

 一個程序向共享的記憶體區域寫入了資料,共享這個記憶體區域的所有程序就可以立刻看到其中的內容。

2)使用共享記憶體要注意的是多個程序之間對一個給定儲存區訪問的互斥。

若一個程序正在向共享記憶體區寫資料,則在它做完這一步操作前,別的程序不應當去讀、寫這些資料。

常用函式

1)建立共享記憶體

所需標頭檔案:

#include <sys/ipc.h>

#include <sys/shm.h>

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

功能:建立或開啟一塊共享記憶體區。

引數:

key:程序間通訊鍵值,ftok() 的返回值。

size該共享儲存段的長度(位元組)。

shmflg標識函式的行為及共享記憶體的許可權,其取值如下:

IPC_CREAT如果不存在就建立
IPC_EXCL  如果已經存在則返回失敗
位或許可權位

共享記憶體位或許可權位後可以設定共享記憶體的訪問許可權,格式和 open() 函式的 mode_t 一樣(open() 的使用請點此連結),但可執行許可權未使用。

返回值:

成功:共享記憶體識別符號。

失敗:-1。

示例程式碼如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
 
#define BUFSZ 1024
 
int main(int argc, char *argv[])
{
	int shmid;
	key_t key;
	
	key = ftok("./", 2015); 
	if(key == -1)
	{
		perror("ftok");
	}
	
	//建立共享記憶體
	shmid = shmget(key, BUFSZ, IPC_CREAT|0666);	
	if(shmid < 0) 
	{ 
		perror("shmget"); 
		exit(-1); 
	} 
 
	return 0;
}

執行結果如下:

 

2)共享記憶體對映

所需標頭檔案:

#include <sys/types.h>

#include <sys/shm.h>

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

功能:

將一個共享記憶體段對映到呼叫程序的資料段中。簡單來理解,讓程序和共享記憶體建立一種聯絡,讓程序某個指標指向此共享記憶體

引數:

shmid共享記憶體識別符號,shmget() 的返回值。

shmaddr共享記憶體對映地址(若為 NULL 則由系統自動指定),推薦使用 NULL

shmflg共享記憶體段的訪問許可權和對映條件( 通常為 0 ),具體取值如下:

0共享記憶體具有可讀可寫許可權。
SHM_RDONLY只讀。
SHM_RND(shmaddr 非空時才有效)

返回值:

成功:共享記憶體段對映地址( 相當於這個指標就指向此共享記憶體 )
失敗:-1

3)解除共享記憶體對映

所需標頭檔案:

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

int shmdt(const void *shmaddr);

功能:

將共享記憶體和當前程序分離( 僅僅是斷開聯絡並不刪除共享記憶體,相當於讓之前的指向此共享記憶體的指標,不再指向)。

引數:

shmaddr共享記憶體對映地址。

返回值:

成功:0

失敗:-1

4)共享記憶體控制

所需的標頭檔案:

#include <sys/ipc.h>

#include <sys/shm.h>

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

功能: 共享記憶體屬性的控制。

引數:

shmid共享記憶體識別符號。

cmd函式功能的控制,其取值如下:

IPC_RMID:刪除。(常用 )
IPC_SET:設定 shmid_ds 引數,相當於把共享記憶體原來的屬性值替換為 buf 裡的屬性值。
IPC_STAT:儲存 shmid_ds 引數,把共享記憶體原來的屬性值備份到 buf 裡。
SHM_LOCK:鎖定共享記憶體段( 超級使用者 )。
SHM_UNLOCK:解鎖共享記憶體段。

SHM_LOCK 用於鎖定記憶體,禁止記憶體交換。並不代表共享記憶體被鎖定後禁止其它程序訪問。其真正的意義是:被鎖定的記憶體不允許被交換到虛擬記憶體中。這樣做的優勢在於讓共享記憶體一直處於記憶體中,從而提高程式效能。

bufshmid_ds 資料型別的地址,用來存放或修改共享記憶體的屬性。

返回值:

成功:0

失敗:-1

實戰示例

接下來我們做這麼一個例子:建立兩個程序,在 A 程序中建立一個共享記憶體,並向其寫入資料,通過 B 程序從共享記憶體中讀取資料。

寫端程式碼如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
 
#define BUFSZ 512
 
int main(int argc, char *argv[])
{
	int shmid;
	int ret;
	key_t key;
	char *shmadd;
	
	//建立key值
	key = ftok("../", 2015); 
	if(key == -1)
	{
		perror("ftok");
	}
	
	//建立共享記憶體
	shmid = shmget(key, BUFSZ, IPC_CREAT|0666);	
	if(shmid < 0) 
	{ 
		perror("shmget"); 
		exit(-1); 
	}
	
	//對映
	shmadd = shmat(shmid, NULL, 0);
	if(shmadd < 0)
	{
		perror("shmat");
		_exit(-1);
	}
	
	//拷貝資料至共享記憶體區
	printf("copy data to shared-memory\n");
	bzero(shmadd, BUFSZ); // 共享記憶體清空
	strcpy(shmadd, "how are you, mike\n");
	
	return 0;
}

讀端程式碼如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
 
#define BUFSZ 512
 
int main(int argc, char *argv[])
{
	int shmid;
	int ret;
	key_t key;
	char *shmadd;
	
	//建立key值
	key = ftok("../", 2015); 
	if(key == -1)
	{
		perror("ftok");
	}
	
	system("ipcs -m"); //檢視共享記憶體
	
	//開啟共享記憶體
	shmid = shmget(key, BUFSZ, IPC_CREAT|0666);
	if(shmid < 0) 
	{ 
		perror("shmget"); 
		exit(-1); 
	} 
	
	//對映
	shmadd = shmat(shmid, NULL, 0);
	if(shmadd < 0)
	{
		perror("shmat");
		exit(-1);
	}
	
	//讀共享記憶體區資料
	printf("data = [%s]\n", shmadd);
	
	//分離共享記憶體和當前程序
	ret = shmdt(shmadd);
	if(ret < 0)
	{
		perror("shmdt");
		exit(1);
	}
	else
	{
		printf("deleted shared-memory\n");
	}
	
	//刪除共享記憶體
	shmctl(shmid, IPC_RMID, NULL);
	
	system("ipcs -m"); //檢視共享記憶體
	
	return 0;
}

執行結果如下: