我原来的问题是here,似乎没人感兴趣。
我决定打破这个乏味的问题并提出以下问题:
char* shared_memory;
shared_memory = (char*) shmat (segment_id, 0, 0);
我们通常会像上面的例子那样获得指向共享内存的指针吗?换句话说,我们是否应该始终将指针转换为char*
或更适合我们需要的指针?
答案 0 :(得分:4)
是。虽然演员阵容在C中是多余的,但
char *shared_memory = shmat(segment_id, 0, 0);
现在通常首选使用较新的共享内存对象(shm_open
/ mmap
)。
您可以将任何类型的数据放在共享内存段中,
struct s { int x; char s[16]; float z; };
struct s *shared_memory = shmat(segment_id, 0, 0);
注意:如果将指针放在共享内存段中,请注意它们指向的数据可能不会被共享,如果是,则可能在不同的进程中具有不同的地址。
答案 1 :(得分:4)
我并不完全了解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] ;
此共享内存是API问题,而不是C或C ++特定问题。
在你的original question中,你提到了共享内存中的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风格演员,总是会招致语言律师的评论(我对此感到内疚)。 / p>
尽管争论激烈,但事实是每种语言都是正确的,因为尽管它们具有强烈的相似性和共同点,但它们在一个主要领域是不同的: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 *
使用指向共享内存的指针将无法正常工作。
答案 2 :(得分:1)
shmat
与malloc
一样,返回void *
,可以将其(更好地隐式)转换为任何其他指针类型。
通常,由于对齐问题,void *
指针无法安全地转换为任何其他指针类型,但是如果它已经在足够大的边界上对齐(就像返回指针的情况一样{{1你安全地将它转换为另一种指针类型(甚至是隐式)并安全地取消引用它。