sistemas - C-memoria compartida-matriz dinámica dentro de la estructura compartida
memoria compartida sistemas operativos (5)
¿Estás trabajando en Windows o Linux? En cualquier caso, lo que necesita es un archivo mapeado en la memoria . Documentación con ejemplos de código aquí,
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
Estoy tratando de compartir una estructura como esta
ejemplo:
typedef struct {
int* a;
int b;
int c;
} ex;
entre procesos, el problema es que cuando inicializo ''a'' con un malloc, se vuelve privado al montón del proceso que hace esto (o al menos creo que esto es lo que sucede). ¿Hay alguna manera de crear una memoria compartida (con shmget, shmat) con esta estructura que funcione?
EDITAR: estoy trabajando en Linux.
EDITAR: Tengo un proceso que inicializa el buffer de esta manera:
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);
el otro proceso se une a la memoria compartida de esta manera:
key_t key = ftok("gr", ''p'');
int shmid = shmget(key, sizeof(ex), 0);
ex* e;
e = (ex*)shmat(shmid, NULL, 0);
y luego obtener un elemento de a, en este caso, que en la posición 1
int i = get_el(e, 1);
En primer lugar, para compartir el contenido señalado por su campo int *a
, deberá copiar toda la memoria relacionada con él. Por lo tanto, necesitará una memoria compartida que pueda contener al menos size_t shm_size = sizeof(struct ex) + get_the_length_of_your_ex();
.
A partir de ahora, como mencionaste shmget y shmat, asumiré que ejecutas un sistema Linux.
El primer paso es la creación del segmento de memoria compartida . Sería bueno si puede determinar un límite superior al tamaño del contenido int *a
. De esta forma, no tendría que crear / eliminar el segmento de memoria compartida una y otra vez. Pero si lo hace, una sobrecarga adicional para indicar cuánto tiempo se necesitarán los datos reales. Asumiré que un simple size_t
hará el truco para este propósito.
Luego, después de crear su segmento, debe establecer los datos correctamente para que conserve lo que desea. Tenga en cuenta que, si bien la dirección física del segmento de memoria es siempre la misma, cuando llame a shmat
obtendrá punteros virtuales , que solo se pueden usar en el proceso llamado shmat
. El siguiente código de ejemplo debe darle algunos trucos para hacerlo.
#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;
}
Disculpe mi pereza, sería espacio optimizado para no copiar en absoluto el valor de la int *a
puntero de la estructura ex ya que está completamente sin usar en la memoria compartida, pero me ahorré extra-código para manejar esto (y algunos comprobaciones de puntero como la integridad de los argumentos p_ex).
Pero cuando haya terminado, debe encontrar una forma de compartir la identificación de shm entre sus procesos. Esto podría hacerse usando tomas de corriente, tuberías ... O usando ftok
con la misma entrada.
La memoria que asigna a un puntero usando malloc()
es privada para ese proceso. Por lo tanto, cuando intenta acceder al puntero en otro proceso (que no sea el proceso que lo malloced) es probable que acceda a una página de memoria no válida o a una página de memoria asignada en otro espacio de direcciones de proceso. Por lo tanto, es probable que obtenga un segfault.
Si está utilizando la memoria compartida, debe asegurarse de que todos los datos que desea exponer a otros procesos estén "en" el segmento de memoria compartida y no en los segmentos de memoria privados del proceso.
Podría intentar, dejando los datos en un desplazamiento especificado en el segmento de memoria, que pueden definirse concretamente en tiempo de compilación o colocarse en un campo en alguna ubicación conocida en el segmento de memoria compartida.
Por ejemplo: si estás haciendo esto
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);
Otra forma sería tener un búfer de tamaño fijo para pasar la información utilizando el enfoque de memoria compartida, en lugar de usar el puntero asignado dinámicamente.
Espero que esto ayude.
Necesita utilizar memoria compartida / archivos mapeados en memoria / lo que su sistema operativo le de. En general, el IPC y la memoria compartida entre procesos depende bastante del sistema operativo, especialmente en lenguajes de bajo nivel como C (los lenguajes de nivel superior suelen tener bibliotecas para eso; por ejemplo, incluso C ++ tiene soporte para ello usando boost). Si está en Linux, generalmente uso shmat para cantidades pequeñas, y mmap ( http://en.wikipedia.org/wiki/Mmap ) para cantidades más grandes. En Win32, hay muchos enfoques; el que prefiero usualmente usa archivos mapeados de memoria respaldados por archivos de página ( http://msdn.microsoft.com/en-us/library/ms810613.aspx )
Además, debe prestar atención a dónde está utilizando estos mecanismos dentro de sus estructuras de datos: como se menciona en los comentarios, sin utilizar precauciones, el puntero que tiene en su proceso de "origen" no es válido en el proceso "objetivo", y necesita ser reemplazado / ajustado (IIRC, los punteros que vienen de mmap ya están bien (mapeados); al menos, bajo los punteros de Windows que sale de MapViewOfFile están bien).
EDIT: de tu ejemplo editado: lo que haces aquí:
e = (ex*)shmat(mid, NULL, 0);
(otro proceso)
int shmid = shmget(key, sizeof(ex), 0);
ex* e = (ex*)shmat(shmid, NULL, 0);
es correcto, pero debe hacerlo para cada puntero que tenga, no solo para el puntero "principal" de la estructura. Por ejemplo, tienes que hacer:
e->a = (int*)shmat(shmget(another_key, dim_of_a, IPC_CREAT | 0666), NULL, 0);
en lugar de crear la matriz con malloc. Luego, en el otro proceso, también necesita hacer shmget / shmat para el puntero. Esta es la razón por la cual, en los comentarios, dije que generalmente prefiero empaquetar las estructuras: así que no necesito pasar por la molestia de estas operaciones para cada puntero.
Convierta la estructura:
typedef struct {
int b;
int c;
int a[];
} ex;
y luego en el proceso principal:
int mid = shmget(key, sizeof(ex) + arraysize*sizeof(int), 0666);
deberia de funcionar.
En general, es difícil trabajar con matrices dinámicas dentro de las estructuras en c, pero de esta manera usted puede asignar la memoria adecuada (esto también funcionará en malloc: ¿Cómo incluir una matriz dinámica DENTRO de una estructura en C? )