?
8.5??共享內(nèi)存
8.5.1??共享內(nèi)存概述
可以說,共享內(nèi)存是一種最為高效的進程間通信方式。因為進程可以直接讀寫內(nèi)存,不需要任何數(shù)據(jù)的復制。為了在多個進程間交換信息,內(nèi)核專門留出了一塊內(nèi)存區(qū)。這段內(nèi)存區(qū)可以由需要訪問的進程將其映射到自己的私有地址空間。因此,進程就可以直接讀寫這一內(nèi)存區(qū)而不需要進行數(shù)據(jù)的復制,從而大大提高了效率。當然,由于多個進程共享一段內(nèi)存,因此也需要依靠某種同步機制,如互斥鎖和信號量等(請參考本章的共享內(nèi)存實驗)。其原理示意圖如圖8.8所示。
圖8.8??共享內(nèi)存原理示意圖
8.5.2??共享內(nèi)存的應用
1.函數(shù)說明
共享內(nèi)存的實現(xiàn)分為兩個步驟,第一步是創(chuàng)建共享內(nèi)存,這里用到的函數(shù)是shmget(),也就是從內(nèi)存中獲得一段共享內(nèi)存區(qū)域,第二步映射共享內(nèi)存,也就是把這段創(chuàng)建的共享內(nèi)存映射到具體的進程空間中,這里使用的函數(shù)是shmat()。到這里,就可以使用這段共享內(nèi)存了,也就是可以使用不帶緩沖的I/O讀寫命令對其進行操作。除此之外,當然還有撤銷映射的操作,其函數(shù)為shmdt()。這里就主要介紹這3個函數(shù)。
2.函數(shù)格式
表8.20列舉了shmget()函數(shù)的語法要點。
表8.20 shmget()函數(shù)語法要點
所需頭文件 |
#include?<sys/types.h> |
函數(shù)原型 |
int?shmget(key_t?key,?int?size,?int?shmflg) |
函數(shù)傳入值 |
key:共享內(nèi)存的鍵值,多個進程可以通過它訪問同一個共享內(nèi)存,其中有個特殊值IPC_PRIVATE。它用于創(chuàng)建當前進程的私有共享內(nèi)存 |
size:共享內(nèi)存區(qū)大小 |
|
shmflg:同open()函數(shù)的權限位,也可以用八進制表示法 |
|
函數(shù)返回值 |
成功:共享內(nèi)存段標識符 |
出錯:-1 |
表8.21列舉了shmat()函數(shù)的語法要點。
表8.21 shmat()函數(shù)語法要點
所需頭文件 |
#include?<sys/types.h> |
|
函數(shù)原型 |
char?*shmat(int?shmid,?const?void?*shmaddr,?int?shmflg) |
|
函數(shù)傳入值 |
shmid:要映射的共享內(nèi)存區(qū)標識符 |
|
shmaddr:將共享內(nèi)存映射到指定地址(若為0則表示系統(tǒng)自動分配地址并把該段共享內(nèi)存映射到調(diào)用進程的地址空間) |
||
shmflg? |
SHM_RDONLY:共享內(nèi)存只讀 |
|
默認0:共享內(nèi)存可讀寫 |
||
函數(shù)返回值 |
成功:被映射的段地址 |
|
出錯:-1 |
表8.22列舉了shmdt()函數(shù)的語法要點。
表8.22 shmdt()函數(shù)語法要點
所需頭文件 |
#include?<sys/types.h> |
函數(shù)原型 |
int?shmdt(const?void?*shmaddr) |
函數(shù)傳入值 |
shmaddr:被映射的共享內(nèi)存段地址 |
函數(shù)返回值 |
成功:0 |
出錯:-1 |
?
3.使用實例
該實例說明如何使用基本的共享內(nèi)存函數(shù)。首先是創(chuàng)建一個共享內(nèi)存區(qū)(采用的共享內(nèi)存的鍵值為IPC_PRIVATE,是因為本實例中創(chuàng)建的共享內(nèi)存是父子進程之間的共用部分),之后創(chuàng)建子進程,在父子兩個進程中將共享內(nèi)存分別映射到各自的進程地址空間之中。
父進程先等待用戶輸入,然后將用戶輸入的字符串寫入到共享內(nèi)存,之后往共享內(nèi)存的頭部寫入“WROTE”字符串表示父進程已成功寫入數(shù)據(jù)。子進程一直等到共享內(nèi)存的頭部字符串為“WROTE”,然后將共享內(nèi)存的有效數(shù)據(jù)(在父進程中用戶輸入的字符串)在屏幕上打印。父子兩個進程在完成以上工作之后,分別解除與共享內(nèi)存的映射關系。
最后在子進程中刪除共享內(nèi)存。因為共享內(nèi)存自身并不提供同步機制,所以應該額外實現(xiàn)不同進程之間的同步(例如:信號量)。為了簡單起見,在本實例中用標志字符串來實現(xiàn)非常簡單的父子進程之間的同步。
這里要介紹的一個命令是ipcs,這是用于報告進程間通信機制狀態(tài)的命令。它可以查看共享內(nèi)存、消息隊列等各種進程間通信機制的情況,這里使用了system()函數(shù)用于調(diào)用shell命令“ipcs”。程序源代碼如下所示:
/*?shmem.c?*/
#include?<sys/types.h>
#include?<sys/ipc.h>
#include?<sys/shm.h>
#include?<stdio.h>
#include?<stdlib.h>
#include?<string.h>
#define?BUFFER_SIZE?2048
int?main()
{
????pid_t?pid;
????int?shmid;
????char?*shm_addr;
????char?flag[]?=?"WROTE";
????char?*buff;
????
????/*?創(chuàng)建共享內(nèi)存?*/
????if?((shmid?=?shmget(IPC_PRIVATE,?BUFFER_SIZE,?0666))?<?0)
????{
????????perror("shmget");
????????exit(1);
????}
????else
????{
????????printf("Create?shared-memory:?%dn",shmid);
????}
????
????/*?顯示共享內(nèi)存情況?*/
????system("ipcs?-m");
????
????pid?=?fork();
????if?(pid?==?-1)
????{
????????perror("fork");
????????exit(1);
????}
????else?if?(pid?==?0)?/*?子進程處理?*/
????{
????????/*映射共享內(nèi)存*/
????????if?((shm_addr?=?shmat(shmid,?0,?0))?==?(void*)-1)
????????{
????????????perror("Child:?shmat");
????????????exit(1);
????????}
????????else
????????{
????????????printf("Child:?Attach?shared-memory:?%pn",?shm_addr);
????????}
????????system("ipcs?-m");
????????
????????/*?通過檢查在共享內(nèi)存的頭部是否標志字符串"WROTE"來確認
父進程已經(jīng)向共享內(nèi)存寫入有效數(shù)據(jù)?*/
????????while?(strncmp(shm_addr,?flag,?strlen(flag)))
????????{
????????????printf("Child:?Wait?for?enable?data...n");
????????????sleep(5);
????????}
????????
????????/*?獲取共享內(nèi)存的有效數(shù)據(jù)并顯示?*/
????????strcpy(buff,?shm_addr?+?strlen(flag));
????????printf("Child:?Shared-memory?:%sn",?buff);
????????
????????/*?解除共享內(nèi)存映射?*/
????????if?((shmdt(shm_addr))?<?0)
????????{
????????????perror("shmdt");
????????????exit(1);
????????}
????????else
????????{
????????????printf("Child:?Deattach?shared-memoryn");
????????}
????????system("ipcs?-m");
??????????
????????/*?刪除共享內(nèi)存?*/
????????if?(shmctl(shmid,?IPC_RMID,?NULL)?==?-1)
????????{
????????????perror("Child:?shmctl(IPC_RMID)n");
????????????exit(1);
????????}
????????else
????????{
????????????printf("Delete?shared-memoryn");
????????}
????????
????????system("ipcs?-m");
????}
????else?/*?父進程處理?*/
????{
????????/*映射共享內(nèi)存*/
????????if?((shm_addr?=?shmat(shmid,?0,?0))?==?(void*)-1)
????????{
????????????perror("Parent:?shmat");
????????????exit(1);
????????}
????????else
????????{
????????????printf("Parent:?Attach?shared-memory:?%pn",?shm_addr);
????????}
????????
????????sleep(1);
????????printf("nInput?some?string:n");
????????fgets(buff,?BUFFER_SIZE,?stdin);
????????strncpy(shm_addr?+?strlen(flag),?buff,?strlen(buff));
????????strncpy(shm_addr,?flag,?strlen(flag));
????????
????????/*?解除共享內(nèi)存映射?*/
????????if?((shmdt(shm_addr))?<?0)
????????{
????????????perror("Parent:?shmdt");
????????????exit(1);
????????}
????????else
????????{
????????????printf("Parent:?Deattach?shared-memoryn");
????????}
????????system("ipcs?-m");
????????
????????waitpid(pid,?NULL,?0);????????
????????printf("Finishedn");
????}
????exit(0);
}
?
下面是運行結果。從該結果可以看出,nattch的值隨著共享內(nèi)存狀態(tài)的變化而變化,共享內(nèi)存的值根據(jù)不同的系統(tǒng)會有所不同。
$?./shmem
Create?shared-memory:?753665
/*?在剛創(chuàng)建共享內(nèi)存時(尚未有任何地址映射)共享內(nèi)存的情況?*/
------?Shared?Memory?Segments?--------
key????????shmid??????owner??????perms??????bytes??????nattch?????status??????
0x00000000?753665?????david?????666????????2048???????0???????????????????????
Child:?Attach?shared-memory:?0xb7f59000?/*?共享內(nèi)存的映射地址?*/
Parent:?Attach?shared-memory:?0xb7f59000
/*?在父子進程中進行共享內(nèi)存的地址映射之后共享內(nèi)存的情況*/
------?Shared?Memory?Segments?--------
key????????shmid??????owner??????perms??????bytes??????nattch?????status??????
0x00000000?753665?????david?????666????????2048???????2???????????????????????
Child:?Wait?for?enable?data...
Input?some?string:
Hello?/*?用戶輸入字符串“Hello”?*/
Parent:?Deattach?shared-memory
/*?在父進程中解除共享內(nèi)存的映射關系之后共享內(nèi)存的情況?*/
------?Shared?Memory?Segments?--------
key????????shmid??????owner??????perms??????bytes??????nattch?????status??????
0x00000000?753665?????david?????666????????2048???????1???????????????????????
/*在子進程中讀取共享內(nèi)存的有效數(shù)據(jù)并打印*/
Child:?Shared-memory?:hello
Child:?Deattach?shared-memory
/*?在子進程中解除共享內(nèi)存的映射關系之后共享內(nèi)存的情況?*/
------?Shared?Memory?Segments?--------
key????????shmid??????owner??????perms??????bytes??????nattch?????status??????
0x00000000?753665?????david?????666????????2048???????0???????????????????????
Delete?shared-memory
/*?在刪除共享內(nèi)存之后共享內(nèi)存的情況?*/
------?Shared?Memory?Segments?--------
key????????shmid??????owner??????perms??????bytes??????nattch?????status??????
Finished