在 C 語言中使用 shmget 來分配共享記憶體

Jinku Hu 2023年10月12日
在 C 語言中使用 shmget 來分配共享記憶體

本文將演示關於使用 shmget 函式在 C 語言中分配共享記憶體的多種方法。

使用 shmget 在 C 語言中分配共享記憶體

共享記憶體是程序間通訊的方式之一,它允許兩個或多個程序在使用者空間進行資料交換和快速通訊。共享記憶體意味著多個程序共享記憶體中的同一區域,它們可以根據需要修改/訪問這段記憶體。

在下面的例子中,我們將演示的介面稱為 System V 共享記憶體,它是用四個函式提供的。shmgetshmatshmdtshmctl

  • shmget 用於建立一個新的共享記憶體段或檢索已經建立的記憶體段的識別符號。
  • shmat 呼叫用於將給定的共享記憶體段附加到呼叫程序的記憶體空間。
  • shmdt 可用於分離給定的記憶體段。
  • shmctl 用於修改和重新分配有多個選項的段。

下一個示例程式碼實現了一個程序對 shmgetshmat 函式的基本使用,該程序建立了一個新的共享段,然後向其中寫入一些文字。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 函式返回有效的指標,我們就可以把它當作任何記憶體地址,並根據需要對它進行操作。最後,呼叫 shmdtshmctl 函式來分離給定的段並重新分配記憶體。假設 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
作者: Jinku Hu
Jinku Hu avatar Jinku Hu avatar

DelftStack.com 創辦人。Jinku 在機器人和汽車行業工作了8多年。他在自動測試、遠端測試及從耐久性測試中創建報告時磨練了自己的程式設計技能。他擁有電氣/ 電子工程背景,但他也擴展了自己的興趣到嵌入式電子、嵌入式程式設計以及前端和後端程式設計。

LinkedIn Facebook

相關文章 - C Memory