shmget 是如何分配内存的?无法使用线性寻址访问(地址边界错误)

How does shmget allocate memory? Cannot access with linear addressing (Address Boundary error)

在下面的示例中,我尝试使用 shmget 一个整数和 10 个 foo 结构 分配内存并尝试访问他们线性。但是,它会出现 "Address Boundary error".

错误

对于 MacOS 系统(但在 Linux 上应该是相同的),我尝试分配两个数据结构应该占用的确切内存量,并尝试对它们进行线性字节寻址。

#include <stdio.h>
#include <sys/shm.h>

typedef struct {
  int f1;
  int f2;
} foo;

int main() {
  // Size of memory. Consider, int size to be 4. (4 + 8 * 10 = 84 bytes)
  const int shmsz = sizeof(int) + sizeof(foo) * 10; 

  // Create shared mem id.
  const int shmid = shmget(IPC_PRIVATE, shmsz, 0666);
  if (shmid < 0) {
    perror("shmget failed.");
    return 1;
  }

  // Pointer to the shared memory. Cast as char* for byte addressing.
  char* shmemPtr = (char*) shmat(shmid, 0, 0);
  if (*shmemPtr == -1) {
    perror("shmat failed.");
    return 1;
  }

  // The integer should point to the first 4 bytes. This is the same as
  // the address pointed to by shmemPtr itself.
  int* iPtr = (int*) shmemPtr[0];

  // The 80 bytes for the 10 foos range from index 4 -> 83
  foo* fPtr = (foo*) ((void*) shmemPtr[sizeof(int)]);

  printf("i: %p\n", iPtr); // why is this 0x0 ?
  printf("x: %p\n", fPtr); // why is this 0x0 ?

  *iPtr = 0; // <-- This dereference crashes, probably since the address of iPtr is 0x0
}

分配内存并通过 shmat 接收指向它的指针后,我尝试创建的指向已分配内存的任何指针都是 0x0 并且任何取消引用都会(理所当然地)使程序崩溃。我希望 int*foo* 是指向共享内存的有效指针。

我只是用 C/C++ 涉猎一些系统的东西所以如果我在这里遗漏任何东西请原谅我。

char* shmemPtr = (char*) shmat(shmid, 0, 0);

你现在有一个指向共享内存块的指针。它是一个 char *,指向块的开始。到目前为止,还不错。

int* iPtr = (int*) shmemPtr[0];

因为 shmemPtr 是一个 char *shmemPtr[0] 是它的第一个字节。上面莫名其妙地将一个字节转换成一个int *。这当然没有意义。

您显然打算将 shmemPtr 重新解释为指向您的 int 的指针,因此您可以 *iPtr 以便阅读您期望的第一个 int在您的共享内存块中。如果是这样,这应该是:

int* iPtr = (int*) shmemPtr;

那是针对 C 的。如果你使用的是 C++,你还可以帮助你的编译器帮助你避免搬起石头砸自己的脚:

int* iPtr = reinterpret_cast<int *>(shmemPtr);

锐意进取,我们来到:

foo* fPtr = (foo*) ((void*) shmemPtr[sizeof(int)]);

恭喜。 shmemPtr[sizeof(int)] 显然是 shmemPtr[4],或共享内存块中的第五个字节;然后你只是拖着你的编译器,又踢又叫,先把那个单字节转换成 void *,然后再转换成 foo *

显然,您的意思是计算该字节的 地址,然后重新解释它。

foo* fPtr = (foo *)(shmemPtr + sizeof(int));   /* C default */

foo* fPtr = reinterpret_cast<foo *>(shmemPtr + sizeof(int)); /* Preferred in C++ */

由于特定于平台的对齐问题,这仍然可能 100% 正确,也可能不是 100% 正确。但这将是一个好的开始。