- 論壇徽章:
- 0
|
共享內(nèi)存
共享內(nèi)存是第二種IPC工具。他允許兩個無關(guān)的進(jìn)程訪問相同的邏輯內(nèi)存。共享內(nèi)存是在兩個運行的程序之間傳遞數(shù)據(jù)的有效手段。盡管X/Open標(biāo)準(zhǔn)并沒有要求,很可能絕大數(shù)的共享內(nèi)存實現(xiàn)都是會將不同進(jìn)程之間正在共享的內(nèi)存安排在相同的物理內(nèi)存中。
共享內(nèi)存為在多個進(jìn)程之間共享與傳遞數(shù)據(jù)提供一個有效的手段。因為他并沒有提供同步的方法,所以通常我們需要使用其他的機(jī)制來同步對共享內(nèi)存的訪問。通常,我們也許會使用共享內(nèi)存來提供對大塊內(nèi)存區(qū)的有效訪問,并且傳遞少量的消息來同步對此內(nèi)存的訪問。
共享內(nèi)存是由IPC為一個進(jìn)程所創(chuàng)建并且出現(xiàn)在這個進(jìn)程的地址空間中的一段特殊的地址序列。其他的進(jìn)程可以將同樣的共享內(nèi)存段關(guān)聯(lián)到他們自己的地址空間
中。所有的進(jìn)程都可以訪問這段內(nèi)存地址,就如同這段內(nèi)存是由malloc所分配的。如果一個進(jìn)程寫入共享內(nèi)存,這些改變立即就可以為訪問相同共享內(nèi)存的其
他進(jìn)程所見。
就其自身而言,共享內(nèi)存并沒有提供任何共享方法。并沒有自動的方法來阻止在第一個進(jìn)程完成寫入共享內(nèi)存之前第二個進(jìn)程開始讀取共享內(nèi)存。同步訪問是程序員的責(zé)任。圖14-2顯示共享內(nèi)存是如何工作的。
每一個箭頭顯示的是每一個進(jìn)程的邏輯地址空間到可用的物理內(nèi)存的映射。實際的情形要更為復(fù)雜,因為可用的內(nèi)存是由物理內(nèi)存與交換到磁盤上的內(nèi)存混合構(gòu)成的。
用于共享內(nèi)存的函數(shù)如下:
#include
void *shmat(int shm_id, const void *shm_addr, int shmflg);
int shmctl(int shm_id, int cmd, struct shmid_ds *buf);
int shmdt(const void *shm_addr);
int shmget(key_t key, size_t size, int shmflg);
與信號量相類似,通常需要在包含shm.h文件之前包含sys/types.h與sys/ipc.h這兩個頭文件。
shmget
我們使用shmget函數(shù)創(chuàng)建共享內(nèi)存:
int shmget(key_t key, size_t size, int shmflg);
與信號量相類似,這個函數(shù)也提供了key,這可以有效的命名共享內(nèi)存段,而且shmget函數(shù)會返回一個共享內(nèi)存標(biāo)識符,這個標(biāo)識符可以用于后續(xù)的共享內(nèi)
存函數(shù)中。還有一個特殊的關(guān)鍵值,IPC_PRIVATE,這可以創(chuàng)建進(jìn)程私有的共享內(nèi)存。我們通常并不會使用這個值,而且與信號量相類似,我們會發(fā)現(xiàn)私
有的共享內(nèi)存在許多Linux系統(tǒng)上實際上并不是私有的。
第二個參數(shù),size,以字節(jié)形式指定了所需要的內(nèi)存數(shù)量。
第三個參數(shù),shmflg,是由9個權(quán)限標(biāo)記所組成的,這些標(biāo)記的使用與用于創(chuàng)建文件的模型參數(shù)相同。IPC_CREAT定義了一個特殊位,必須與權(quán)限標(biāo)
記進(jìn)行位或操作來創(chuàng)建一個新的共享內(nèi)存段。設(shè)置IPC_CREAT標(biāo)記并且傳遞一個已經(jīng)存在的共享內(nèi)存段并不是錯誤。如果不需要,IPC_CREAT只是
簡單的被忽略掉。
權(quán)限標(biāo)記是十分有用的,因為這些權(quán)限標(biāo)記可以允許創(chuàng)建共享內(nèi)存所有者進(jìn)程可以寫入而其他用戶所創(chuàng)建的進(jìn)程只能讀取的共享內(nèi)存。我們可以應(yīng)用這個特點通過將數(shù)據(jù)放入共享內(nèi)存中,從而提供對于只讀數(shù)據(jù)的有效訪問,而不必?fù)?dān)心數(shù)據(jù)被其他用戶修改的風(fēng)險。
如果共享內(nèi)存成功創(chuàng)建,shmget會返回一個非負(fù)整數(shù),共享內(nèi)存標(biāo)識符。如果失敗,則會返回-1。
shmat
當(dāng)我們第一次創(chuàng)建一個共享內(nèi)存段時,他并不能為任何進(jìn)程所訪問。為了能夠訪問共享內(nèi)存,我們必須將其與一個進(jìn)程地址空間關(guān)聯(lián)到一起。我們可以使用shmat函數(shù)來達(dá)到這一目的:
void *shmat(int shm_id, const void *shm_addr, int shmflg);
第一個參數(shù),shm_id,是由shmget函數(shù)所返回的共享內(nèi)存標(biāo)識符。
第二個參數(shù),shm_addr,是將要關(guān)聯(lián)到當(dāng)前進(jìn)程的共享內(nèi)存所在的位置。這個參數(shù)應(yīng)總是一個空指針,從而可以允許系統(tǒng)來選擇內(nèi)存出現(xiàn)的地址。
第三個參數(shù),shmflg,是一個位標(biāo)記集合。兩個可能的值為SHM_RND與SHM_RDONLY。前者與shm_addr聯(lián)合,控制將被關(guān)聯(lián)的共享內(nèi)
存所在的地址;而后者使得關(guān)聯(lián)的內(nèi)存只讀。通常很少需要來控制被關(guān)聯(lián)的內(nèi)存所在的地址;我們通常應(yīng)允許系統(tǒng)來為我們選擇一個地址,否則就會使得程序變得高
度硬件相關(guān)。
如果shmat調(diào)用成功,他會返回一個指向共享內(nèi)存第一字節(jié)的指針。如果失敗,則會返回-1。
共享內(nèi)存將會依據(jù)所有者(共享內(nèi)存的創(chuàng)建者),權(quán)限與當(dāng)前進(jìn)程的所有者而具有讀或?qū)憴?quán)限。共享內(nèi)存上的權(quán)限與文件上的權(quán)限相類似。
shmfgl & SHM_RDONLY為真的情況是這個規(guī)則的一個例外。此時共享內(nèi)存并不可寫,盡管權(quán)限已經(jīng)允許了寫訪問。
shmdt
shmdt函數(shù)將共享內(nèi)存與當(dāng)前進(jìn)程相分離。他傳遞一個指向由shmat所返回的地址的指針。如果成功,則會返回0;如果失敗,則會返回-1。注意,分離共享內(nèi)存并不會刪除他;他只是使得內(nèi)存對于當(dāng)前進(jìn)程不可用。
shmctl
共享內(nèi)存的控制函數(shù)要比復(fù)雜的信號量控制函數(shù)簡單得多:
int shmctl(int shm_id, int command, struct shmid_ds *buf);
shmid_ds結(jié)構(gòu)至少具有下列成員:
struct shmid_ds {
uid_t shm_perm.uid;
uid_t shm_perm.gid;
mode_t shm_perm.mode;
}
第一個參數(shù),shm_id,是由shmget所返回的標(biāo)記符。
第二個參數(shù),command,是要執(zhí)行的動作。他可以有三個值:
命令 描述
IPC_STAT 設(shè)置shmid_ds結(jié)構(gòu)中的數(shù)據(jù)反射與共享內(nèi)存相關(guān)聯(lián)的值。
IPC_SET 如果進(jìn)程有相應(yīng)的權(quán)限,將與共享內(nèi)存相關(guān)聯(lián)的值設(shè)置為shmid_ds數(shù)據(jù)結(jié)構(gòu)中所提供的值。
IPC_RMID 刪除共享內(nèi)存段。
第三個參數(shù),buf,是一個指向包含共享內(nèi)存模式與權(quán)限的結(jié)構(gòu)的指針。
如果成功,則返回0,如果失敗,則會返回-1。X/Open并沒有說明如果我們嘗試刪除一個已經(jīng)關(guān)聯(lián)的共享內(nèi)存時會發(fā)生什么。通常,一個已經(jīng)關(guān)聯(lián)但是被刪
除的共享內(nèi)存通常會繼續(xù)發(fā)揮作用,直到他與最后一個進(jìn)程相分離。然而,因為這個行為并沒有被規(guī)范,所以最好不要依賴于他。
試驗--共享內(nèi)存
現(xiàn)在我們已經(jīng)了解了共享內(nèi)存函數(shù),我們可以編寫一些代碼來使用這些函數(shù)。我們將會編寫一對程序,shm1.c與shm2.c。第一個程序(消費者)將會創(chuàng)
建一個共享內(nèi)存段并且顯示寫入共享內(nèi)存中的數(shù)據(jù)。第二個程序(生產(chǎn)者)將會關(guān)聯(lián)已經(jīng)存在的共享內(nèi)存段并且允許我們進(jìn)入內(nèi)存段中的數(shù)據(jù)。
1 首先,我們創(chuàng)建一個通用頭文件來描述我們希望傳遞的共享內(nèi)存。我們將其命名為shm_com.h。
#ifndef _SHM_COM_H
#define _SHM_COM_H 1
#define TEXT_SZ 2048
struct shared_use_at
{
int written_by_you;
char some_text[TEXT_SZ];
};
#endif
這個文件定義了在消費者程序與生產(chǎn)者程序中都會用到的結(jié)構(gòu)。當(dāng)數(shù)據(jù)已經(jīng)寫入結(jié)構(gòu)的其他部分并且認(rèn)為我們需要傳送2k文本時,我們使用一個int標(biāo)記written_by_you來通知消費者。
2 我們的第一個程序用于消費者。在包含頭文件之后,我們通過調(diào)用shmget函數(shù),指定IPC_CREAT位來創(chuàng)建一個共享內(nèi)存段(我們共享內(nèi)存結(jié)構(gòu)的大。
#include
#include
#include
#include
#include
#include
#include
#include "shm_com.h"
int main()
{
int running = 1;
void *shared_memory = (void *)0;
struct shared_use_st *shared_stuff;
int shmid;
srand((unsigned int) getpid());
shmid = shmget((key_t)1234, sizeof(struct shared_use_st), 0666|IPC_CREAT);
if(shmid == -1)
{
fprintf(stderr, "shmget failed\n");
exit(EXIT_FAILURE);
}
3 我們現(xiàn)在使用共享內(nèi)存可以為程序所訪問:
shared_memory = shmat(shmid, (void *)0, 0);
if(shared_memory == (void *)-1)
{
fprintf(stderr, "shmat failed\n");
exit(EXIT_FAILURE);
}
printf("Memory attached at %X\n", (int)shared_memroy);
4
程序的接下來部分將shared_memroy段賦給shared_stuff,后者會輸出written_by_you中的任何文本。程序繼續(xù)循環(huán)直到
written_by_you中的文本為end。sleep調(diào)用會強(qiáng)制消費者停留在其臨界區(qū)中,這會使得生產(chǎn)者程序等待。
shared_stuff = (struct_shared_use_st *)shared_memory;
shared_stuff->written_by_you = 0;
while(running)
{
if(shared_stuff->written_by_you)
{
printf("You wrote: %s", shared_stuff->some_text);
sleep(rand() % 4);
shared_stuff->written_by_you = 0;
if(strncmp(shared_stuff->some_text, "end", 3)==0)
{
running = 0;
}
}
}
5 最后共享內(nèi)存被分離并被刪除:
if(shmdt(shared_memory)==-1)
{
fprintf(stderr, "shmdt failed\n");
exit(EXIT_FAILURE);
}
if(shmctl(shmid, IPC_RMID, 0)==-1)
{
fprintf(stderr, "shmctl(IPC_RMID) failed\n");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
6 我們的第二個程序,shm2.c,是生產(chǎn)者程序;他允許我們進(jìn)入消費者的數(shù)據(jù)。這個程序與shm1.c程序十分相似:
#include
#include
#include
#include
#include
#include
#include
#include "shm_com.h"
int main()
{
int runnint = 1;
void *shared_memory = (void *)0;
struct shared_use_st *shared_stuff;
char buffer[BUFSIZ];
int shmid;
shmid = shmget((key_t)1234, sizeof(struct shared_use_st), 0666 | IPC_CREAT);
if(shmid == -1)
{
fprintf(stderr, "shmget failed\n");
exit(EXIT_FAILURE);
}
shared_memory = shmat(shmid, (void *)0, 0);
if(shared_memory == (void *)-1)
{
fprintf(stderr, "shmat failed\n");
exit(EXIT_FAILURE);
}
printf("Memory attached at %X\n", (int)shared_memory);
shared_stuff = (struct shared_use_st *)shared_memory;
while(running)
{
while(shared_stuff->written_by_you == 1)
{
sleep(1);
printf("waiting for client...\n");
}
printf("Enter some text: ");
fgets(buffer, BUFSIZ, stdin);
strncpy(shared_stuff->some_text, buffer, TEXT_SZ);
shared_stuff->written_by_you = 1;
if(strncmp(buffer, "end", 3) == 0)
{
running = 0;
}
}
if(shmdt(shared_memory) == -1)
{
fprintf(stderr, "shmdt failed\n");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
當(dāng)我們運行這些程序,我們會得到下面的輸出:
$ ./shm1 &
[1] 294
Memory attached at 40017000
$ ./shm2
Memory attached at 40017000
Enter some text: hello
You wrote: hello
waiting for client...
waiting for client...
Enter some text: Linux!
You wrote: Linux!
waiting for client...
waiting for client...
waiting for client...
Enter some text: end
You wrote: end
$
工作原理
第一個程序,shm1,創(chuàng)建共享內(nèi)存段并其關(guān)聯(lián)到他的地址空間。我們在共享內(nèi)存的第一部分揭示了shared_use_st結(jié)構(gòu)。這個結(jié)構(gòu)有一個標(biāo)
記,written_by_you,當(dāng)數(shù)據(jù)可用時會設(shè)置這個標(biāo)記。當(dāng)設(shè)置了這個標(biāo)記時,程序會讀取文本,輸出文本,并且清除標(biāo)記來表示程序已經(jīng)讀取數(shù)據(jù)
了。我們使用一個特殊的字符串,end,來進(jìn)行由循環(huán)中的退出。程序然后分離共享內(nèi)存并且刪除他。
第二個程序,shm2,獲得并關(guān)聯(lián)共享內(nèi)存段,因為他使用相同的鍵值,1234。然后他提示用戶輸入一些文本。如果設(shè)置了written_by_you標(biāo)
記,shm2就會知道客戶端程序還沒有讀取前面輸入的數(shù)據(jù)并且進(jìn)行等待。當(dāng)其他進(jìn)程清除了這個標(biāo)記,shm2會寫入新的數(shù)據(jù)并且設(shè)置標(biāo)記。他也使用字符串
end來結(jié)束并分離共享內(nèi)存段。
注意,我們必須提供我們自己的,相當(dāng)粗糙的同步標(biāo)記,written_by_you,這會導(dǎo)致一個低效的忙等待。在實際的程序中,我們會傳遞一個消息,或者使用管道,或者使用IPC消息(我們會在稍后討論),生成信息,或是使用信號量來在程序的讀取與寫入部分提供同步。
本文來自ChinaUnix博客,如果查看原文請點:http://blog.chinaunix.net/u/19185/showart_2034932.html |
|