16

私はこの
例のような構造体を共有しようとしています:

typedef struct {
    int* a;
    int b;
    int c;
} ex;

プロセス間で、問題は、「a」をmallocで初期化すると、これを行うプロセスのヒープに対してプライベートになることです(または、少なくともこれが起こると思います)。この構造体を使用して共有メモリを (shmget、shmat で) 作成する方法はありますか?

編集:私はLinuxに取り組んでいます。
編集:次のようにバッファを初期化するプロセスがあります:

key_t key = ftok("gr", 'p');   
int mid = shmget(key, sizeof(ex), IPC_CREAT | 0666);    
ex* e = NULL;
status b_status = init(&e, 8); //init gives initial values to b c and allocate space for 'a' with a malloc
e = (ex*)shmat(mid, NULL, 0);

他のプロセスは、次のように共有メモリにアタッチします。

key_t key = ftok("gr", 'p');
int shmid = shmget(key, sizeof(ex), 0);
ex* e;
e = (ex*)shmat(shmid, NULL, 0);  

後でaから要素を取得します。この場合、位置1にあります

int i = get_el(e, 1);
4

5 に答える 5

6

まず、int *aフィールドが指すコンテンツを共有するには、それに関連するメモリ全体をコピーする必要があります。したがって、少なくとも を保持できる共有メモリが必要になりますsize_t shm_size = sizeof(struct ex) + get_the_length_of_your_ex();

ここからは、shmget と shmat について言及したので、Linux システムを実行していると仮定します。
最初のステップは、共有メモリ セグメントの作成です。int *aコンテンツのサイズの上限を決定できるとよいでしょう。この方法では、共有メモリ セグメントを何度も作成/削除する必要はありません。ただし、そうすると、実際のデータの長さを示す余分なオーバーヘッドが必要になります。size_tこの目的のためには、シンプルな方法でうまくいくと思います。

次に、セグメントを作成した後、データを正しく設定して、必要なものを保持する必要があります。メモリ セグメントの物理アドレスは常に同じですが、 を呼び出すと、 を呼び出したプロセスでのみ使用できる仮想shmatポインタが取得されることに注意してください。以下のコード例は、そうするためのいくつかのトリックを提供するはずです。shmat

#include <sys/types.h>
#include <sys/ipc.h>

/* Assume a cannot point towards an area larger than 4096 bytes. */
#define A_MAX_SIZE (size_t)4096

struct ex {
    int *a;
    int b;
    int c;
}

int shm_create(void)
{
    /*
     * If you need to share other structures,
     * You'll need to pass the key_t as an argument
     */
    key_t k = ftok("/a/path/of/yours");
    int shm_id = 0;
    if (0 > (shm_id = shmget(
        k, sizeof(struct ex) + A_MAX_SIZE + sizeof(size_t), IPC_CREAT|IPC_EXCL|0666))) {
        /* An error occurred, add desired error handling. */
    }
    return shm_id;
}

/*
 * Fill the desired shared memory segment with the structure
 */
int shm_fill(int shmid, struct ex *p_ex)
{
    void *p = shmat(shmid, NULL, 0);
    void *tmp = p;
    size_t data_len = get_my_ex_struct_data_len(p_ex);
    if ((void*)(-1) == p) {
        /* Add desired error handling */
        return -1;
    }
    memcpy(tmp, p_ex, sizeof(struct ex));
    tmp += sizeof(struct ex);
    memcpy(tmp, &data_len, sizeof(size_t);
    tmp += 4;
    memcpy(tmp, p_ex->a, data_len);

    shmdt(p);
    /*
     * If you want to keep the reference so that
     * When modifying p_ex anywhere, you update the shm content at the same time :
     * - Don't call shmdt()
     * - Make p_ex->a point towards the good area :
     * p_ex->a = p + sizeof(struct ex) + sizeof(size_t);
     * Never ever modify a without detaching the shm ...
     */
    return 0;
}

/* Get the ex structure from a shm segment */
int shm_get_ex(int shmid, struct ex *p_dst)
{
    void *p = shmat(shmid, NULL, SHM_RDONLY);
    void *tmp;
    size_t data_len = 0;
    if ((void*)(-1) == p) {
        /* Error ... */
        return -1;
    }
    data_len = *(size_t*)(p + sizeof(struct ex))
    if (NULL == (tmp = malloc(data_len))) {
        /* No memory ... */
        shmdt(p);
        return -1;
    }
    memcpy(p_dst, p, sizeof(struct ex));
    memcpy(tmp, (p + sizeof(struct ex) + sizeof(size_t)), data_len);
    p_dst->a = tmp;
    /*
     * If you want to modify "globally" the structure,
     * - Change SHM_RDONLY to 0 in the shmat() call
     * - Make p_dst->a point to the good offset :
     * p_dst->a = p + sizeof(struct ex) + sizeof(size_t);
     * - Remove from the code above all the things made with tmp (malloc ...)
     */
    return 0;
}

/*
 * Detach the given p_ex structure from a shm segment.
 * This function is useful only if you use the shm segment
 * in the way I described in comment in the other functions.
 */
void shm_detach_struct(struct ex *p_ex)
{
    /*
     * Here you could : 
     * - alloc a local pointer
     * - copy the shm data into it
     * - detach the segment using the current p_ex->a pointer
     * - assign your local pointer to p_ex->a
     * This would save locally the data stored in the shm at the call
     * Or if you're lazy (like me), just detach the pointer and make p_ex->a = NULL;
     */

    shmdt(p_ex->a - sizeof(struct ex) - sizeof(size_t));
    p_ex->a = NULL;
}

私の怠惰を許してください、共有メモリでは完全に使用されていないため、構造体exint *aのポインターの値をまったくコピーしないようにスペースが最適化されますが、これを処理するために余分なコードを節約しました(およびいくつかのポインターチェックp_ex 引数の整合性)。

ただし、完了したら、プロセス間で shm ID を共有する方法を見つける必要があります。これは、ソケット、パイプを使用して行うことができます...またはftok同じ入力で使用します。

于 2013-01-28T09:56:50.697 に答える
3

を使用してポインタに割り当てるメモリmalloc()は、そのプロセス専用です。したがって、別のプロセス(それをマロックしたプロセス以外)でポインタにアクセスしようとすると、無効なメモリページまたは別のプロセスアドレス空間にマップされたメモリページにアクセスする可能性があります。したがって、セグメンテーション違反が発生する可能性があります。

共有メモリを使用している場合は、他のプロセスに公開するすべてのデータが、プロセスのプライベートメモリセグメントではなく、共有メモリセグメントの「中に」あることを確認する必要があります。

データをメモリセグメントの指定されたオフセットに残して、コンパイル時に具体的に定義するか、共有メモリセグメントの既知の場所のフィールドに配置してみてください。

例:これを行っている場合

char *mem = shmat(shmid2, (void*)0, 0);

// So, the mystruct type is at offset 0.
mystruct *structptr = (mystruct*)mem;

// Now we have a structptr, use an offset to get some other_type.
other_type *other = (other_type*)(mem + structptr->offset_of_other_type);

もう1つの方法は、動的に割り当てられたポインターを使用する代わりに、共有メモリアプローチを使用して情報を渡すための固定サイズのバッファーを用意することです。

お役に立てれば。

于 2013-01-28T09:53:38.860 に答える
1

Windows または Linux で作業していますか? いずれにせよ、必要なのはメモリマップされたファイルです。ここにコード例を含むドキュメント、

http://msdn.microsoft.com/en-us/library/aa366551%28VS.85%29.aspx http://menehune.opt.wfu.edu/Kokua/More_SGI/007-2478-008/sgi_html/ch03 .html

于 2013-01-28T09:01:23.240 に答える
1

共有メモリ/メモリマップファイル/OSが提供するものは何でも使用する必要があります。一般に、IPC とプロセス間のメモリ共有は、特に C のような低レベル言語では OS に大きく依存します (高レベル言語には、通常、そのためのライブラリがあります。たとえば、C++ でさえ、boost を使用してそれをサポートしています)。Linux を使用している場合、通常、少量の場合は shmat を使用し、大量の場合は mmap ( http://en.wikipedia.org/wiki/Mmap ) を使用します。Win32 では、多くのアプローチがあります。私が好むのは、通常、ページファイルでバックアップされたメモリマップファイルを使用することです ( http://msdn.microsoft.com/en-us/library/ms810613.aspx )

また、データ構造内でこれらのメカニズムを使用している場所に注意を払う必要があります。コメントに記載されているように、「ソース」プロセスにあるポインタは「ターゲット」プロセスでは無効であり、注意を払う必要があります。置き換え/調整する必要があります (IIRC、mmap からのポインターは既に問題ありません (マップされています)。少なくとも Windows では、MapViewOfFile から取得したポインターは問題ありません)。

編集:編集した例から:ここで行うこと:

e = (ex*)shmat(mid, NULL, 0);

(その他のプロセス)

int shmid = shmget(key, sizeof(ex), 0);
ex* e = (ex*)shmat(shmid, NULL, 0);

正しいですが、構造体への「メイン」ポインターだけでなく、持っているポインターごとに行う必要があります。たとえば、次のことを行う必要があります。

e->a = (int*)shmat(shmget(another_key, dim_of_a, IPC_CREAT | 0666), NULL, 0);

malloc で配列を作成する代わりに。次に、他のプロセスで、ポインターに対して shmget/shmat を実行する必要もあります。これが、コメントで、通常は構造体をパックすることを好むと述べた理由です。したがって、すべてのポインターに対してこれらの操作を行うという面倒な作業を行う必要はありません。

于 2013-01-28T09:03:46.033 に答える
0

構造体を変換します。

typedef struct {
     int b;
     int c;
     int a[];
} ex;

次に、親プロセスで:

int mid = shmget(key, sizeof(ex) + arraysize*sizeof(int), 0666);

それはうまくいくはずです。

一般に、C の構造体内で動的配列を操作することは困難ですが、この方法で適切なメモリを割り当てることができます (これは malloc でも機能します: How to include a dynamic array INSIDE a struct in C? )

于 2017-01-04T18:59:22.467 に答える