天天看點

Linux程序間通信---共享記憶體

  • 共享記憶體允許多個程序共享一個給定的記憶體空間,程序可以直接讀寫記憶體,是以是IPC中速度最快的。
  • Linux中,核心專門留出了一塊記憶體區作為共享記憶體區,用于多個程序交換資訊。需要通信的程序将共享記憶體區映射到自己的私有位址空間,進而使讀寫程序位址空間就相當于讀寫記憶體區。使用共享記憶體的頭檔案是#include <sys/shm.h>
  • 由于多個程序讀寫同一塊記憶體區,是以需要進行同步處理,一般要和信号量聯合使用(也可使用互斥量和記錄鎖)。
  • 共享記憶體段預設是32M位元組。
  • 共享記憶體的操作流程(隻使用于相關程序,即親緣程序間的通信):
    • 建立/打開一塊共享記憶體區
    • 把指定的共享記憶體映射到程序的位址空間
    • 撤銷共享記憶體映射
    • 删除共享記憶體對象(key代表的IPC對象)
  • 共享記憶體常用函數:
    • shmget(key, size, flag):建立新的共享記憶體段或者取得已有的共享記憶體段,函數傳回共享存儲段的ID(shmid)。key辨別共享記憶體的鍵值,兩個程序用相同的key時用shmget得到的shmid是相同的,此時可以通路同一塊共享記憶體。size表示獲得的共享記憶體大小。flag表示共享記憶體塊的通路權限,如果想共享記憶體段不存在時建立一個,用IPC_CREAT與權限值做位與操作即可。
    • shmat(shmid, addr, flag):将共享存儲段映射到程序的位址空間。應當制定addr為0,由系統選擇位址(該位址位于堆棧之間)。 flag是一組标志位,一般也為0。調用成功後,傳回指向共享記憶體第一個位元組的指針。
    • shmdt(addr):将程序位址空間與該共享記憶體段分離,使該共享記憶體對目前程序而言不可用。
    • shmctl(shm_id, command, buf):控制共享記憶體。command參數:IPC_RMID(删除共享記憶體段)、IPC_STAT和IPC_SET(不常用)
  • 無關程序(非親緣程序)共享記憶體的方法:
    • 使用XSI共享存儲函數
    • 使用mmap将同一檔案映射到多個程序的位址空間,為此要使用MAP_SHARED标志以保證:一個程序寫到存儲段,另一個程序可見。
  • 使用共享記憶體的優缺點:
    • 優點:友善,接口簡單;資料不用傳送而是直接讀寫記憶體,效率高;沒有無名管道那種親緣程序才能通信的限制,适用于不相關程序通信。
    • 缺點:需要借助外部的同步機制
  • 共享記憶體的使用例子,建立兩個程序,shmwrite向共享記憶體寫資料,shmread從共享記憶體讀資料:
    • shmread程序,建立一塊共享記憶體段,将共享記憶體段映射到自己的記憶體空間,從記憶體中讀資料。
#include <unistd.h>  
#include <stdlib.h>  
#include <stdio.h>  
#include <sys/shm.h>
struct shared_use_st  
{  
    int wirte_read_flag;        //作為一個标志,非0:表示可讀,0表示可寫  
    char text[1024];              //記錄寫入和讀取的文本  
};
int main()  
{  
    int running = 1;    //程式是否繼續運作的标志  
    void *shm = NULL;    //配置設定的共享記憶體的原始首位址  
    struct shared_use_st *shared;   //指向shm  
    int shmid;         //共享記憶體辨別ID号  
    //建立共享記憶體,當key一樣時傳回的shmid也是一樣的,則兩個程序使用同一塊共享記憶體,IPC_CREAT表示建立一塊指定key的共享記憶體塊
    shmid = shmget((key_t)1234, sizeof(struct shared_use_st), 0666|IPC_CREAT);  
    if(shmid == -1)  
    {  
        fprintf(stderr, "shmget failed
");  
        exit(EXIT_FAILURE);  
    }  
    //将共享記憶體連接配接到目前程序的位址空間  
    shm = shmat(shmid, 0, 0);  
    if(shm == (void*)-1)  
    {  
        fprintf(stderr, "shmat failed
");  
        exit(EXIT_FAILURE);  
    }  
    printf("
Memory attached at %X
", (int)shm);  
    //設定共享記憶體  
    shared = (struct shared_use_st*)shm;  
    shared->wirte_read_flag = 0;  //設為可寫
    while(running)   //讀取共享記憶體中的資料  
    {  
        //可讀模式,從程序的位址空間shm裡讀就相當于從共享記憶體裡讀
        if(shared->wirte_read_flag != 0)  
        {  
            printf("Receive Message: %s", shared->text);  
            sleep(rand() % 3);  
            //讀取完資料,設定wirte_read_flag使共享記憶體段可寫  
            shared->wirte_read_flag = 0;  
            //輸入了end,退出循環  
            if(strncmp(shared->text, "end", 3) == 0)  
                running = 0;  
        }  
        else        //有其他程序在寫資料,不能讀取資料  
            sleep(1);  
    }  
    //把共享記憶體從目前程序中分離  
    if(shmdt(shm) == -1)  
    {  
        fprintf(stderr, "shmdt failed
");  
        exit(EXIT_FAILURE);  
    }  
    //删除共享記憶體  
    if(shmctl(shmid, IPC_RMID, 0) == -1)  
    {  
        fprintf(stderr, "shmctl(IPC_RMID) failed
");  
        exit(EXIT_FAILURE);  
    }  
    exit(EXIT_SUCCESS);  
}        
    • shmwrite程序,取得共享記憶體,将共享記憶體映射到自己的記憶體空間,向記憶體中寫資料。
#include <unistd.h>  
#include <stdlib.h>  
#include <stdio.h>  
#include <string.h>  
#include <sys/shm.h>  
struct shared_use_st  
{  
    int wirte_read_flag;        //作為一個标志,非0:表示可讀,0表示可寫  
    char text[1024];              //記錄寫入和讀取的文本  
};
int main()  
{  
    int running = 1;  
    void *shm = NULL;  
    struct shared_use_st *shared = NULL;  
    char buffer[1024 + 1];  //用于儲存輸入的文本  
    int shmid;  
    //建立共享記憶體,當key一樣時,傳回的shmid也是一樣的,則兩個程序通路同一塊共享記憶體,IPC_CREAT表示建立一塊指定key的共享記憶體
    shmid = shmget((key_t)1234, sizeof(struct shared_use_st), 0666|IPC_CREAT);  
    if(shmid == -1)  
    {  
        fprintf(stderr, "shmget failed
");  
        exit(EXIT_FAILURE);  
    }  
    //将共享記憶體連接配接到目前程序的位址空間  
    shm = shmat(shmid, (void*)0, 0);  
    if(shm == (void*)-1)  
    {  
        fprintf(stderr, "shmat failed
");  
        exit(EXIT_FAILURE);  
    }  
    printf("Memory attached at %X
", (int)shm);  
    //設定共享記憶體  
    shared = (struct shared_use_st*)shm;  
    while(running)    //向共享記憶體中寫資料  
    {  
        //資料還沒有被讀取,則等待資料被讀取,不能向共享記憶體寫
        while(shared->wirte_read_flag == 1)  
        {  
            sleep(1);  
            printf("Waiting...
");  
        }  
        //向共享記憶體中寫入資料,向程序的位址空間shm裡寫就相當于往共享記憶體裡寫!  
        printf("Enter some text: ");  
        fgets(buffer, BUFSIZ, stdin);  
        strncpy(shared->text, buffer, 1024);  
        //寫完資料,設定written使共享記憶體段可讀  
        shared->wirte_read_flag = 1;  
        //輸入了end,退出循環  
        if(strncmp(buffer, "end", 3) == 0)  
            running = 0;  
    }  
    //把共享記憶體從目前程序中分離  
    if(shmdt(shm) == -1)  
    {  
        fprintf(stderr, "shmdt failed
");  
        exit(EXIT_FAILURE);  
    }  
    sleep(2);  
    exit(EXIT_SUCCESS);  
}        

(ps:以上程式轉自http://blog.csdn.net/ljianhui/article/details/10253345)