進程間通信之共享內存 收藏
共享內存 共享內存是第二種IPC工具,。他允許兩個無關的進程訪問相同的邏輯內存,。共享內存是在兩個運行的程序之間傳遞數(shù)據(jù)的有效手段,。盡管X/Open標準并沒有要求,,很可能絕大數(shù)的共享內存實現(xiàn)都是會將不同進程之間正在共享的內存安排在相同的物理內存中。
共享內存為在多個進程之間共享與傳遞數(shù)據(jù)提供一個有效的手段,。因為他并沒有提供同步的方法,,所以通常我們需要使用其他的機制來同步對共享內存的訪問。通常,,我們也許會使用共享內存來提供對大塊內存區(qū)的有效訪問,,并且傳遞少量的消息來同步對此內存的訪問。
共享內存是由IPC為一個進程所創(chuàng)建并且出現(xiàn)在這個進程的地址空間中的一段特殊的地址序列,。其他的進程可以將同樣的共享內存段關聯(lián)到他們自己的地址空間中,。所有的進程都可以訪問這段內存地址,就如同這段內存是由malloc所分配的,。如果一個進程寫入共享內存,,這些改變立即就可以為訪問相同共享內存的其他進程所見。
就其自身而言,,共享內存并沒有提供任何共享方法,。并沒有自動的方法來阻止在第一個進程完成寫入共享內存之前第二個進程開始讀取共享內存。同步訪問是程序員的責任,。圖14-2顯示共享內存是如何工作的,。
每一個箭頭顯示的是每一個進程的邏輯地址空間到可用的物理內存的映射。實際的情形要更為復雜,,因為可用的內存是由物理內存與交換到磁盤上的內存混合構成的,。
用于共享內存的函數(shù)如下:
#include <sys/shm.h>
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)建共享內存:
int shmget(key_t key, size_t size, int shmflg);
與信號量相類似,,這個函數(shù)也提供了key,,這可以有效的命名共享內存段,而且shmget函數(shù)會返回一個共享內存標識符,,這個標識符可以用于后續(xù)的共享內存函數(shù)中,。還有一個特殊的關鍵值,IPC_PRIVATE,,這可以創(chuàng)建進程私有的共享內存,。我們通常并不會使用這個值,而且與信號量相類似,,我們會發(fā)現(xiàn)私有的共享內存在許多Linux系統(tǒng)上實際上并不是私有的,。
第二個參數(shù),,size,,以字節(jié)形式指定了所需要的內存數(shù)量。
第三個參數(shù),,shmflg,,是由9個權限標記所組成的,這些標記的使用與用于創(chuàng)建文件的模型參數(shù)相同。IPC_CREAT定義了一個特殊位,,必須與權限標記進行位或操作來創(chuàng)建一個新的共享內存段,。設置IPC_CREAT標記并且傳遞一個已經存在的共享內存段并不是錯誤。如果不需要,IPC_CREAT只是簡單的被忽略掉,。
權限標記是十分有用的,因為這些權限標記可以允許創(chuàng)建共享內存所有者進程可以寫入而其他用戶所創(chuàng)建的進程只能讀取的共享內存,。我們可以應用這個特點通過將數(shù)據(jù)放入共享內存中,,從而提供對于只讀數(shù)據(jù)的有效訪問,而不必擔心數(shù)據(jù)被其他用戶修改的風險,。
如果共享內存成功創(chuàng)建,shmget會返回一個非負整數(shù),,共享內存標識符,。如果失敗,,則會返回-1,。
shmat
當我們第一次創(chuàng)建一個共享內存段時,他并不能為任何進程所訪問,。為了能夠訪問共享內存,,我們必須將其與一個進程地址空間關聯(lián)到一起。我們可以使用shmat函數(shù)來達到這一目的:
void *shmat(int shm_id, const void *shm_addr, int shmflg);
第一個參數(shù),,shm_id,,是由shmget函數(shù)所返回的共享內存標識符。
第二個參數(shù),,shm_addr,,是將要關聯(lián)到當前進程的共享內存所在的位置,。這個參數(shù)應總是一個空指針,從而可以允許系統(tǒng)來選擇內存出現(xiàn)的地址,。
第三個參數(shù),,shmflg,,是一個位標記集合,。兩個可能的值為SHM_RND與SHM_RDONLY,。前者與shm_addr聯(lián)合,,控制將被關聯(lián)的共享內存所在的地址;而后者使得關聯(lián)的內存只讀,。通常很少需要來控制被關聯(lián)的內存所在的地址;我們通常應允許系統(tǒng)來為我們選擇一個地址,,否則就會使得程序變得高度硬件相關,。
如果shmat調用成功,他會返回一個指向共享內存第一字節(jié)的指針,。如果失敗,,則會返回-1,。
共享內存將會依據(jù)所有者(共享內存的創(chuàng)建者),,權限與當前進程的所有者而具有讀或寫權限,。共享內存上的權限與文件上的權限相類似。
shmfgl & SHM_RDONLY為真的情況是這個規(guī)則的一個例外,。此時共享內存并不可寫,,盡管權限已經允許了寫訪問。
shmdt
shmdt函數(shù)將共享內存與當前進程相分離,。他傳遞一個指向由shmat所返回的地址的指針,。如果成功,則會返回0,;如果失敗,則會返回-1,。注意,,分離共享內存并不會刪除他;他只是使得內存對于當前進程不可用,。
shmctl
共享內存的控制函數(shù)要比復雜的信號量控制函數(shù)簡單得多:
int shmctl(int shm_id, int command, struct shmid_ds *buf);
shmid_ds結構至少具有下列成員:
struct shmid_ds {
uid_t shm_perm.uid; uid_t shm_perm.gid; mode_t shm_perm.mode; } 第一個參數(shù),,shm_id,是由shmget所返回的標記符,。
第二個參數(shù),,command,是要執(zhí)行的動作,。他可以有三個值:
命令 描述
IPC_STAT 設置shmid_ds結構中的數(shù)據(jù)反射與共享內存相關聯(lián)的值,。 IPC_SET 如果進程有相應的權限,將與共享內存相關聯(lián)的值設置為shmid_ds數(shù)據(jù)結構中所提供的值。 IPC_RMID 刪除共享內存段,。 第三個參數(shù),,buf,是一個指向包含共享內存模式與權限的結構的指針,。
如果成功,,則返回0,如果失敗,,則會返回-1,。X/Open并沒有說明如果我們嘗試刪除一個已經關聯(lián)的共享內存時會發(fā)生什么。通常,,一個已經關聯(lián)但是被刪除的共享內存通常會繼續(xù)發(fā)揮作用,,直到他與最后一個進程相分離。然而,,因為這個行為并沒有被規(guī)范,,所以最好不要依賴于他。
試驗--共享內存
現(xiàn)在我們已經了解了共享內存函數(shù),,我們可以編寫一些代碼來使用這些函數(shù),。我們將會編寫一對程序,shm1.c與shm2.c,。第一個程序(消費者)將會創(chuàng)建一個共享內存段并且顯示寫入共享內存中的數(shù)據(jù),。第二個程序(生產者)將會關聯(lián)已經存在的共享內存段并且允許我們進入內存段中的數(shù)據(jù)。
1 首先,,我們創(chuàng)建一個通用頭文件來描述我們希望傳遞的共享內存,。我們將其命名為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
這個文件定義了在消費者程序與生產者程序中都會用到的結構,。當數(shù)據(jù)已經寫入結構的其他部分并且認為我們需要傳送2k文本時,,我們使用一個int標記written_by_you來通知消費者。
2 我們的第一個程序用于消費者,。在包含頭文件之后,,我們通過調用shmget函數(shù),指定IPC_CREAT位來創(chuàng)建一個共享內存段(我們共享內存結構的大?。?/div>
#include <stdio.h>
#include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/types.h>
#include <sys/irc.h> #include <sys/shm.h> #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)在使用共享內存可以為程序所訪問:
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調用會強制消費者停留在其臨界區(qū)中,,這會使得生產者程序等待。
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 最后共享內存被分離并被刪除:
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,,是生產者程序,;他允許我們進入消費者的數(shù)據(jù)。這個程序與shm1.c程序十分相似:
#include <stdio.h>
#include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/types.h>
#include <sys/ipc.h> #include <sys/shm.h> #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);
} 當我們運行這些程序,,我們會得到下面的輸出:
$ ./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)建共享內存段并其關聯(lián)到他的地址空間,。我們在共享內存的第一部分揭示了shared_use_st結構,。這個結構有一個標記,written_by_you,,當數(shù)據(jù)可用時會設置這個標記,。當設置了這個標記時,,程序會讀取文本,,輸出文本,并且清除標記來表示程序已經讀取數(shù)據(jù)了,。我們使用一個特殊的字符串,,end,來進行由循環(huán)中的退出,。程序然后分離共享內存并且刪除他,。
第二個程序,shm2,,獲得并關聯(lián)共享內存段,,因為他使用相同的鍵值,1234,。然后他提示用戶輸入一些文本,。如果設置了written_by_you標記,shm2就會知道客戶端程序還沒有讀取前面輸入的數(shù)據(jù)并且進行等待,。當其他進程清除了這個標記,,shm2會寫入新的數(shù)據(jù)并且設置標記。他也使用字符串end來結束并分離共享內存段,。
注意,,我們必須提供我們自己的,相當粗糙的同步標記,,written_by_you,,這會導致一個低效的忙等待。在實際的程序中,,我們會傳遞一個消息,,或者使用管道,或者使用IPC消息(我們會在稍后討論),,生成信息,,或是使用信號量來在程序的讀取與寫入部分提供同步,。
發(fā)表于 @ 2009年08月23日 11:51:00 | 評論( 4 ) | 編輯| 舉報| 收藏
舊一篇:Linux進程間通信之信號量 | 新一篇:進程間通信之消息隊列
本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/mylxiaoyi/archive/2009/08/23/4474776.aspx
|
|
來自: Home of heart > 《我的圖書館》