I have 2 programs which need to share memory. Let's call one program A, the other one B.
There is a struct in this format for this purpose:
struct town_t {
int population;
char * name;
}
In program A, data gets written to shared memory using mmap. This works for program A. (It uses strcpy for the name)
Program B's purpose is to simply read the data. This too works with mmap. Accessing the shared memory's population field works without any problems. However, accessing the population field gives a segmentation fault.
Since I used strcpy, the entire string should be in the shared memory right?
I use the following flags to get the pointer to shared memory, which returns no error.
tptr = (struct town_t *) mmap(0, 1024, PROT_READ, MAP_SHARED, shm_fd, 0)
How could I make it so I can actually read the string (char*) from program B?
There's no point in putting a pointer in shared memory. A pointer gives a location inside the address space of a particular process. It would have no meaning to another process with another address space. (With some complicated exceptions such as a pointer to memory allocated before a call to fork accessed by related processes running the same executable.)
You can store the string data itself in shared memory if you wish. For example, this will work:
#define MAX_NAME_SIZE 100
struct town_t
{
int population;
char name[MAX_NAME_SIZE];
};
Related
This question already has answers here:
how to use shared memory to communicate between two processes
(3 answers)
Closed 5 years ago.
I have a variable with a value and I want to share it with the proccesses.
Ex:
typedef struct {
unsigned int a;
unsigned int b;
another_struct * c;
} struct1;
...
struct1 A ={...};
...
Now, I want to create a shared memory region and put the A variable in this region. How can i do this?
Shared memory is an operating system feature (which does not exist in C11). It is not "provided" by the C standard.
I guess that you are coding for Linux. BTW, read Advanced Linux Programming.
Read first shm_overview(7). You'll need to synchronize, so read also sem_overview(7).
You'll get some shared memory segment into a pointer and you'll use that pointer.
First, open the shared memory segment with shm_open(3):
int shfd = shm_open("/somesharedmemname", O_RDWR|O_CREAT, 0750);
if (shfd<0) { perror("shm_open"); exit(EXIT_FAILURE); };
Then use mmap(2) on that shfd:
void* ad = mmap(NULL, sizeof(struct1), PROT_READ|PROT_WRITE, MAP_SHARED,
shfd, (off_t)0);
if (ad==MAP_FAILED) { perror("mmap"); exit(EXIT_FAILURE); };
Then you can cast that address into a pointer:
struct1* ptr = (struct1*)ad;
and use it. (Don't forget to close).
BTW, you don't and you cannot put a variable into a shared memory. You get a pointer to that shared memory and you use it, e.g. ptr->a = 23;
Of course, don't expect the same shared segment to be mapped at the same address (so you can't easily deal with pointer fields like c) in different processes. You probably should avoid pointer fields in shared struct-s.
Notice that C variables exist only at compile time. At runtime, you only have locations and pointers.
PS. Shared memory is a quite difficult inter-process communication mechanism. You should perhaps prefer pipe(7)-s or fifo(7)-s and you'll need to multiplex using poll(2).
Take a look at Beej's Guide to IPC.
I would basically treat the whole shard memory segment as a void* that you can place items at. You could use memcpy, of string.h, to copy A into your shared memory space. However your pointer in A would become invalid and cause a segfault if you attempted to use it in another process connected to shared memory segment.
I have a "Server" and a "Client" process, they "communicate" through a struct shared memory.
typedef struct
{
char* buffer;
int cards;
int maxCards;
} myStruct
Both "Server" and "Client" processes can get/set the values of the two int types, but the thing is different with the buffer (we use this as a pointer to chars): the "Server" allocates memory, like:
(*sharedMem).buffer = (char*) malloc(sizeof(char)*maxCards);
has the ability to set or get values:
(*sharedMem).buffer[0] = 'a';
printf("%c\n",(*sharedMem).buffer[0]);
but when the "Client" process tries to access a char:
printf("%c\n",(*sharedMem).buffer[0])
It collapses with Segmentation Fault Core Dumped error.
Is there a way to solve this problem without using an Array or List? Any Ideas?
Both processes have different address spaces, so pointers in one are generally meaningless in the other (except for possibly NULL).
If you need to exchange more data between processes, you could use
pipes (see the pipe() system call)
shared memory (see POSIX shm* calls)
memory mapped files (see mmap())
use an array-of-char in your struct
If I have some struct
struct processData{
int *a;
int *b;
}
And I set up my shared memory ID as
int shmid = shmget(1234, sizeof(processData), IPC_CREAT | 0666);
Where 1234 is my key
Would I set up my actual shared struct like this..
processData* pData =(processData *) shmat(shmid, NULL, 0);
And then shouldn't I be able to change things around in seperate processes like:
pData -> a = SOME_NUMBER;
When I do this, all of my processes aren't interacting with the same piece of memory and I'm clueless why.
The members *a and *b in your structure processData are integer pointer type. Attaching a shared memory to your process does not make them point to a memory location inside the shared memory. Secondly the shared memory that you are creating using the shmget() function in the first place has only enough space to hold a processData data type. You will need some additional memory space to hold two int data types which *a and *b will point to and off course these memory spaces should also be shared so that other processes can access it.
So the solution to your problem will be something like this.
//Create the shared memory to hold the structure.
shmid=shmget(KEY1,sizeof(struct processData),IPC_CREAT|0666);
//Attach it to the process.
pData=shmat(shmid,0,0);
//Create a shared memory to hold an integer which will be pointed by *a.
shmid=shmget(KEY2,sizeof(int),IPC_CREAT|0666);
//Attach the shared memory to the location pointed by *a.
pData->a=shmat(shmid,0,0);
//Create a shared memory to hold an integer which will be pointed by *b.
shmid=shmget(KEY3,sizeof(int),IPC_CREAT|0666);
//Attach the shared memory to the location pointed by *b.
pData->b=shmat(shmid,0,0);
This is the way you should create and attach the shared memories to all the processes that are going to access the shared memory.
(Note: You do not need to create 3 separate shared memories using 3 separate key values. It can also be done by creating a single chunk of shared memory of sufficient size using 1 key and then seeking the pointers to the their respective positions inside the shared memory. It is a little complicated that is why I have given a simple example for better understanding.)
Now coming to the next problem. Since you are using pointers in your structure you cannot assign values to them like this, pData -> a = SOME_NUMBER;. Because, pData -> a is a pointer, to assign some value to it you need to deference it. So it will be done like this.
*(pData -> a) = SOME_NUMBER;
Similarly to read the value in other processes will also need to derefence it there.
SOME_NUMBER = *(pData -> a);
When in your local process you copy the pointer of a int into your struct with:
pData -> a = some-pointer;
you are actually copying the logical memory address (which is valid in your current process) into the shared memory.
When from another process you read such value, it will be a logical memory address occupied by something else (possibly also out of current addressable space).
For a project I have to do I have to use:
void *ptr = mmap(NULL, N, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, 0, 0);
where N is the number of bytes of RAM to ask for from the OS.
When this statement executes, what is ptr pointing to? Is it pointing to the start of the shared memory between processes? Also, if in this memory space say I want to store 1,000 int pointers, do I need to have N = 1000 * sizeof(int *);?
And assuming that I am correct, where is the second place in memory that I can store something? Is it at ptr + 1 or ptr + 4 because an int * is 4 bytes on a 32-bit system?
Thank I appreciate it.
Since mmap is not defined in standard C, I'm assuming you're using this: http://linux.die.net/man/2/mmap
The return value is a pointer to the memory:
On success, mmap() returns a pointer to the mapped area. On error, the
value MAP_FAILED (that is, (void *) -1) is returned, and errno is set
appropriately. On success, munmap() returns 0, on failure -1, and
errno is set (probably to EINVAL).
You are calculating N correctly.
However, you'll probably have problems using addition with a void* pointer, so cast your pointer to an int* to do addition.
int* p = ptr;
int* nextP = p + 1;
void *ptr = mmap(
NULL, N,
PROT_READ|PROT_WRITE,
MAP_SHARED|MAP_ANONYMOUS,
0, 0);
where N is the number of bytes of RAM to ask for from the OS.
Here's one important constraint you must follow: N must be an integer multiple of the system's page size (or if mapping hugepages, an integer multiple of a hugepage's size). Usually it's 4096 bytes, but the actual value is reported by sysconf(PAGESIZE).
When this statement executes, what is ptr pointing to?
The beginning of the portion of the address space this particular mapping has been created at. With a non-anonymous mapping you can map the same memory several times at different addresses (this is a neat trick to implement transparent ringbuffers).
Is it pointing to the start of the shared memory between processes?
At this particular point, it's just pointing to some memory. There's nothing shared about this yet. The mapping however will be shared after a fork (or a clone with the right flags); this already is process shared memory, but you probably will want to also execve into a different executable.
Also, if in this memory space say I want to store 1,000 int pointers, do I need to have N = 1000 * sizeof(int *);?
Well, technically you need to have n * sysconf(PAGESIZE) == N >= 1000 * sizeof(int*); however why do you want to share int pointers? Unless you have those pointers point into (another) shared memory region, that's at the same address in every process that uses those pointers, it's pretty useless to share pointers. Of course after a fork the address space of the processes is identical, but a execve will unmap all previous mappings, and you'd have to use non-anonymous mmap with MAP_FIXED and a filedescriptor obtained with, e.g., memfd_create, and use the first parameter of mmap where exactly to map it at (which might fail, or overmap a previously existing mapping).
What you can sensibly share is offsets and bulk data.
Oh, and just to eliminate any confusion: Placing a pointer in a shared memory region will not automagically share the memory it's pointing to.
And assuming that I am correct, where is the second place in memory
that I can store something? Is it at ptr + 1 or ptr + 4 because an int
* is 4 bytes on a 32-bit system?
It's just plain memory, that you can use, as if it were allocated with malloc. Cast the pointer to whatever type you see fit and use it as usual.
My process is accessing a shared memory that is already created. The pointer attached to the shared memory is a structure containing a pointer and 2 or 3 variables.
eg:
typedef struct _info_t{
int id;
char c;
}info_t;
typedef struct _details_t{
int buff_id;
info_t* info;
}details_t;
details_t* details = shmat(shmid,(void*)0,0);
printf("\n %d \n",details->info->id); // gives me a segmentation fault
If a memory segment is shared between more than one process, there's no guarantee it will be mapped at the same address, so you cannot store pointers in shared memory segment. Try to avoid using pointers, use offsets or arrays (if possible).
shmat(2) is a syscall (on Linux). It may fail. So at least code
details_t* details = shmat(shmid,(void*)0,0);
if (!details) { perror("shmat"); exit(EXIT_FAILURE); };
and you cannot put (easily) pointers in shared memory, since the address is specific to each process.