我最初的问题是here,似乎没人对此感兴趣。
我已决定将这个乏味的问题分解为以下几个问题:
char* shared_memory;
shared_memory = (char*) shmat (segment_id, 0, 0);
我们是否通常像上面的示例那样获得指向共享内存的指针?换句话说,我们应该始终将指针转换为
char*
还是更适合我们需求的指针?
最佳答案
我不完全了解shmat
,但是我有一些与WinAPI相当的经验(MapViewOfFile,所以我会给出一个更一般的答案。
因为您将两个问题联系在一起,另一个问题是关于共享内存中的C++对象,所以我将在这里处理C和C++的情况。我邀请您将[c++]标记添加到您的问题中。
内存和共享内存
无论使用哪种API,最终都会得到一个void *
,因为它就是它的形式:某个内存区域的地址(共享或不共享)。
该“分配API”(malloc,shmat,MapViewOfFile等)不知道您将使用该内存做什么,将使用什么抽象(C结构,C++对象,甚至C宏或内置类型)。 ,因此API唯一可以做的就是为您提供:
void *
问题是:您将如何处理该内存?
您无法通过
void *
访问该内存的内容,因为您无法取消引用void *
(毕竟,它是指向void
的指针...)。 void *
仅包含一个地址。仅此而已。char
抽象最容易找到的第一个抽象是
char
抽象。在C和C++中没有byte
类型,并且char
(或unsigned char
)类型填补了角色。因此,如果您想以字节数组的形式访问该内存,则可以将该内存的返回值转换为char
:/* C code */
char * pc = p ; /* p being the void * pointer */
// C++ code
char * pc = static_cast<char *>(p) ; // p being the void * pointer
struct
抽象第二种抽象是假设共享内存是一个结构(或者甚至是一个结构数组),因此您应该将指针转换为指向该结构的指针:
/* C code */
typedef struct S { /* etc. */ } S ;
S * ps = p ; /* p being the void * pointer */
// C++ code
struct S { /* etc. */ } ;
S * ps = static_cast<S *>(p) ; // p being the void * pointer
因此,您可以通过该结构访问该共享内存。
并发?
最坏的情况:两个进程将同时工作。因此,如果您不使用同步原语(例如进程间互斥体),则会陷入竞争状态(一个进程在写入值,而另一个进程正在读取值,这有可能导致读取数据损坏)
关于共享内存和指针
通常,共享内存用于在进程之间共享。通常,这意味着API可以(并且可能)为同一共享内存返回每个进程的不同地址。
原因有些复杂(应该是他们自己的问题),但是这里是:两个具有指向相同内存的指针的进程在其各自的指针上不会具有相同的地址。
/* C code */
/* process A */
void * p = getSharedMemory("ABCD") ;
/* p could have the value 0x1234 */
/* process B */
void * p = getSharedMemory("ABCD") ;
/* p could have the value 0x56789 */
地址在共享内存中无用。如果将来自进程A的有效指针地址放入共享内存中,则该指针地址在进程B中将无效。因此,切勿放置地址。
您可以放入共享内存中的是索引。例如,您可以具有以下结构:
/* C code */
typedef struct S
{
size_t index ;
double value[1000] ;
} S ;
使用此结构,可以在
value
数组中的索引42处设置值3.1415:/* C code - process A */
S * s = p ; /* p being the pointer to the shared memory */
s->index = 42 ;
s->value[42] = 3.1415 ;
然后在另一个过程中检索它:
/* C code - process B */
S * s = p ; /* p being the pointer to the shared memory */
size_t index = s->index ;
double value = s->value[index] ;
关于C和C++
共享内存是API问题,而不是C或C++特定问题。
在original question中,您提到了共享内存中的C++对象,因此我将详细介绍C和C++的一些差异,尽管它们超出了您的问题的真实范围。
C强制转换与C++强制转换
在C语言中,将
void *
指针隐式转换为任何类型的指针T *
是合法的。在C++中,您需要为此执行静态转换:/* valid C code */
T * t = p ; /* p being a void * pointer */
T * t = (T *) p ; /* useless but authorized cast */
// valid C++ code ;
T * t = static_cast<T *>(p) ; // p being a void * pointer
T * t = (T *) p ; // considered bad style for multiple reasons
因此,通常为了生成C / C++兼容代码,大多数人会使用两种语言通用的C样式强制转换,并总是招致语言律师的评价(对此我有罪)。
尽管存在激烈的辩论,但事实是每种语言都是正确的,因为尽管它们有很强的相似性和共同点,但它们在一个主要 Realm 中是不同的:C是一种弱类型语言,而C++是一种强类型语言。
将C++对象放入共享内存
还记得我写过的那篇文章,您不应将指针放在共享内存中吗?
对于C和C++来说确实如此:在共享内存中有指针而不是相对索引的那一刻,您就有了可能的问题(即可能的错误)。
因此,如果在进程A中将包含指针成员的结构放入共享内存中,则该地址在进程B中将无效。
C++对象提供了很强的抽象性,这意味着它们易于使用和安全(例如,尽管涉及到大量内存分配,但使用
std::string
或std::vector<std::string> objects
时没有内存泄漏的风险)。但是这种强大的抽象只掩盖了一个事实,在里面,您仍然有指针...共享内存中C++对象的第二个困难是必须手动处理构造和破坏(使用放置新的和显式的析构函数调用)。
结论:除非您知道所使用的对象可以处理它,并且您正确使用了该对象,否则请编写以下强制转换:
// C++ code
struct MyObject { /* constructors, destructors, etc. */ } ;
MyObject * myObject = static_cast<MyObject*>(p) ; // p being void *
不能使用指向共享内存的指针正常工作。
关于c++ - 指向共享内存POSIX的指针类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8261761/