指针类型为共享内存POSIX

时间:2011-11-24 19:35:16

标签: c++ c pointers shared-memory

我原来的问题是here,似乎没人感兴趣。

我决定打破这个乏味的问题并提出以下问题:

char* shared_memory;
shared_memory = (char*) shmat (segment_id, 0, 0);

我们通常会像上面的例子那样获得指向共享内存的指针吗?换句话说,我们是否应该始终将指针转换为char*或更适合我们需要的指针?

3 个答案:

答案 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 *
  • 一个对齐(通常类似于"该地址将与您能想到的所有内容对齐")。您应该查阅API文档以获取该信息。在极少数情况下,没有对齐保证,那么你必须只使用该内存的对齐子集(这是另一个问题)

问题是:你对那段记忆做了什么?

您无法通过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 casts vs. C ++ casts

在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和C ++来说都是如此:当你在共享内存中有一个指针而不是相对索引时,你就有了一个可能的问题(即一个可能的错误)。

因此,如果你在共享内存中放入一个结构,其中一个指针成员包含进程A中的地址,那么该进程在进程B中将无效。

C ++对象提供了强大的抽象,这意味着它们使用起来既方便又安全(尽管涉及大量内存分配,但使用std::stringstd::vector<std::string> objects时没有内存泄漏风险,例)。但这种强烈的抽象只能隐藏内部事实,你仍然有指针......

共享内存中C ++对象的第二个难点是必须手动处理构造和破坏(使用放置新的和显式的析构函数调用)。

结论:除非您知道您正在使用的对象可以处理它,并且您正确使用了该对象,否则请编写以下演员:

// C++ code
struct MyObject { /* constructors, destructors, etc. */ }  ;

MyObject * myObject = static_cast<MyObject*>(p) ; // p being void *
使用指向共享内存的指针

将无法正常工作。

答案 2 :(得分:1)

shmatmalloc一样,返回void *,可以将其(更好地隐式)转换为任何其他指针类型。

通常,由于对齐问题,void *指针无法安全地转换为任何其他指针类型,但是如果它已经在足够大的边界上对齐(就像返回指针的情况一样{{1你安全地将它转换为另一种指针类型(甚至是隐式)并安全地取消引用它。