共享內(nèi)存通信模式介紹

共享內(nèi)存

顧名思義四康,共享內(nèi)存就是允許兩個(gè)不相關(guān)的進(jìn)程訪問同一片物理內(nèi)存愧膀。共享內(nèi)存是在兩個(gè)正在運(yùn)行的進(jìn)程之間共享和傳遞數(shù)據(jù)的一種非常有效的方式。不同進(jìn)程之間共享的內(nèi)存通常安排為同一段物理內(nèi)存架忌。進(jìn)程可以將同一段共享內(nèi)存連接到它們自己的地址空間中壹店,所有進(jìn)程都可以訪問共享內(nèi)存中的地址,就好像它們是由用C語言函數(shù)malloc分配的內(nèi)存一樣求泰。而如果某個(gè)進(jìn)程向共享內(nèi)存寫入數(shù)據(jù)央渣,所做的改動將立即影響到可以訪問同一段共享內(nèi)存的任何其他進(jìn)程。

特別提醒:共享內(nèi)存并未提供同步機(jī)制渴频,也就是說芽丹,在第一個(gè)進(jìn)程結(jié)束對共享內(nèi)存的寫操作之前,并無自動機(jī)制可以阻止第二個(gè)進(jìn)程開始對它進(jìn)行讀取卜朗。所以我們通常需要用其他的機(jī)制來同步對共享內(nèi)存的訪問

共享內(nèi)存的使用

shmget函數(shù)

該函數(shù)用來創(chuàng)建共享內(nèi)存

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

第一個(gè)參數(shù)拔第,與信號量的semget函數(shù)一樣,程序需要提供一個(gè)參數(shù)key(非0整數(shù))场钉,它有效地為共享內(nèi)存段命名蚊俺,shmget函數(shù)成功時(shí)返回一個(gè)與key相關(guān)的共享內(nèi)存標(biāo)識符(非負(fù)整數(shù)),用于后續(xù)的共享內(nèi)存函數(shù)逛万。調(diào)用失敗返回-1.

不相關(guān)的進(jìn)程可以通過該函數(shù)的返回值訪問同一共享內(nèi)存泳猬,它代表程序可能要使用的某個(gè)資源,程序?qū)λ泄蚕韮?nèi)存的訪問都是間接的宇植,程序先通過調(diào)用shmget函數(shù)并提供一個(gè)鍵得封,再由系統(tǒng)生成一個(gè)相應(yīng)的共享內(nèi)存標(biāo)識符(shmget函數(shù)的返回值)。
第二個(gè)參數(shù)指郁,size以字節(jié)為單位指定需要共享的內(nèi)存容量
第三個(gè)參數(shù)忙上,shmflg是權(quán)限標(biāo)志,它的作用與open函數(shù)的mode參數(shù)一樣闲坎,如果要想在key標(biāo)識的共享內(nèi)存不存在時(shí)疫粥,創(chuàng)建它的話茬斧,可以與IPC_CREAT做或操作。共享內(nèi)存的權(quán)限標(biāo)志與文件的讀寫權(quán)限一樣梗逮,舉例來說项秉,0644,它表示允許一個(gè)進(jìn)程創(chuàng)建的共享內(nèi)存被內(nèi)存創(chuàng)建者所擁有的進(jìn)程向共享內(nèi)存讀取和寫入數(shù)據(jù),同時(shí)其他用戶創(chuàng)建的進(jìn)程只能讀取共享內(nèi)存库糠。

shmat函數(shù)

第一次創(chuàng)建完共享內(nèi)存時(shí)伙狐,它還不能被任何進(jìn)程訪問涮毫,shmat函數(shù)的作用就是用來啟動對該共享內(nèi)存的訪問瞬欧,并把共享內(nèi)存連接到當(dāng)前進(jìn)程的地址空間。它的原型如下:

void *shmat(int shm_id, const void *shm_addr, int shmflg);  

第一個(gè)參數(shù)罢防,shm_id是由shmget函數(shù)返回的共享內(nèi)存標(biāo)識艘虎。
第二個(gè)參數(shù),shm_addr指定共享內(nèi)存連接到當(dāng)前進(jìn)程中的地址位置咒吐,通常為空野建,表示讓系統(tǒng)來選擇共享內(nèi)存的地址
第三個(gè)參數(shù)恬叹,shm_flg是一組標(biāo)志位候生,通常為0。

調(diào)用成功時(shí)返回一個(gè)指向共享內(nèi)存第一個(gè)字節(jié)的指針绽昼,如果調(diào)用失敗返回-1.

shmdt函數(shù)

該函數(shù)用于將共享內(nèi)存從當(dāng)前進(jìn)程中分離唯鸭。注意,將共享內(nèi)存分離并不是刪除它硅确,只是使該共享內(nèi)存對當(dāng)前進(jìn)程不再可用目溉。它的原型如下:

int shmdt(const void *shmaddr);  

參數(shù)shmaddr是shmat函數(shù)返回的地址指針,調(diào)用成功時(shí)返回0菱农,失敗時(shí)返回-1.

4缭付、shmctl函數(shù)
與信號量的semctl函數(shù)一樣,用來控制共享內(nèi)存循未,它的原型如下:

int shmctl(int shm_id, int command, struct shmid_ds *buf);  

第一個(gè)參數(shù)陷猫,shm_id是shmget函數(shù)返回的共享內(nèi)存標(biāo)識符。
第二個(gè)參數(shù)的妖,command是要采取的操作绣檬,它可以取下面的三個(gè)值 :
IPC_STAT:把shmid_ds結(jié)構(gòu)中的數(shù)據(jù)設(shè)置為共享內(nèi)存的當(dāng)前關(guān)聯(lián)值,即用共享內(nèi)存的當(dāng)前關(guān)聯(lián)值覆蓋shmid_ds的值羔味。
IPC_SET:如果進(jìn)程有足夠的權(quán)限河咽,就把共享內(nèi)存的當(dāng)前關(guān)聯(lián)值設(shè)置為shmid_ds結(jié)構(gòu)中給出的值
IPC_RMID:刪除共享內(nèi)存段

第三個(gè)參數(shù),buf是一個(gè)結(jié)構(gòu)指針赋元,它指向共享內(nèi)存模式和訪問權(quán)限的結(jié)構(gòu)忘蟹。
shmid_ds結(jié)構(gòu)至少包括以下成員:

struct shmid_ds  
{  
    uid_t shm_perm.uid;  
    uid_t shm_perm.gid;  
    mode_t shm_perm.mode;  
};  

使用共享內(nèi)存進(jìn)行進(jìn)程間通信

說了這么多飒房,又到了實(shí)戰(zhàn)的時(shí)候了。下面就以兩個(gè)不相關(guān)的進(jìn)程來說明進(jìn)程間如何通過共享內(nèi)存來進(jìn)行通信媚值。其中一個(gè)文件shmread.c創(chuàng)建共享內(nèi)存狠毯,并讀取其中的信息,另一個(gè)文件shmwrite.c向共享內(nèi)存中寫入數(shù)據(jù)褥芒。為了方便操作和數(shù)據(jù)結(jié)構(gòu)的統(tǒng)一嚼松,為這兩個(gè)文件定義了相同的數(shù)據(jù)結(jié)構(gòu),定義在文件shmdata.c中锰扶。結(jié)構(gòu)shared_use_st中的written作為一個(gè)可讀或可寫的標(biāo)志献酗,非0:表示可讀,0表示可寫坷牛,text則是內(nèi)存中的文件罕偎。

shmdata.c的源代碼如下:

#ifndef _SHMDATA_H_HEADER  
#define _SHMDATA_H_HEADER  
#define TEXT_SZ 2048  
  
struct shared_use_st  
{  
    int written;//作為一個(gè)標(biāo)志,非0:表示可讀京闰,0表示可寫  
    char text[TEXT_SZ];//記錄寫入和讀取的文本  
}; 
  
#endif  

源文件shmread.c的源代碼如下:

#include <unistd.h>  
#include <stdlib.h>  
#include <stdio.h>  
#include <sys/shm.h>  
#include "shmdata.h"  
  
int main()  
{  
    int running = 1;//程序是否繼續(xù)運(yùn)行的標(biāo)志  
    void *shm = NULL;//分配的共享內(nèi)存的原始首地址  
    struct shared_use_st *shared;//指向shm  
    int shmid;//共享內(nèi)存標(biāo)識符  
    //創(chuàng)建共享內(nèi)存  
    shmid = shmget((key_t)1234, sizeof(struct shared_use_st), 0666|IPC_CREAT);  
    if(shmid == -1)  
    {  
        fprintf(stderr, "shmget failed\n");  
        exit(EXIT_FAILURE);  
    }  
    //將共享內(nèi)存連接到當(dāng)前進(jìn)程的地址空間  
    shm = shmat(shmid, 0, 0);  
    if(shm == (void*)-1)  
    {  
        fprintf(stderr, "shmat failed\n");  
        exit(EXIT_FAILURE);  
    }  
    printf("\nMemory attached at %X\n", (int)shm);  
    //設(shè)置共享內(nèi)存  
    shared = (struct shared_use_st*)shm;  
    shared->written = 0;  
    while(running)//讀取共享內(nèi)存中的數(shù)據(jù)  
    {  
        //沒有進(jìn)程向共享內(nèi)存定數(shù)據(jù)有數(shù)據(jù)可讀取  
        if(shared->written != 0)  
        {  
            printf("You wrote: %s", shared->text);  
            sleep(rand() % 3);  
            //讀取完數(shù)據(jù)颜及,設(shè)置written使共享內(nèi)存段可寫  
            shared->written = 0;  
            //輸入了end,退出循環(huán)(程序)  
            if(strncmp(shared->text, "end", 3) == 0)  
                running = 0;  
        }  
        else//有其他進(jìn)程在寫數(shù)據(jù)蹂楣,不能讀取數(shù)據(jù)  
            sleep(1);  
    }  
    //把共享內(nèi)存從當(dāng)前進(jìn)程中分離  
    if(shmdt(shm) == -1)  
    {  
        fprintf(stderr, "shmdt failed\n");  
        exit(EXIT_FAILURE);  
    }  
    //刪除共享內(nèi)存  
    if(shmctl(shmid, IPC_RMID, 0) == -1)  
    {  
        fprintf(stderr, "shmctl(IPC_RMID) failed\n");  
        exit(EXIT_FAILURE);  
    }  
    exit(EXIT_SUCCESS);  
}  

源文件shmwrite.c的源代碼如下:

#include <unistd.h>  
#include <stdlib.h>  
#include <stdio.h>  
#include <string.h>  
#include <sys/shm.h>  
#include "shmdata.h"  
  
int main()  
{  
    int running = 1;  
    void *shm = NULL;  
    struct shared_use_st *shared = NULL;  
    char buffer[BUFSIZ + 1];//用于保存輸入的文本  
    int shmid;  
    //創(chuàng)建共享內(nèi)存  
    shmid = shmget((key_t)1234, sizeof(struct shared_use_st), 0666|IPC_CREAT);  
    if(shmid == -1)  
    {  
        fprintf(stderr, "shmget failed\n");  
        exit(EXIT_FAILURE);  
    }  
    //將共享內(nèi)存連接到當(dāng)前進(jìn)程的地址空間  
    shm = shmat(shmid, (void*)0, 0);  
    if(shm == (void*)-1)  
    {  
        fprintf(stderr, "shmat failed\n");  
        exit(EXIT_FAILURE);  
    }  
    printf("Memory attached at %X\n", (int)shm);  
    //設(shè)置共享內(nèi)存  
    shared = (struct shared_use_st*)shm;  
    while(running)//向共享內(nèi)存中寫數(shù)據(jù)  
    {  
        //數(shù)據(jù)還沒有被讀取俏站,則等待數(shù)據(jù)被讀取,不能向共享內(nèi)存中寫入文本  
        while(shared->written == 1)  
        {  
            sleep(1);  
            printf("Waiting...\n");  
        }  
        //向共享內(nèi)存中寫入數(shù)據(jù)  
        printf("Enter some text: ");  
        fgets(buffer, BUFSIZ, stdin);  
        strncpy(shared->text, buffer, TEXT_SZ);  
        //寫完數(shù)據(jù),設(shè)置written使共享內(nèi)存段可讀  
        shared->written = 1;  
        //輸入了end痊土,退出循環(huán)(程序)  
        if(strncmp(buffer, "end", 3) == 0)  
            running = 0;  
    }  
    //把共享內(nèi)存從當(dāng)前進(jìn)程中分離  
    if(shmdt(shm) == -1)  
    {  
        fprintf(stderr, "shmdt failed\n");  
        exit(EXIT_FAILURE);  
    }  
    sleep(2);  
    exit(EXIT_SUCCESS);  
}  

再來看看運(yùn)行的結(jié)果:


![](file:///C:/Users/Administrator/AppData/Local/youdao/ynote/images/C7E9AC0DD98A40C89F66DC1F136EBD7A/QQ%E6%88%AA%E5%9B%BE20130823213636.jpg)

分析:
1肄扎、程序shmread創(chuàng)建共享內(nèi)存,然后將它連接到自己的地址空間施戴。在共享內(nèi)存的開始處使用了一個(gè)結(jié)構(gòu)struct_use_st反浓。該結(jié)構(gòu)中有個(gè)標(biāo)志written,當(dāng)共享內(nèi)存中有其他進(jìn)程向它寫入數(shù)據(jù)時(shí)赞哗,共享內(nèi)存中的written被設(shè)置為0雷则,程序等待。當(dāng)它不為0時(shí)肪笋,表示沒有進(jìn)程對共享內(nèi)存寫入數(shù)據(jù)月劈,程序就從共享內(nèi)存中讀取數(shù)據(jù)并輸出,然后重置設(shè)置共享內(nèi)存中的written為0藤乙,即讓其可被shmwrite進(jìn)程寫入數(shù)據(jù)猜揪。

2、程序shmwrite取得共享內(nèi)存并連接到自己的地址空間中坛梁。檢查共享內(nèi)存中的written而姐,是否為0,若不是划咐,表示共享內(nèi)存中的數(shù)據(jù)還沒有被完拴念,則等待其他進(jìn)程讀取完成钧萍,并提示用戶等待。若共享內(nèi)存的written為0政鼠,表示沒有其他進(jìn)程對共享內(nèi)存進(jìn)行讀取风瘦,則提示用戶輸入文本,并再次設(shè)置共享內(nèi)存中的written為1公般,表示寫完成万搔,其他進(jìn)程可對共享內(nèi)存進(jìn)行讀操作。

四官帘、關(guān)于前面的例子的安全性討論
這個(gè)程序是不安全的瞬雹,當(dāng)有多個(gè)程序同時(shí)向共享內(nèi)存中讀寫數(shù)據(jù)時(shí),問題就會出現(xiàn)遏佣⊥诰妫可能你會認(rèn)為揽浙,可以改變一下written的使用方式状婶,例如,只有當(dāng)written為0時(shí)進(jìn)程才可以向共享內(nèi)存寫入數(shù)據(jù)馅巷,而當(dāng)一個(gè)進(jìn)程只有在written不為0時(shí)才能對其進(jìn)行讀取膛虫,同時(shí)把written進(jìn)行加1操作,讀取完后進(jìn)行減1操作钓猬。這就有點(diǎn)像文件鎖中的讀寫鎖的功能稍刀。咋看之下,它似乎能行得通敞曹。但是這都不是原子操作账月,所以這種做法是行不能的。試想當(dāng)written為0時(shí)澳迫,如果有兩個(gè)進(jìn)程同時(shí)訪問共享內(nèi)存局齿,它們就會發(fā)現(xiàn)written為0,于是兩個(gè)進(jìn)程都對其進(jìn)行寫操作橄登,顯然不行抓歼。當(dāng)written為1時(shí),有兩個(gè)進(jìn)程同時(shí)對共享內(nèi)存進(jìn)行讀操作時(shí)也是如些拢锹,當(dāng)這兩個(gè)進(jìn)程都讀取完是谣妻,written就變成了-1.

要想讓程序安全地執(zhí)行,就要有一種進(jìn)程同步的進(jìn)制卒稳,保證在進(jìn)入臨界區(qū)的操作是原子操作蹋半。例如,可以使用前面所講的信號量來進(jìn)行進(jìn)程的同步充坑。因?yàn)樾盘柫康牟僮鞫际窃有缘?/strong>减江。

使用共享內(nèi)存的優(yōu)缺點(diǎn)

1闻蛀、優(yōu)點(diǎn):我們可以看到使用共享內(nèi)存進(jìn)行進(jìn)程間的通信真的是非常方便,而且函數(shù)的接口也簡單您市,數(shù)據(jù)的共享還使進(jìn)程間的數(shù)據(jù)不用傳送觉痛,而是直接訪問內(nèi)存,也加快了程序的效率茵休。同時(shí)薪棒,它也不像匿名管道那樣要求通信的進(jìn)程有一定的父子關(guān)系。
2榕莺、缺點(diǎn):共享內(nèi)存沒有提供同步的機(jī)制俐芯,這使得我們在使用共享內(nèi)存進(jìn)行進(jìn)程間通信時(shí),往往要借助其他的手段來進(jìn)行進(jìn)程間的同步工作钉鸯。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末吧史,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子唠雕,更是在濱河造成了極大的恐慌贸营,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,602評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件岩睁,死亡現(xiàn)場離奇詭異钞脂,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)捕儒,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評論 2 382
  • 文/潘曉璐 我一進(jìn)店門冰啃,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人刘莹,你說我怎么就攤上這事阎毅。” “怎么了点弯?”我有些...
    開封第一講書人閱讀 152,878評論 0 344
  • 文/不壞的土叔 我叫張陵扇调,是天一觀的道長。 經(jīng)常有香客問我蒲拉,道長肃拜,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,306評論 1 279
  • 正文 為了忘掉前任雌团,我火速辦了婚禮燃领,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘锦援。我一直安慰自己猛蔽,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,330評論 5 373
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著曼库,像睡著了一般区岗。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上毁枯,一...
    開封第一講書人閱讀 49,071評論 1 285
  • 那天慈缔,我揣著相機(jī)與錄音,去河邊找鬼种玛。 笑死藐鹤,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的赂韵。 我是一名探鬼主播娱节,決...
    沈念sama閱讀 38,382評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼祭示!你這毒婦竟也來了肄满?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,006評論 0 259
  • 序言:老撾萬榮一對情侶失蹤质涛,失蹤者是張志新(化名)和其女友劉穎稠歉,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蹂窖,經(jīng)...
    沈念sama閱讀 43,512評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡轧抗,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,965評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了瞬测。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,094評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡纠炮,死狀恐怖月趟,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情恢口,我是刑警寧澤孝宗,帶...
    沈念sama閱讀 33,732評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站耕肩,受9級特大地震影響因妇,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜猿诸,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,283評論 3 307
  • 文/蒙蒙 一婚被、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧梳虽,春花似錦址芯、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,286評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽北专。三九已至,卻和暖如春旬陡,著一層夾襖步出監(jiān)牢的瞬間拓颓,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,512評論 1 262
  • 我被黑心中介騙來泰國打工描孟, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留录粱,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,536評論 2 354
  • 正文 我出身青樓画拾,卻偏偏與公主長得像啥繁,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子青抛,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,828評論 2 345

推薦閱讀更多精彩內(nèi)容