繁体   English   中英

指向共享内存POSIX的指针类型

[英]Pointer type to shared memory POSIX

我最初的问题是在这里 ,似乎没有人对此感兴趣。

我决定将这个乏味的问题分解为以下几个问题:

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

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

是。 尽管在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);

注意:如果将指针放在共享内存段中,请注意,它们指向的数据可能不会共享,如果共享,则在不同进程中可能具有不同的地址。

我不完全了解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 ++特定问题。

最初的问题中 ,您提到了共享内存中的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样式强制转换,并总是招致语言律师的评价(对此我有罪)。

尽管存在激烈的争论,但事实是每种语言都是正确的,因为尽管它们有很强的相似性和共同点,但它们在一个主要领域中是不同的: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 *

不能使用指向共享内存的指针正常工作。

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

通常,由于对齐问题, void *指针不能安全地强制转换为任何其他指针类型,但是如果它已经在足够大的边界上对齐(如shmatmallocmmap等返回的指针的情况)。您可以安全地将其强制转换为另一种指针类型(甚至是隐式的),并安全地取消引用它。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM