共有メモリへのポインタ型POSIX

私の元々の質問は、ここ、誰も興味を持たないようだ。

私はその退屈な質問を打ち破り、以下の質問をすることに決めました。

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

上記の例のように共有メモリへのポインタを取得するのは一般的ですか?つまり、常にポインタを char * にキャストするか、必要に応じてポインタをキャストする必要がありますか?

1
おそらくあなたはC ++の例を見ているでしょうか? Cコードでは、 void * char * にキャストするのは異常です。
追加された 著者 Dietrich Epp,
@TimCooperほとんどすべての例では、char *にキャストしています。私の元の質問で説明したように、私は共有メモリに文字列の代わりにユーザー定義のオブジェクトを書き、ポインタがMyObject *型であるようにしたいと思います。私はオブジェクトを書く上での例を探してきました。
追加された 著者 Terry Li,
@DietrichEppなぜCでないのですか?
追加された 著者 Terry Li,
@paercebal混乱して申し訳ありませんが、私は本当に意味します:共有メモリへのポインタはcharへのポインタだけか、それとも何かへのポインタになることはできますか?ほとんどすべての例でchar *を使用しているのでわからない。
追加された 著者 Terry Li,
@TerryLi: char がバイトとして使用されるため、通常は char * が使用されます。ただし、任意のポインタ型にキャストできます。
追加された 著者 Mooing Duck,
@Terry LiYifeng:Cでは、明示的にキャストすることなく、あなたの頭にどんなポインタが入っても、 void * ポインタをキャストすることができます。 C ++では、キャストを使用する必要があります。したがって、Cでは、キャストはオプションであり、C ++では必須です。私が見ていないのは、このキャストの有無が共有メモリに関する質問とどのように関係しているかです: - /
追加された 著者 paercebal,

3 答え

はい。キャストは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);

注:共有メモリーセグメントにポインターを置くと、ポインタが指しているデータが共有されていない可能性があることに注意してください。そうであれば、異なるプロセスで異なるアドレスを持つ可能性があります。

4
追加された

私は shmat について正確には分かりませんが、私はWinAPIの同等の経験を持っています( MapViewOfFile ので、より一般的な答えを示します。

2つの質問を結びつけて共有メモリのC ++オブジェクトについての質問があるので、ここではCとC ++の両方のケースを扱います。あなたの質問に[c ++]タグを追加するようお願いします。

メモリと共有メモリ

APIが何であれ、 void * を得ることになります。なぜなら、あるメモリゾーンのアドレス(共有されているかどうか)です。

その「割り当てAPI」(malloc、shmat、MapViewOfFileなど)は、そのメモリで何をするのか、どの抽象化を使用するのか(C構造体、C ++オブジェクト、さらにはCマクロまたは組み込み型) 、APIができるのはあなたに与えることだけです:

  • an address, thus a void *
  • an alignment (usually something like "that address will be aligned for everything you can think of"). You should consult the API documentation to get that info. In the rare case no alignment guarantee is done, then you must use only the aligned subset of that memory (this is for another question)

問題は:あなたはその記憶で何をしますか?

void * を参照できないので、あなたは void * を使ってそのメモリの内容にアクセスすることはできません(結局のところ、 code> void ...)。 void * にはアドレスしか含まれていません。それ以上のものはありません。

char 抽象化

The first abstraction you'll find, the easiest one, is char 抽象化. There is no byte type in C and C++, and the char (or unsigned char) type fills the role. So, if you want to access that memory as an array of bytes, you cast the return of that memory into char:

/* C code */
char * pc = p ; /* p being the void * pointer */

// C++ code
char * pc = static_cast(p) ;//p being the void * pointer

struct 抽象化

2番目の抽象化は共有メモリが構造体(またはおそらく構造体の配列)であると仮定するため、ポインタをその構造体へのポインタにキャストする必要があります:

/* C code */
typedef struct S { /* etc. */ } S ;
S * ps = p ; /* p being the void * pointer */

// C++ code
struct S { /* etc. */ } ;
S * ps = static_cast(p) ;//p being the void * pointer

したがって、構造体を通してその共用メモリにアクセスすることができます。

並行性?

最悪の場合:2つのプロセスが同時に動作します。したがって、プロセス間相互排除のような同期プリミティブを使用しないと競合状態に陥ります(あるプロセスは値を書きますが、もう一方は値を読み込みますが、読み取りデータが破損する可能性があります)

共有メモリとポインタについて

通常、共有メモリはプロセス間で共有されるために使用されます。通常、これは、APIが、同じ共有メモリーに対して、プロセスごとに異なるアドレスを返すことができる(そしておそらく)ことを意味します。

The reasons are somewhat complicated (and should be their own question), but here it is: Two processes with pointers to the same memory won't have the same addresses on their respective pointers.

/* 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 ;

この構造体では、インデックス42の value 配列に値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 ++について

この共有メモリの問題は、CまたはC ++固有の問題ではなく、APIの問題です。

オリジナルの質問 C + +オブジェクトを共有メモリに記述したので、質問の真の範囲外であるにもかかわらず報告されているCとC ++の違いについて詳しく説明します。

CキャストとC ++キャスト

Cでは、任意の種類のポインタ T * に暗黙的に任意の void * ポインタをキャストできます。 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(p) ;//p being a void * pointer
T * t = (T *) p ;            //considered bad style for multiple reasons

だから、通常、C/C ++互換コードを生成するために、ほとんどの人が2つの言語に共通するCスタイルのキャストを使用し、常に言語弁護士の発言を犯します(私はそれを犯しています)。

熱い議論にもかかわらず、真実は各言語が正しいということです。なぜなら、強い類似点と共通の根拠にもかかわらず、それらは1つの主要なドメインでは異なっているからです.Cは弱く型付けされた言語ですが、C ++は強く型付けされた言語です。

C ++オブジェクトを共有メモリに置く

共有メモリにポインタを置いてはいけないと書いた部分を覚えていますか?

これはCとC ++に当てはまります。共有メモリに相対インデックスではなくポインタを持っている瞬間に、問題が発生する可能性があります。

したがって、プロセスAのアドレスを含むポインタメンバーを持つ構造体を共有メモリに置くと、そのアドレスはプロセスBでは無効になります。

C++ objects offer a strong abstraction, meaning they are easy and safe to use (there's no memory leaks risks when using std::string or std::vector objectsdespite the amount of memory allocation involved, for example). But this strong abstraction only hides the fact that inside, you still do have pointers...

共有メモリ内のC ++オブジェクトの第2の難点は、構築と破壊を手動で処理する必要があることです(新しいデストラクタ呼び出しと明示的なデストラクタ呼び出しを使用して)。

結論:あなたが使っているオブジェクトがそれを扱うことができ、そのオブジェクトを正しく使用していることが分かっていない限り、以下のキャストを書く:

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

MyObject * myObject = static_cast(p) ;//p being void *

共有メモリへのポインタでは正しく動作しません。

4
追加された
非常に役立ちます。どうもありがとうございました!
追加された 著者 Terry Li,
+1は本当に徹底的で有益な答えです。
追加された 著者 Blagovest Buyukliev,

shmat, like malloc, returns void *, which can be casted (better implicitly) to any other pointer type.

一般に、 void * ポインタは、整列の問題のために他のポインタ型に安全にキャストすることはできませんが、十分に大きな境界に整列されていると( mmap など)を安全に他のポインタ型にキャストして(暗黙的に)安全に間接参照します。

1
追加された