
共享記憶體
共享記憶體是兩個或多個程序共享的記憶體。但是,為什麼我們需要共享記憶體或其他一些通訊方式呢?
重申一下,每個程序都有自己的地址空間,如果任何程序想要與其自身地址空間中的某些資訊與其他程序通訊,那麼只有使用 IPC(程序間通訊)技術才能實現。我們已經知道,通訊可以發生在相關程序或不相關程序之間。
通常,相關程序的通訊使用管道或命名管道來執行。不相關程序(例如,一個程序在一個終端執行,另一個程序在另一個終端執行)的通訊可以使用命名管道或透過共享記憶體和訊息佇列等常用的 IPC 技術來執行。
我們已經瞭解了管道和命名管道的 IPC 技術,現在是時候瞭解其餘的 IPC 技術了,即共享記憶體、訊息佇列、訊號量、訊號和記憶體對映。
在本章中,我們將瞭解有關共享記憶體的所有內容。

我們知道,要在兩個或多個程序之間進行通訊,我們使用共享記憶體,但在使用共享記憶體之前,需要對系統呼叫做些什麼,讓我們看看:
建立共享記憶體段或使用已建立的共享記憶體段 (shmget())
將程序附加到已建立的共享記憶體段 (shmat())
將程序從已附加的共享記憶體段分離 (shmdt())
控制共享記憶體段的操作 (shmctl())
讓我們看一下與共享記憶體相關的幾個系統呼叫的細節。
#include <sys/ipc.h> #include <sys/shm.h> int shmget(key_t key, size_t size, int shmflg)
上述系統呼叫建立或分配一個 System V 共享記憶體段。需要傳遞的引數如下:
**第一個引數,key,**識別共享記憶體段。金鑰可以是任意值,也可以是從庫函式 ftok() 派生的值。金鑰也可以是 IPC_PRIVATE,這意味著,執行的程序作為伺服器和客戶端(父子關係),即相關程序通訊。如果客戶端想要使用此金鑰的共享記憶體,則它必須是伺服器的子程序。此外,子程序必須在父程序獲得共享記憶體後建立。
**第二個引數,size,**是共享記憶體段的大小,四捨五入到 PAGE_SIZE 的倍數。
**第三個引數,shmflg,**指定所需的共享記憶體標誌,例如 IPC_CREAT(建立新段)或 IPC_EXCL(與 IPC_CREAT 一起使用以建立新段,如果段已存在,則呼叫失敗)。還需要傳遞許可權。
**注意** - 有關許可權的詳細資訊,請參閱前面幾節。
如果成功,此呼叫將返回一個有效的共享記憶體識別符號(用於後續的共享記憶體呼叫),如果失敗則返回 -1。要了解失敗的原因,請檢查 errno 變數或 perror() 函式。
#include <sys/types.h> #include <sys/shm.h> void * shmat(int shmid, const void *shmaddr, int shmflg)
上述系統呼叫對 System V 共享記憶體段執行共享記憶體操作,即將共享記憶體段附加到呼叫程序的地址空間。需要傳遞的引數如下:
**第一個引數,shmid,**是共享記憶體段的識別符號。此 ID 是共享記憶體識別符號,它是 shmget() 系統呼叫的返回值。
**第二個引數,shmaddr,**用於指定附加地址。如果 shmaddr 為 NULL,則系統預設選擇合適的地址來附加段。如果 shmaddr 不為 NULL 並且在 shmflg 中指定了 SHM_RND,則附加等於 SHMLBA(下邊界地址)的最近倍數的地址。否則,shmaddr 必須是共享記憶體附加/開始的頁面對齊地址。
**第三個引數,shmflg,**指定所需的共享記憶體標誌,例如 SHM_RND(將地址四捨五入到 SHMLBA)或 SHM_EXEC(允許執行段的內容)或 SHM_RDONLY(以只讀方式附加段,預設情況下為讀寫)或 SHM_REMAP(替換 shmaddr 指定的範圍內的現有對映,並繼續到段的末尾)。
如果成功,此呼叫將返回附加共享記憶體段的地址,如果失敗則返回 -1。要了解失敗的原因,請檢查 errno 變數或 perror() 函式。
#include <sys/types.h> #include <sys/shm.h> int shmdt(const void *shmaddr)
上述系統呼叫對 System V 共享記憶體段執行共享記憶體操作,即將共享記憶體段從呼叫程序的地址空間分離。需要傳遞的引數是:
引數 shmaddr 是要分離的共享記憶體段的地址。要分離的段必須是 shmat() 系統呼叫返回的地址。
如果成功,此呼叫將返回 0,如果失敗則返回 -1。要了解失敗的原因,請檢查 errno 變數或 perror() 函式。
#include <sys/ipc.h> #include <sys/shm.h> int shmctl(int shmid, int cmd, struct shmid_ds *buf)
上述系統呼叫對 System V 共享記憶體段執行控制操作。需要傳遞以下引數:
第一個引數 shmid 是共享記憶體段的識別符號。此 ID 是共享記憶體識別符號,它是 shmget() 系統呼叫的返回值。
第二個引數 cmd 是對共享記憶體段執行所需控制操作的命令。
cmd 的有效值為:
**IPC_STAT** - 將 struct shmid_ds 的每個成員的當前值的的資訊複製到 buf 指向的已傳遞結構中。此命令需要對共享記憶體段具有讀取許可權。
**IPC_SET** - 設定所有者使用者的 ID、組的 ID、許可權等,這些由結構 buf 指向。
**IPC_RMID** - 將段標記為要銷燬。只有在最後一個程序分離段後,才會銷燬該段。
**IPC_INFO** - 返回有關共享記憶體限制和引數的資訊,這些資訊包含在 buf 指向的結構中。
**SHM_INFO** - 返回一個 shm_info 結構,其中包含有關共享記憶體消耗的系統資源的資訊。
第三個引數 buf 是指向名為 struct shmid_ds 的共享記憶體結構的指標。此結構的值將根據 cmd 用於設定或獲取。
此呼叫根據傳遞的命令返回相應的值。如果 IPC_INFO 和 SHM_INFO 或 SHM_STAT 成功,則返回共享記憶體段的索引或識別符號,對於其他操作則返回 0,如果失敗則返回 -1。要了解失敗的原因,請檢查 errno 變數或 perror() 函式。
讓我們考慮以下示例程式。
建立兩個程序,一個用於寫入共享記憶體 (shm_write.c),另一個用於從共享記憶體讀取 (shm_read.c)
該程式透過寫入程序 (shm_write.c) 將資料寫入共享記憶體,並透過讀取程序 (shm_read.c) 從共享記憶體讀取資料。
在共享記憶體中,寫入程序建立一個大小為 1K(和標誌)的共享記憶體,並附加共享記憶體。
寫入程序 5 次寫入從 'A' 到 'E' 的字母,每個字母 1023 位元組到共享記憶體中。最後一個位元組表示緩衝區的結尾。
讀取程序將從共享記憶體讀取資料並寫入標準輸出。
讀取和寫入程序的操作同時執行。
寫入完成後,寫入程序更新以指示寫入共享記憶體完成(使用 struct shmseg 中的 complete 變數)。
讀取程序從共享記憶體執行讀取操作並顯示輸出,直到收到寫入程序完成的指示(struct shmseg 中的 complete 變數)。
為了簡化,也為了避免無限迴圈和使程式複雜化,執行幾次讀取和寫入程序的操作。
以下是寫入程序的程式碼(寫入共享記憶體 - 檔案:shm_write.c)
/* Filename: shm_write.c */ #include<stdio.h> #include<sys/ipc.h> #include<sys/shm.h> #include<sys/types.h> #include<string.h> #include<errno.h> #include<stdlib.h> #include<unistd.h> #include<string.h> #define BUF_SIZE 1024 #define SHM_KEY 0x1234 struct shmseg { int cnt; int complete; char buf[BUF_SIZE]; }; int fill_buffer(char * bufptr, int size); int main(int argc, char *argv[]) { int shmid, numtimes; struct shmseg *shmp; char *bufptr; int spaceavailable; shmid = shmget(SHM_KEY, sizeof(struct shmseg), 0644|IPC_CREAT); if (shmid == -1) { perror("Shared memory"); return 1; } // Attach to the segment to get a pointer to it. shmp = shmat(shmid, NULL, 0); if (shmp == (void *) -1) { perror("Shared memory attach"); return 1; } /* Transfer blocks of data from buffer to shared memory */ bufptr = shmp->buf; spaceavailable = BUF_SIZE; for (numtimes = 0; numtimes < 5; numtimes++) { shmp->cnt = fill_buffer(bufptr, spaceavailable); shmp->complete = 0; printf("Writing Process: Shared Memory Write: Wrote %d bytes\n", shmp->cnt); bufptr = shmp->buf; spaceavailable = BUF_SIZE; sleep(3); } printf("Writing Process: Wrote %d times\n", numtimes); shmp->complete = 1; if (shmdt(shmp) == -1) { perror("shmdt"); return 1; } if (shmctl(shmid, IPC_RMID, 0) == -1) { perror("shmctl"); return 1; } printf("Writing Process: Complete\n"); return 0; } int fill_buffer(char * bufptr, int size) { static char ch = 'A'; int filled_count; //printf("size is %d\n", size); memset(bufptr, ch, size - 1); bufptr[size-1] = '\0'; if (ch > 122) ch = 65; if ( (ch >= 65) && (ch <= 122) ) { if ( (ch >= 91) && (ch <= 96) ) { ch = 65; } } filled_count = strlen(bufptr); //printf("buffer count is: %d\n", filled_count); //printf("buffer filled is:%s\n", bufptr); ch++; return filled_count; }
編譯和執行步驟
Writing Process: Shared Memory Write: Wrote 1023 bytes Writing Process: Shared Memory Write: Wrote 1023 bytes Writing Process: Shared Memory Write: Wrote 1023 bytes Writing Process: Shared Memory Write: Wrote 1023 bytes Writing Process: Shared Memory Write: Wrote 1023 bytes Writing Process: Wrote 5 times Writing Process: Complete
以下是讀取程序的程式碼(從共享記憶體讀取並寫入標準輸出 - 檔案:shm_read.c)
/* Filename: shm_read.c */ #include<stdio.h> #include<sys/ipc.h> #include<sys/shm.h> #include<sys/types.h> #include<string.h> #include<errno.h> #include<stdlib.h> #define BUF_SIZE 1024 #define SHM_KEY 0x1234 struct shmseg { int cnt; int complete; char buf[BUF_SIZE]; }; int main(int argc, char *argv[]) { int shmid; struct shmseg *shmp; shmid = shmget(SHM_KEY, sizeof(struct shmseg), 0644|IPC_CREAT); if (shmid == -1) { perror("Shared memory"); return 1; } // Attach to the segment to get a pointer to it. shmp = shmat(shmid, NULL, 0); if (shmp == (void *) -1) { perror("Shared memory attach"); return 1; } /* Transfer blocks of data from shared memory to stdout*/ while (shmp->complete != 1) { printf("segment contains : \n\"%s\"\n", shmp->buf); if (shmp->cnt == -1) { perror("read"); return 1; } printf("Reading Process: Shared Memory: Read %d bytes\n", shmp->cnt); sleep(3); } printf("Reading Process: Reading Done, Detaching Shared Memory\n"); if (shmdt(shmp) == -1) { perror("shmdt"); return 1; } printf("Reading Process: Complete\n"); return 0; }
編譯和執行步驟
segment contains : "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" Reading Process: Shared Memory: Read 1023 bytes segment contains : "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB" Reading Process: Shared Memory: Read 1023 bytes segment contains : "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC" Reading Process: Shared Memory: Read 1023 bytes segment contains : "DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD" Reading Process: Shared Memory: Read 1023 bytes segment contains : "EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE" Reading Process: Shared Memory: Read 1023 bytes Reading Process: Reading Done, Detaching Shared Memory Reading Process: Complete