1. 程式人生 > >Linux 程序通訊之:記憶體共享(Shared Memory)

Linux 程序通訊之:記憶體共享(Shared Memory)

一、簡介

共享記憶體允許兩個程序訪問同一塊記憶體區域,它們使用同一個 key 值標記。

二、特點

優點:

通訊方便,兩個程序也是直接訪問同一塊記憶體區域,減少了資料複製的操作,速度上也有明顯優勢。

缺點:

沒有提供同步機制,往往需要我們使用其它(例如訊號)等手段實現程序的同步工作。

三、API 說明

1. 標頭檔案

#include <sys/shm.h>

2. 建立記憶體共享區

int shmget(key_t key, size_t size, int shmflg);
  • key : 一個非零整數,兩個程序需保持一致,即兩個程序之間通訊的鑰匙
  • size : 申請的共享區的大小,單位是位元組。需要是記憶體頁大小的整數倍
  • shmflg : 同 open 函式的 mode 引數,設定檔案訪問許可權,這裡額外多出一個 IPC_CREATIPC_EXCL,可與 mode 進行或操作。IPC_CREAT 表示共享區不存在則建立,IPC_EXCLIPC_CREAT 共同使用,表示共享區已存在則返回錯誤。如 0644 | IPC_CREAT
  • 返回值 : 返回共享區域的 id 值,用於唯一識別該區域

3. 對映記憶體共享區

void *shmat(int shmid, const void *shmaddr,
int shmflg);

at 是 attach 的意思。建立記憶體共享後,還不能被任何程序使用,需要使用該函式啟動對共享記憶體的訪問。

  • shmid : 建立時候返回的 id 值
  • shmaddr : 將共享記憶體對映到指定地址,可以為 NULL,此時系統將自動分配地址
  • shmflg : 同 shmget 函式中的引數,通常為 0
  • 返回值 : 成功執行後,返回該地址的起始地址,失敗返回 -1

4. 撤銷對映

int shmdt(const void *shmaddr);

dt 是 detach 的意思。用於將共享記憶體從該程序中分離,但並不會刪除共享記憶體。

  • shmaddr : shmat 函式返回的地址
  • 返回值 : 成功返回 0,失敗返回 -1,errno 將被設定為相應的值

5. 刪除記憶體共享區

int shmctl(int shmid, int cmd, struct shmid_ds *buf);

ctl 是 control 的意思。該函式用於查詢、更新、刪除共享區域。共享記憶體不使用後必須刪除,以便回收記憶體。

  • shmid : 建立時候返回的 id 值
  • cmd : 控制命令。
    • IPC_STAT 狀態查詢
    • IPC_SET 在許可權允許下,將共享記憶體狀態更新為 buf 中的資料
    • IPC_RMID 刪除共享記憶體
  • 返回值 : 成功返回 0,失敗返回 -1,errno 將被設定為相應的值

四、示例

1. 寫端

#include <sys/shm.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
 
int main() {
    void *shmaddr = NULL;
    const char data[] = "Hello World\n";
    int shmid;
    key_t key = (key_t) 666;
    long page_size = sysconf(_SC_PAGESIZE);
    int data_size =  (strlen(data) + page_size - 1) & (~(page_size - 1));
    printf("data size: %d, page size: %ld\n", data_size, page_size);
 
    // 1. create shared memory
    shmid = shmget(key, data_size, 0644 | IPC_CREAT);
    if (shmid == -1) {
        perror("shmget failed\n");
        exit(EXIT_FAILURE);
    }
 
    // 2. attach shared memory
    shmaddr = shmat(shmid, NULL, 0);
    if (shmaddr == (void *)-1) {
        perror("shmat failed\n");
        exit(EXIT_FAILURE);
    }
 
    // 3. write data to shared memory
    memset(shmaddr, 0, data_size);
    memcpy(shmaddr, &data, strlen(data));
 
    // 4. detach shared memory
    if (shmdt(shmaddr) == -1) {
        perror("shmdt failed\n");
        exit(EXIT_FAILURE);
    }
 
    printf("write done !\n");
    return 0;
}

2. 讀端

#include <sys/shm.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
 
int main() {
    void *shmaddr = NULL;
    const char data[] = "Hello World\n";
    int shmid;
    key_t key = (key_t) 666;
    long page_size = sysconf(_SC_PAGESIZE);
    int data_size =  (strlen(data) + page_size - 1) & (~(page_size - 1));
    printf("data size: %d, page size: %ld\n", data_size, page_size);
 
    // 1. create shared memory
    shmid = shmget(key, data_size, 0644 | IPC_CREAT);
    if (shmid == -1) {
        perror("shmget failed\n");
        exit(EXIT_FAILURE);
    }
 
    // 2. attach shared memory
    shmaddr = shmat(shmid, NULL, 0);
    if (shmaddr == (void *)-1) {
        perror("shmat failed\n");
        exit(EXIT_FAILURE);
    }
 
    // 3. read data to shared memory
    printf("read form shead memory: %s\n", (char *)shmaddr);
 
    // 4. detach shared memory
    if (shmdt(shmaddr) == -1) {
        perror("shmdt failed\n");
        exit(EXIT_FAILURE);
    }
 
    // 5. delete shared memory
    if (shmctl(shmid, IPC_RMID, 0) == -1) {
        perror("shmctl delete shared memory failed\n");
        exit(EXIT_FAILURE);
    }
 
    return 0;
}

3. 執行結果

第一次執行寫程序,往 shared memory 中寫入了 “Hello World”,再執行讀程序,讀出並列印 shared memory 中的內容,並刪除掉共享記憶體。
在這裡插入圖片描述