![](/img/trans.png)
[英]C Windows - Memory Mapped File - dynamic array within a shared struct
[英]C - shared memory - dynamic array inside shared struct
我正在嘗試分享這樣的結構
例:
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 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);
首先,要共享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 <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;
}
請原諒我的懶惰,它將進行空間優化,不會復制整個結構 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結構? )
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.