Accessing a specific element on IPC shared memory - c

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

Related

How does shmget allocate memory? Cannot access with linear addressing (Address Boundary error)

In the following example, I'm trying to use shmget to allocate memory for one integer, and 10 foo structs and trying to access them linearly. However, it errors out with an "Address Boundary error".
For a MacOS system (but should be the same on Linux), I tried to allocate the exact amount of memory the two data structures should have taken and tried to byte address them linearly.
#include <stdio.h>
#include <sys/shm.h>
typedef struct {
int f1;
int f2;
} foo;
int main() {
// Size of memory. Consider, int size to be 4. (4 + 8 * 10 = 84 bytes)
const int shmsz = sizeof(int) + sizeof(foo) * 10;
// Create shared mem id.
const int shmid = shmget(IPC_PRIVATE, shmsz, 0666);
if (shmid < 0) {
perror("shmget failed.");
return 1;
}
// Pointer to the shared memory. Cast as char* for byte addressing.
char* shmemPtr = (char*) shmat(shmid, 0, 0);
if (*shmemPtr == -1) {
perror("shmat failed.");
return 1;
}
// The integer should point to the first 4 bytes. This is the same as
// the address pointed to by shmemPtr itself.
int* iPtr = (int*) shmemPtr[0];
// The 80 bytes for the 10 foos range from index 4 -> 83
foo* fPtr = (foo*) ((void*) shmemPtr[sizeof(int)]);
printf("i: %p\n", iPtr); // why is this 0x0 ?
printf("x: %p\n", fPtr); // why is this 0x0 ?
*iPtr = 0; // <-- This dereference crashes, probably since the address of iPtr is 0x0
}
After allocating the memory and receiving a pointer to it via shmat, any pointers I try to create to the allocated memory is 0x0 and any derefences will (rightfully) crash the program. I expected the int* and foo* to be valid pointers to shared memory.
I'm just dabbling with some systems stuff with C/C++ so forgive me if I'm trivially missing anything here.
char* shmemPtr = (char*) shmat(shmid, 0, 0);
You've got now a pointer to your shared memory block. It is a char *, pointing to the start of the block. So far, so good.
int* iPtr = (int*) shmemPtr[0];
Since shmemPtr is a char *, shmemPtr[0] is its first byte. The above, inexplicably, converts a single byte into an int *. This makes no sense, of course.
You obviously meant to reinterpret shmemPtr as a pointer to your int, so you can *iPtr in order to read the first int that you expect to be in your shared memory block. If so, this should be:
int* iPtr = (int*) shmemPtr;
That's for C. If you're using C++, you can also help your compiler help you from shooting yourself in the foot:
int* iPtr = reinterpret_cast<int *>(shmemPtr);
Forging ahead, we come next to:
foo* fPtr = (foo*) ((void*) shmemPtr[sizeof(int)]);
Congratulations. shmemPtr[sizeof(int)] is, apparently, shmemPtr[4], or the fifth byte in your shared memory block; and you just dragged your compiler, kicking and screaming, into converting that single byte into a void *, first, then into a foo *.
Obviously, you meant to compute the address of that byte, and then reinterpret it.
foo* fPtr = (foo *)(shmemPtr + sizeof(int)); /* C default */
foo* fPtr = reinterpret_cast<foo *>(shmemPtr + sizeof(int)); /* Preferred in C++ */
This still may or may not be 100% correct, due to platform-specific alignment issues. But this will be a good first start.

C: Copy struct with string to shared memory [duplicate]

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.

using Shmget() in c with structure

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

How to attach an array of strings to shared memory? C

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

Pointers inside shared memory segment

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

Resources