C – 共享内存 – 共享结构中的动态数组

我正在尝试分享这样的结构
例:

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

在进程之间,问题是当我使用malloc初始化’a’时,它变为私有的进程堆执行此操作(或者至少我认为这是发生的事情)。 有没有办法用这个有效的结构创建共享内存(使用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 bc 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); 

首先,要共享int *a字段指向的内容,您需要复制与其相关的整个内存。 因此,您将需要一个至少可以保存size_t shm_size = sizeof(struct ex) + get_the_length_of_your_ex();的共享内存size_t shm_size = sizeof(struct ex) + get_the_length_of_your_ex();

从现在开始,既然你提到了shmget和shmat,我会假设你运行一个Linux系统。
第一步是创建共享内存段 。 如果您可以确定int *a内容大小的上限,那将是一件好事。 这样您就不必一遍又一遍地创建/删除共享内存段。 但是如果你这样做,需要额外的开销来说明需要多长时间的实际数据。 我会假设一个简单的size_t就可以达到这个目的。

然后,在创建细分后,您必须正确设置数据以使其保持您想要的状态。 请注意,虽然内存段的物理地址始终相同,但在调用shmat您将获得虚拟指针,这些指针仅在调用shmat的进程中可用。 下面的示例代码应该为您提供一些技巧。

 #include  #include  /* 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; } 

请原谅我的懒惰,它将进行空间优化,不会复制结构ex int *a指针int *a所有值,因为它在共享内存中完全未使用,但我不遗余力地处理这个(以及一些指针检查,如p_ex参数完整性)。

但是当你完成后,你必须找到一种方法来在你的进程之间共享shm ID。 这可以使用套接字,管道…或使用具有相同输入的ftok来完成。

使用malloc()分配给指针的malloc()对该进程是私有的。 因此,当您尝试访问另一个进程中的指针(除了对其进行malloced的进程)时,您可能会访问无效的内存页面或映射到另一个进程地址空间的内存页面。 所以,你可能会遇到段错误。

如果使用共享内存,则必须确保要向其他进程公开的所有数据都在“共享内存”段中,而不是该进程的专用内存段。

您可以尝试将数据保留在内存段中的指定偏移处,这可以在编译时具体定义,也可以放置在共享内存段中某个已知位置的字段中。

例如:如果你这样做

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); 

其他方法是使用固定大小的缓冲区来使用共享内存方法传递信息,而不是使用动态分配的指针。

希望这可以帮助。

你在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的

您需要使用共享内存/内存映射文件/您的操作系统提供的任何内容。 一般来说,IPC和进程之间共享内存非常依赖于操作系统,特别是在像C这样的低级语言中(高级语言通常有用于此的库 – 例如,即使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。 这就是为什么在评论中我说我通常更喜欢打包结构:所以我不需要为每个指针经历麻烦来进行这些操作。

转换结构:

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

然后在父进程上:

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

它应该工作。

一般来说,很难在c中的结构中使用动态数组,但是这样你就可以分配适当的内存(这也适用于malloc: 如何在C中包含动态数组INSIDE结构? )