I've been trying this for hours, and google all the things I kind think of, but I'm going crazy.
I have a struct:
typedef struct {
int rows;
int collumns;
int* mat;
char* IDs_row;
} mem;
I don't know the sizes of the int* (a Matrix) and char* untill later.
When I do, I create the shared memory like this:
mem *ctrl;
int size = (2 + ((i-1)*num_cons))*sizeof(int) + i*26*sizeof(char); //I have the real size now
shmemid = shmget(KEY, size, IPC_CREAT | 0666);
if (shmemid < 0) {
perror("Ha fallado la creacion de la memoria compartida.");
exit(1);
}
ctrl = (mem *)shmat(shmemid, 0, 0);
if (ctrl <= (mem *)(0)) {
perror("Ha fallado el acceso a memoria compartida");
exit(2);
}
No problem here. Then I give a value to ctrl->rows and collumns, and assign 0 to all the matrix.
But after that, I write something in the char* and bam, segmentation fault.
Debugging the program I saw that both pointers, mat and IDs_row where null. How do I give them the correct values inside the shared memory segment??
I tried removing the char* pointer, just to give it a try, and then the segmentation fault error was in the other program that connected to said shared memory and just checked the values inside the matrix (checking ->rows and ->collumns was succesfull)
First of all, putting absolute pointers in shared memory segments is terrible terible idea - those pointers would only be valid in the process that filled in their values. Shared memory segments are not guaranteed to attach at the same virtual address in every process. On the contrary - they attach where the system deems it possible when shmaddr == NULL is specified on call to shmat(). You could specify the same virtual address when calling shmat() but it is up to you to ensure that nothing else is mapped on that memory region in all participating processes. This is hard to do in a portable manner. What you would most like to do is to either:
1) Allocate one big shared memory segment that accomodates both the mem structure and the two data arrays. Then you should put not absolute pointers but rather pointers relative to the beginning of the memory block and then adjust on usage.
2) Allocate three different shared memory segments but instead of putting pointers, put the shared memory IDs as returned by shmget():
typedef struct {
int rows;
int collumns;
int mat_id;
int IDs_row_id;
} mem;
When you need to access the matrix or the IDs array just attach to the shared memory ID stored in the corresponding field.
Pay attention though that using the same KEY in subsequent invocations of shmget() will not produce the expected result unless KEY == IPC_PRIVATE. It is best to use a the fixed key value for the shared memory block with the descriptor (of type mem) and IPC_PRIVATE for the other two memory blocks otherwise the three calls will actually return the same shared memory block - the first one will create it and the next two will simply return its ID since a block with that key already exists.
ctrl = (mem *)shmat(shmemid, 0, 0);
This only assigns valid memory to the ctrl pointer, not to ctrl->mat or ctrl->IDs_row.
You probably want:
mem *ctrl;
shmemid = shmget(KEY, sizeof(ctrl), IPC_CREAT | 0666);
//allocate memory for the structure
ctrl = (mem *)shmat(shmemid, 0, 0);
//allocate memory for the int*
shmemid = shmget(KEY,((i-1)*num_cons))*sizeof(int), IPC_CREAT | 0666);
ctrl->mat = (int*)shmat(shmemid, 0, 0);
//allocate memory for the char*
shmemid = shmget(KEY,i*26*sizeof(char), IPC_CREAT | 0666);
ctrl->IDs_row = (char*)shmat(shmemid,0,0);
Related
I'm reading about shared memory, and a question popped up in my head - since the signature of shmget is int shmget(key_t key, size_t size,int shmflg) then we can allocate for example a shared memory of 30 integers by passing 30*sizeof(int) as argument to shmget, so my question is can we access an nth element of that shared memory ? (can we access the 4th integer of our shared memory for example?)
Why don't you access it like you would do with a normal pointer ? For instance :
int shmid = shmget(key, 30 * sizeof(int), 0644 | IPC_CREATE);
void* data = shmat(shmid, (void *)0, 0);
int element = *((int *)data + 3);
This question already has answers here:
C - shared memory - dynamic array inside shared struct
(5 answers)
Closed 5 years ago.
I am working on a server with forking which stores a map(k->v) of strings in shared memory. I want to do it very simplistic but am lost in pointers and in what exactly I need to copy. So I extracted the relevant code which looks like this:
struct key_value {
char key[32];
char value[32];
};
struct key_value **map;
int *map_size;
int shmid = shmget(IPC_PRIVATE, sizeof(struct key_value**), 0600);
map = (struct key_value**) shmat(shmid, 0, 0);
int shmid_size = shmget(IPC_PRIVATE, sizeof(int), 0600);
map_size = (int*) shmat(shmid_size, 0, 0);
*map_size = 0;
//the above happens before fork()
char *c = "abc";
int shmid_struct = shmget(IPC_PRIVATE, sizeof(struct key_value*), 0600);
struct key_value *entry = (struct key_value*) shmat(shmid_struct, 0, 0);
*entry->key = *c;
printf("%s\n", map[0]->key);
//smhdt's & shmctl's
So what I want is to copy that string "abc" from *c into the map so into shared memory. Clearly, I do not yet fully understand pointers and structs so am hoping someone can clear it up. I currently get a segfault 'somewhere in main' (thanks gdb...).
Note that I am ok with the map having a fixed max_size for now (though would be great if dynamic).
EDIT: it's been pointed out in an answer that having a char* in the struct is difficult, so to use char[x] instead. Have updated the code to reflect that, but still not working.
Structures which contain pointers cannot be safely stored in shared memory, as the pointers are meaningless outside the process that created them. Even if the shared memory region is mapped at the same address in each process (which is true if the memory was mapped before a fork(), but may be false in other scenarios), pointers into non-shared memory will not behave properly, as each process may have different data at that address.
If you want to store strings in shared memory, you will need to store them as explicit character arrays, e.g.
struct key_value {
char key[32];
char value[32];
};
or use another scheme, such as storing an offset into a string table in the shared memory region.
Generally speaking, though, shared memory is not a good tool for inter-process communication. If your application depends on being able to share data in memory, threading is probably a better approach.
am using C on OSx and using shmget() to get memory to be used between two different programs i
have a structure of 2 int values
does this mean if i want a size of 27 instead it will have to be 54 and how would this work with the pointers used for the structure or any help would be greatly appreciated
No, you need to use sizeof on the struct. You can't guarantee how the implementation will pad the values inside the struct, so you don't assume the size of them. Also, if the size changes later due to adding member(s) to the struct, the memory allocation will still work correctly.
Then use that value from sizeof to multiply by the number of instances of the struct you intend to use in the shared memory area.
Assume you have a struct like this:
typedef struct {
int aCount;
int bCount;
int cCount;
} data_t;
This below function will create shared memory for the specified size.
int shared_memory_create(size_t memory_size)
{
int shm_key = shmget(IPC_PRIVATE, memory_size, IPC_CREAT | IPC_EXCL | 0666);
if(shm_key < 0) {
perror("Failed to Create Shared Memory Key");
exit(0);
}
return shm_key;
}
If you want to create shared memory to accommodate 27 elements, call the function shared_memory_create as shown below:
shm_key = shared_memory_create(27*sizeof(data_t));
I've got 2 processes (Client and Server) that are communicating through shared memory.
I need to create a 2D Array that is Dynamic (based on parameters). The array is stored in a struct and then written to the shared segment.
I can write the array to the shared memory, but cannot retrieve it from the other process.
Client Code:
struct shared_use_st {
int written_by_you;
int **PID_PRI_array;
};
/* Prepare Dynamic 2D array */
data_store = malloc(/*ROWS*/ 5 * sizeof(int*));
for(i=0;i<5; i++)
data_store[i] = malloc(/*COLS*/ 2 * sizeof(int));
/* Prepare Dynamic 2D array - Shared Memory Seg */
shared_stuff->PID_PRI_array = malloc(/*ROWS*/ 5 * sizeof(int*));
for(i=0;i<5; i++)
shared_stuff->PID_PRI_array[i] = malloc(/*COLS*/ 2 * sizeof(int));
/* Write PID and PRI to data_store array */
data_store[0][0] = pid;
data_store[0][1] = 1;
data_store[1][0] = 12345;
data_store[1][1] = 2;
data_store[2][0] = 12346;
data_store[2][1] = 3;
data_store[3][0] = 12347;
data_store[3][1] = 4;
data_store[4][0] = 12348;
data_store[4][1] = 5;
for(i=0;i<5;i++){
for(x=0;x<=1;x++){
shared_stuff->PID_PRI_array[i][x] = data_store[i][x];
}
}
Server Code:
for(i=0;i<5;i++){
printf("PID: %d, PRI:%d\n", shared_stuff->PID_PRI_array[i][0], shared_stuff->PID_PRI_array[i][1]);
}
I get a "Segmentation Fault" error.
Thanks.
Even if your shared_stuff object is in shared memory, you are not writing the array to shared memory. You are allocating space with malloc, writing data to that space, and then putting pointers to that space into shared_stuff. malloc allocates space within the current process‘ normal address space, not in a shared memory segment you have created. You need to write the array contents to the shared memory.
Presuming there is enough space for the array within the shared memory segment, you will have to manage the addresses yourself, not using malloc. (If there is not enough space, you must make the shared memory segment larger or convey the information in pieces over time.)
You can place a variable-length array within the shared memory segment as follows.
First, define a structure that contains all the “management” information you need, such as the array sizes:
struct StuffStruct
{
size_t NumberOfRows, NumberOfColumns;
… Other information as desired.
};
Create a pointer to that structure and set it to point to the shared memory segment:
struct StuffStruct *Stuff = shm; // shm contains the address from shmat, performed previously.
Create a pointer to an array with the desired number of columns and set it to point into the shared memory segment after the initial structure:
int (*data_store)[NumberOfColumns] = (int (*)[NumberOfColumns]) ((char *) Stuff + sizeof *Stuff);
(Note for C purists: Yes, the C standard does not guarantee what happens when you do pointer arithmetic like this. However, any implementation providing shared memory support must provide support for this sort of pointer arithmetic.)
Note that sizeof *Stuff + NumberOfRows * NumberOfColumns * size(int) must be no greater than the size of the shared memory segment. Otherwise you will overrun the shared memory segment in the next step.
For the next step, fill the array with data: Assign values to the elements of data_store as for a normal two-dimensional array.
In the server, set Stuff the same way. Then, after the client has written the shared memory segment, read the numbers of rows and columns from Stuff. Then set data_store the same way. Then read from data_store.
Working with shared memory for the first time, my project is to have readers and writers access shared strings and modify or read them, etc. I know that malloc doesn't work but not sure how to attach a 2d string array to memory, i keep getting this from the compiler:
warning: assignment makes integer from pointer without a cast
int array_id; // id for the shared memory segment
char records[10][50]; // the shared memory segment array
// attach the reader to the shared segment
fread(&newrecord, sizeof(id_record), 1, id_file);
array_id = newrecord.id;
printf("%d\n", array_id);
records[0][0] = (char**) shmat(array_id, (void*) 0, 0);
if (records[0] == (void*)-1) {
perror("Array Attachment Reader");
}
arrayid is correct i've triple checked it don't bring it up.
thanks
You will need to attach the shared memory, but store the pointer:
char (*records)[10][50]; // Pointer to an array
records = shmat(array_id, (void *)0, 0);
if ((void *)records == (void *)-1) ...error...
strcpy((*records)[0], newrecord);
You were trying to change the address at which the records array is stored; C doesn't allow that.
Don't use like this because records[0][0] is of char type not (char**)