C – mémoire partagée – tableau dynamic à l’intérieur de la structure partagée

j’essaie de partager une structure comme celle-ci
Exemple:

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

entre les processus, le problème est que, lorsque j’initialise “a” avec un malloc, il devient privé du tas de processus qui effectue cela (ou du moins, je pense que c’est ce qui se produit). Est-il possible de créer une mémoire partagée (avec shmget, shmat) avec cette structure qui fonctionne?

EDIT: Je travaille sur Linux.
EDIT: J’ai un processus qui initialise le tampon comme ceci:

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

l’autre processus s’attache à la mémoire partagée comme ceci:

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

et plus tard obtenir un élément d’un, dans ce cas que dans la position 1

 int i = get_el(e, 1); 

Tout d’abord, pour partager le contenu pointé par votre int *a field, vous devez copier toute la mémoire qui lui est associée. Ainsi, vous aurez besoin d’une mémoire partagée pouvant contenir au moins size_t shm_size = sizeof(struct ex) + get_the_length_of_your_ex(); .

À partir de maintenant, puisque vous avez mentionné shmget et shmat, je supposerai que vous utilisez un système Linux.
La première étape est la création d’un segment de mémoire partagée . Ce serait une bonne chose si vous pouviez déterminer une limite supérieure à la taille de l’ int *a contenu. De cette façon, vous n’auriez pas à créer / supprimer le segment de mémoire partagée, encore et encore. Mais si vous le faites, une surcharge supplémentaire pour indiquer combien de temps les données réelles seront nécessaires. Je supposerai qu’un simple size_t fera l’affaire dans ce but.

Ensuite, après avoir créé votre segment, vous devez définir les données correctement pour qu’il contienne ce que vous voulez. Notez que, même si l’adresse physique du segment de mémoire est toujours la même, lorsque vous appelez shmat vous obtenez des pointeurs virtuels , qui ne peuvent être utilisés que dans le processus appelé shmat . L’exemple de code ci-dessous devrait vous donner quelques astuces pour le faire.

 #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; } 

Excusez ma paresse, l’espace serait optimisé pour ne pas copier du tout la valeur de l’ int *a pointeur de la struct ex car il est complètement inutilisé dans la mémoire partagée, mais je me suis épargné avec un extra-code pour le gérer (et vérification du pointeur comme l’intégrité des arguments p_ex).

Mais lorsque vous avez terminé, vous devez trouver un moyen de partager l’ID shm entre vos processus. Cela pourrait être fait en utilisant des sockets, des pipes … ou en utilisant ftok avec la même entrée.

La mémoire que vous allouez à un pointeur à l’aide de malloc() est privée pour ce processus. Ainsi, lorsque vous essayez d’accéder au pointeur dans un autre processus (autre que le processus qui l’a malléisé), vous allez probablement accéder à une page mémoire non valide ou à une page mémoire mappée dans un autre espace adresse de processus. Donc, vous êtes susceptible d’avoir une erreur de segmentation.

Si vous utilisez la mémoire partagée, vous devez vous assurer que toutes les données que vous souhaitez exposer à d’autres processus se trouvent “dans” le segment de mémoire partagée et non des segments de mémoire privée du processus.

Vous pouvez essayer de conserver les données à un décalage spécifié dans le segment de mémoire, ce qui peut être défini concrètement lors de la compilation ou placé dans un champ situé à un emplacement connu du segment de mémoire partagée.

Ex: si tu fais ça

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

Une autre solution serait d’avoir un tampon de taille fixe pour transmettre les informations en utilisant l’approche de la mémoire partagée, au lieu d’utiliser le pointeur alloué dynamicment.

J’espère que cela t’aides.

Travaillez-vous sous Windows ou Linux? Dans tous les cas, il vous faut un fichier mappé en mémoire . Documentation avec des exemples de code ici,

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

Vous devez utiliser la mémoire partagée / les fichiers mappés sur la mémoire / tout ce que votre système d’exploitation vous propose. En général, l’IPC et le partage de la mémoire entre les processus dépendent beaucoup du système d’exploitation, en particulier dans les langages de bas niveau tels que C (les langages de niveau supérieur ont généralement des bibliothèques pour cela – par exemple, même le C ++ prend en charge l’utilisation de boost). Si vous êtes sous Linux, j’utilise généralement shmat pour de petites quantités et mmap ( http://en.wikipedia.org/wiki/Mmap ) pour de plus grandes quantités. Sur Win32, il existe de nombreuses approches. celui que je préfère utilise généralement des fichiers mappés en mémoire sauvegardée sur un fichier de page ( http://msdn.microsoft.com/en-us/library/ms810613.aspx )

En outre, vous devez faire attention à l’endroit où vous utilisez ce mécanisme dans vos structures de données: comme indiqué dans les commentaires, sans prendre de précautions, le pointeur que vous avez dans votre processus “source” n’est pas valide dans le processus “cible” et doit être remplacé / ajusté (IIRC, les pointeurs provenant de mmap sont déjà OK (mappés); au moins, sous Windows, les pointeurs que vous sortez de MapViewOfFile sont OK).

EDIT: extrait de votre exemple modifié: ce que vous faites ici:

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

(autre processus)

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

est correct, mais vous devez le faire pour chaque pointeur que vous avez, pas seulement pour le pointeur “principal” vers la structure. Par exemple, vous devez faire:

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

au lieu de créer le tableau avec malloc. Ensuite, sur l’autre processus, vous devez également faire shmget / shmat pour le pointeur. C’est pourquoi, dans les commentaires, j’ai dit que je préfère généralement empaqueter les structures: je n’ai donc pas besoin de passer par le tracas de ces opérations pour chaque pointeur.

Convertir la structure:

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

puis sur le processus parent:

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

ça devrait marcher.

En général, il est difficile de travailler avec des tableaux dynamics dans des structures en c, mais vous pouvez ainsi allouer la mémoire appropriée (cela fonctionnera également dans malloc: Comment inclure un tableau dynamic dans une structure en C? )