shmget 如何分配内存?无法使用线性寻址访问(地址边界错误)
Posted
技术标签:
【中文标题】shmget 如何分配内存?无法使用线性寻址访问(地址边界错误)【英文标题】:How does shmget allocate memory? Cannot access with linear addressing (Address Boundary error) 【发布时间】:2020-01-16 07:21:35 【问题描述】:在以下示例中,我尝试使用shmget
为一个整数和10 个foo
结构分配内存,并尝试线性访问它们。但是,它会出现“地址边界错误”。
对于 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++ 涉足一些系统的东西,所以如果我在这里遗漏了任何东西,请原谅我。
【问题讨论】:
这是错误的:int* iPtr = (int*) shmemPtr[0];
。 shmemPtr
是指向 char
的指针,所以 shmemPtr[0]
是 char
。您不应该将 char
转换为指针。您可能需要int *iPtr = (int *) shmemPtr;
或int *iPtr = (int *) &shmemPtr[0];
。你的编译器应该已经警告你了。如果没有,请将-Wall
添加到您的编译器开关中。
同样,foo* fPtr = (foo*) ((void*) shmemPtr[sizeof(int)]);
应该是 foo *fPtr = (foo *) &shmemPtr[sizeof(int)];
。
这是错误的:*shmemPtr == -1
。如果shmat
失败,它会返回一个在其“值”中为-1 的指针。它不指向包含 -1 的内存位置。与shmemPtr == (char *) -1
比较。
@EricPostpischil 完成了这项工作。我认为这与我没有正确引用指针有关。感谢您提供有关编译器标志的提示!
用你正在编译的语言标记这样的问题,而不是 C 和 C++。你用的是哪个?
【参考方案1】:
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% 正确。但这将是一个好的开始。
【讨论】:
谢谢。如果它不会总是像这样对齐,那么如何以便携的方式做我正在尝试的事情?我是否为每个组件单独创建共享内存? C++ 标签刚刚被删除——这样做是有道理的(没有特定于 C++ 的代码)。但是,不幸的是,这使得reinterpret_cast
不合适。对于是否恢复 C++ 标签或建议您从答案中删除仅限 C++ 的建议,我有两种看法。你有偏好吗?
这里可以介绍 C 和 C++。就对齐而言,在这样的简单情况下,性能不是问题,在计算地址后,通常的方法是简单地将memcpy
转换为单独声明的struct
。
@SamVarshavchik:过去我不得不寻找特定于 C 或 C++ 的答案。也许您认为您可以通过提供两者/任何一个的答案来提供帮助,但是混乱的搜索结果是有代价的。不得不查看太多答案以丢弃不适用的答案可能会很麻烦。以上是关于shmget 如何分配内存?无法使用线性寻址访问(地址边界错误)的主要内容,如果未能解决你的问题,请参考以下文章