在 C 語言中使用 shmget 來分配共享記憶體
本文將演示關於使用 shmget
函式在 C 語言中分配共享記憶體的多種方法。
使用 shmget
在 C 語言中分配共享記憶體
共享記憶體是程序間通訊的方式之一,它允許兩個或多個程序在使用者空間進行資料交換和快速通訊。共享記憶體意味著多個程序共享記憶體中的同一區域,它們可以根據需要修改/訪問這段記憶體。
在下面的例子中,我們將演示的介面稱為 System V 共享記憶體,它是用四個函式提供的。shmget
、shmat
、shmdt
和 shmctl
。
shmget
用於建立一個新的共享記憶體段或檢索已經建立的記憶體段的識別符號。shmat
呼叫用於將給定的共享記憶體段附加到呼叫程序的記憶體空間。shmdt
可用於分離給定的記憶體段。shmctl
用於修改和重新分配有多個選項的段。
下一個示例程式碼實現了一個程序對 shmget
和 shmat
函式的基本使用,該程序建立了一個新的共享段,然後向其中寫入一些文字。shmget
函式需要三個引數,其中第一個引數是記憶體段的 key。如果需要建立新的段,則鍵值可以是 IPC_PRIVATE
巨集,如果需要通過呼叫獲得記憶體的識別符號,則鍵值可以是現有段的鍵值。shmget
的第二個引數指定段的大小,第三個引數是許可權標誌,可以通過或運算來包含多個值。
一旦建立了記憶體段,就會得到段的識別符號,並可以將其傳遞給 shmat
函式來附加給定的記憶體。使用者可以將給定記憶體段的具體地址作為第二個引數傳遞給 shmat
。不過,通常還是讓核心選擇地址,並指定 NULL
來表示。
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <unistd.h>
#include <wait.h>
enum { SEGMENT_SIZE = 0x6400 };
const char *data = "Hello there!";
int main(int argc, char *argv[]) {
int status;
int segment_id;
segment_id = shmget(IPC_PRIVATE, SEGMENT_SIZE,
IPC_CREAT | IPC_EXCL | S_IRUSR | S_IWUSR);
char *sh_mem = (char *)shmat(segment_id, NULL, 0);
printf("Segment ID %d\n", segment_id);
printf("Attached at %p\n", sh_mem);
memmove(sh_mem, data, strlen(data) + 1);
printf("%s\n", sh_mem);
shmdt(sh_mem);
shmctl(segment_id, IPC_RMID, 0);
exit(EXIT_SUCCESS);
}
輸出:
Segment ID 1540195
Attached at 0x7f6ba1437000
Hello there!
一旦 shmat
函式返回有效的指標,我們就可以把它當作任何記憶體地址,並根據需要對它進行操作。最後,呼叫 shmdt
和 shmctl
函式來分離給定的段並重新分配記憶體。假設 shmctl
不對分配的記憶體段進行呼叫。在這種情況下,在程式終止後,它們會留在系統的記憶體中,而且可能會觸及全系統共享記憶體段總數的限制。
另一方面,接下來的示例程式碼演示了兩個程序如何使用共享記憶體進行互動。該程式碼與上一個例子相同,只是在列印 Hello there!
字串後,主程序被分叉,並建立了一個子程序,該子程序將不同的字串儲存到同一地址中。同時,父程序暫停,等待子程序終止,並以成功程式碼退出;新儲存的字串被列印到控制檯。如果多個程序需要同時修改和訪問共享記憶體段,程式設計師需要採用一些同步工具,比如訊號量。
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <unistd.h>
#include <wait.h>
enum { SEGMENT_SIZE = 0x6400 };
const char *data = "Hello there!";
int main(int argc, char *argv[]) {
int status;
int segment_id;
segment_id = shmget(IPC_PRIVATE, SEGMENT_SIZE,
IPC_CREAT | IPC_EXCL | S_IRUSR | S_IWUSR);
char *sh_mem = (char *)shmat(segment_id, 0, 0);
printf("Segment ID %d\n", segment_id);
printf("Attached at %p\n", sh_mem);
memmove(sh_mem, data, strlen(data) + 1);
printf("%s\n", sh_mem);
pid_t child_pid = fork();
if (child_pid == -1) perror("fork");
if (child_pid == 0) {
strcpy(sh_mem, "NEW DATA Stored by Child Process\0");
printf("child pid - %d\n", getpid());
exit(EXIT_SUCCESS);
} else {
pid_t ret = waitpid(child_pid, &status, WUNTRACED | WCONTINUED);
if (ret == -1) perror("waitpid");
if (WIFEXITED(status))
printf("Child exited, status - %d\n", WEXITSTATUS(status));
if (WEXITSTATUS(status) == 0) printf("%s\n", sh_mem);
}
shmdt(sh_mem);
shmctl(segment_id, IPC_RMID, 0);
exit(EXIT_SUCCESS);
}
輸出:
Segment ID 1540195
Attached at 0x7fd825a25000
Hello there!
child pid - 27291
Child exited, status - 0
NEW DATA Stored by Child Process