記憶體共享

記憶體共享

記憶體共享指在多處理器的計算機系統中,大容量的記憶體可以被不同中央處理器(CPU)同時訪問。由於多個CPU需要快速訪問存儲器,這樣就要對存儲器進行快取(Cache)。任何一個快取的數據被更新後,由於其他處理器也可能要存取,共享的記憶體就需要立即更新,否則不同的處理器可能用到不同的數據。

共享記憶體

共享記憶體指在多處理器的計算機系統中,可以被不同中央處理器(CPU)訪問的大容量記憶體。由於多個CPU需要快速訪問存儲器,這樣就要對存儲器進行快取
記憶體共享記憶體共享
(Cache)。任何一個快取的數據被更新後,由於其他處理器也可能要存取,共享記憶體就需要立即更新,否則不同的處理器可能用到不同的數據。共享記憶體(sharedmemory)是Unix下的多進程之間的通信方法,這種方法通常用於一個程式的多進程間通信,實際上多個程式間也可以通過共享記憶體來傳遞信息。

共享記憶體的創建

共享記憶體是存在於核心級別的一種資源,在shell中可以使用ipcs命令來查看當前系統IPC中的狀態,在檔案系統/proc目錄下有對其描述的相應檔案。函式shmget可以創建或打開一塊共享記憶體區。函式原型如下:

#include<sys/shm.h>

intshmget(key_tkey,size_tsize,intflag);

函式中參數key用來變換成一個標識符,而且每一個IPC對象與一個key相對應。當新建一個共享記憶體段時,size參數為要請求的記憶體長度(以位元組為單位)。

注意:核心是以頁為單位分配記憶體,當size參數的值不是系統記憶體頁長的整數倍時,系統會分配給進程最小的可以滿足size長的頁數,但是最後一頁的剩餘部分記憶體是不可用的。

當打開一個記憶體段時,參數size的值為0。參數flag中的相應許可權位初始化ipc_perm結構體中的mode域。同時參數flag是函式行為參數,它指定一些當函式遇到阻塞或其他情況時應做出的反應。shmid_ds結構初始化如表14-4所示。

初始化

shmid_ds結構數據 初值shmid_ds結構數據初值
shm_lpid0shm_dtime0
shm_nattach0shm_ctime系統當前值
shm_atime0shm_segsz參數size

 下面實例演示了使用shmget函式創建一塊共享記憶體。程式中在調用shmget函式時指定key參數值為IPC_PRIVATE,這個參數的意義是創建一個新的共享記憶體區,當創建成功後使用shell命令ipcs來顯示目前系統下共享記憶體的狀態。命令參數-m為只顯示共享記憶體的狀態。

(1)在Vi編輯器中編輯該程式如下:

程式清單14-8create_shm.c使用shmget函式創建共享記憶體

#include<sys/types.h>

#include<sys/ipc.h>

#include<sys/shm.h>

#include<stdlib.h>

#include<stdio.h>

#defineBUFSZ4096

intmain(void)

{

intshm_id;/*共享記憶體標識符*/

shm_id=shmget(IPC_PRIVATE,BUFSZ,0666);

if(shm_id<0){/*創建共享記憶體*/

perror("shmget");

exit(1);

}

printf("successfullycreatedsegment:%d\n",shm_id);

system("ipcs-m");/*調用ipcs命令查看IPC*/

exit(0);

}

(2)在shell中編譯該程式如下:

$gcccreate_shm.c–ocreate_shm

(3)在shell中運行該程式如下:

$./create_shm

successfullycreatedsegment:2752516

------SharedMemorySegments-------- 

 keyshmidownerpermsbytesnattchstatus

0x0000000065536root6003932162dest 

 0x000000002654209root66640960

0x0056a4d52686978root6004881

0x0056a4d62719747root6001310721

0x000000002752516root66640960

上述程式中使用shmget函式來創建一段共享記憶體,並在結束前調用了系統shell命令ipcs–m來查看當前系統IPC狀態。

共享記憶體的操作

由於共享記憶體這一特殊的資源類型,使它不同於普通的檔案,因此,系統需要為其提供專有的操作函式,而這無疑增加了程式設計師開發的難度(需要記憶額外的專有函式)。使用函式shmctl可以對共享記憶體段進行多種操作,其函式原型如下:

#include<sys/shm.h>

intshmctl(intshm_id,intcmd,structshmid_ds*buf);

函式中參數shm_id為所要操作的共享記憶體段的標識符,structshmid_ds型指針參數buf的作用與參數cmd的值相關,參數cmd指明了所要進行的操作,其解釋如表14-5所示。

cmd參數詳解

 cmd的值 意義
IPC_STAT取shm_id所指向記憶體共享段的shmid_ds結構,對參數buf指向的結構賦值
IPC_SET

使用buf指向的結構對sh_mid段的相關結構賦值,只對以下幾個域有作用,shm_perm.

uidshm_perm.gid以及shm_perm.mode 

 注意此命令只有具備以下條件的進程才可以請求:

1.進程的用戶ID等於shm_perm.cuid或者等於shm_perm.uid 

 2.超級用戶特權進程

IPC_RMID刪除shm_id所指向的共享記憶體段,只有當shmid_ds結構的shm_nattch域為零時,才會真正執行刪除命令,否則不會刪除該段

注意此命令的請求規則與IPC_SET命令相同

SHM_LOCK鎖定共享記憶體段在記憶體,此命令只能由超級用戶請求
SHM_UNLOCK對共享記憶體段解鎖,此命令只能由超級用戶請求

 使用函式shmat將一個存在的共享記憶體段連線到本進程空間,其函式原型如下:

#include<sys/shm.h>void*shmat(intshm_id,constvoid*addr,intflag);

函式中參數shm_id指定要引入的共享記憶體,參數addr與flag組合說明要引入的地址值,通常只有2種用法,addr為0,表明讓核心來決定第1個可以引入的位置。addr非零,並且flag中指定SHM_RND,則此段引入到addr所指向的位置(此操作不推薦使用,因為不會只對一種硬體上運行應用程式,為了程式的通用性推薦使用第1種方法),在flag參數中可以指定要引入的方式(讀寫方式指定)。

%說明:函式成功執行返回值為實際引入的地址,失敗返回–1。shmat函式成功執行會將shm_id段的shmid_ds結構的shm_nattch計數器的值加1。

當對共享記憶體段操作結束時,應調用shmdt函式,作用是將指定的共享記憶體段從當前進程空間中脫離出去。函式原型如下:

#include<sys/shm.h>

intshmdt(void*addr);

參數addr是調用shmat函式的返回值,函式執行成功返回0,並將該共享記憶體的shmid_ds結構的shm_nattch計數器減1,失敗返回–1。

下面實例演示了操作共享記憶體段的流程。程式的開始部分先檢測用戶是否有輸入,如出錯則列印該命令的使用幫助。接下來從命令行讀取將要引入的共享記憶體ID,使用shmat函式引入該共享記憶體,並在分離該記憶體之前睡眠3秒以方便查看系統IPC狀態。

(1)在vi編輯器中編輯該程式如下:

程式清單14-9opr_shm.c操作共享記憶體段

#include<sys/types.h>

#include<sys/ipc.h>

#include<sys/shm.h>

#include<stdlib.h>

#include<stdio.h>

intmain(intargc,char*argv[])

{

intshm_id;

char*shm_buf;

if(argc!=2){/*命令行參數錯誤*/

printf("USAGE:atshm<identifier>");/*列印幫助訊息*/

exit(1);

}

shm_id=atoi(argv[1]);/*得到要引入的共享記憶體段*/

/*引入共享記憶體段,由核心選擇要引入的位置*/

if((shm_buf=shmat(shm_id,0,0))<(char*)0){

perror("shmat");

exit(1);

}

printf("segmentattachedat%p\n",shm_buf);/*輸出導入的位置*/

system("ipcs-m");

sleep(3);/*休眠*/

if((shmdt(shm_buf))<0){/*與導入的共享記憶體段分離*/

perror("shmdt");

exit(1);

}

printf("segmentdetached\n");

system("ipcs-m");/*再次查看系統IPC狀態*/

exit(0);

}

(2)在shell中編譯該程式如下:

$gccopr_shm.c–oopr_shm

(3)在shell中運行該程式如下:

$./opr_shm2752516

segmentattachedat0xb7f29000

------SharedMemorySegments--------

keyshmidownerpermsbytesnattchstatus

0x0000000065536root6003932162dest

0x000000002654209root66640960

0x0056a4d52686978root6004881

0x0056a4d62719747root6001310721

0x000000002752516root66640961

segmentdetached

------SharedMemorySegments--------

keyshmidownerpermsbytesnattchstatus

0x0000000065536root6003932162dest

0x000000002654209root66640960

0x0056a4d52686978root6004881

0x0056a4d62719747root6001310721

0x000000002752516root66640960

上述程式中從命令行中讀取所要引入的共享記憶體ID,並使用shmat函式引入該記憶體到當前的進程空間中。注意在使用shmat函式時,將參數addr的值設為0,所表達的意義是由核心來決定該共享記憶體在當前進程中的位置。由於在編程的過程中,很少會針對某一個特定的硬體或系統編程,所以由核心決定引入位置也就是shmat推薦的使用方式。在導入後使用shell命令ipcs–m來顯示當前的系統IPC的狀態,可以看出輸出信息中nattch欄位為該共享記憶體時的引用值,最後使用shmdt函式分離該共享記憶體並列印系統IPC的狀態。

共享記憶體使用注意事項

共享記憶體相比其他幾種方式有著更方便的數據控制能力,數據在讀寫過程中會更透明。當成功導入一塊共享記憶體後,它只是相當於一個字元串指針來指向一塊記憶體,在當前進程下用戶可以隨意的訪問。缺點是,數據寫入進程或數據讀出進程中,需要附加的數據結構控制,共享記憶體通信數據結構示意如圖14-9所示。

結構示意

%說明:圖中兩個進程同時遵循一定的規則來讀寫該記憶體。同時,在多進程同步或互斥上也需要附加的代碼來輔助共享記憶體機制。在共享記憶體段中都是以字元串的默認結束符為一條信息的結尾。每個進程在讀寫時都遵守這個規則,就不會破壞數據的完整性。

相關詞條

熱門詞條

聯絡我們