Why a child process fails to write to a shared memory? - c

I have a simple program that writes to a shared memory and reads from a shared memory, but while reading I am getting segmentation fault.
When I am debugging, the child process is not writing information to the shared memory, and after that, the parent process is trying to read from the shared memory which has no data and is throwing segmentation fault at 1st printf, in parent printf("%d\n",ptr->nread);
Why the child process is not able to write data to the shared memory? (it is failing at ptr->nread=20; line)
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#define SIZE 5*1024
struct databuf{
int nread;
char *buf;
int xyz;
};
struct databuf* ptr;
main()
{
int shmid,pid;
shmid = shmget((key_t)1,SIZE,IPC_CREAT|0777);
pid = fork();
if(pid==0)
{
ptr = (struct databuf *)shmat(shmid,(char*)0,0);
ptr->nread=20;
ptr->buf=ptr+sizeof(ptr->nread);
strcpy(ptr->buf, "abc");
ptr->xyz=20;
}
else
{
wait(0);
ptr = (struct databuf *)shmat(shmid,(char*)0,0);
printf("%d\n",ptr->nread);
printf("%s\n",ptr->buf);
printf("%d\n",ptr->xyz);
}
return 0;
}

If ptr->nread is failing then you should put a error checking code something like this, before acessing the ptr.
ptr = (struct databuf *)shmat(shmid,(char*)0,0);
if (data == (struct databuf *)(-1)) {
perror("shmat failed");
exit(1);
}
ptr->nread=20;
Ref: http://linux.die.net/man/2/shmat
ptr->buf=ptr+sizeof(ptr->nread);
could be written as:
ptr->buf=(char*)ptr+sizeof(struct databuf)+ptr->nread;
or
ptr->buf=(char*)ptr+ptr->nread;
The string can now be accessed in the parent process.
Brief Explanation:
If you're using shared memory, you have to make sure all the data you want to access in other processes is in the shared memory segment. Leave the data at a specified offset(in your case ptr+ptr->nread) in the memory segment. And be careful not to overwrite the existing data in the shared memory. sizeof(ptr->nread) will yield the sizeof(int).

Leaving to one side all the other issues with the code, I think:
shmid = shmget((key_t)1, SIZE, IPC_CREAT|0777) ;
is probably a mistake, unless you can (somehow) guarantee that (key_t)1 is not in use elsewhere. For parent-child communication, as in this case, the simpler approach is:
shmid = shmget((IPC_PRIVATE, SIZE, IPC_CREAT|0777) ;
In general, when something apparently inexplicable happens, I find it helps to make sure I have checked for error returns. In this case: shmget() returning -1 or shmat() returning -1... and I suspect that both have happened.
Worked solution added by #WhozCraig:
The following example works, and likely does what you're trying to accomplish. Pay note to how we calculate the address we store in ptr->buf after we home ptr to our shared memory base address. We have to leave room for the structure, so we calculate the address to start the first byte past the structure back-side.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
struct databuf
{
int nread;
char *buf;
int xyz;
};
#define SIZE (5*1024)
int main()
{
// acquire shared memory first (read/write access)
int shmid = shmget(IPC_PRIVATE, SIZE, IPC_CREAT|0666);
if (shmid == -1)
{
perror("Failed to acquire shared emory.");
exit(EXIT_FAILURE);
}
// fork child process
pid_t pid = fork();
// both parent and child need this. may as well do both before
// special casing child vs. parent logic.
struct databuf *ptr = shmat(shmid,(char*)0,0);
if (ptr == (void*)(-1))
{
perror("Failed to map shared memory to our process");
exit(EXIT_FAILURE);
}
// child process
if (pid==0)
{
ptr->nread = 20;
ptr->buf = ((char*)ptr) + sizeof(*ptr);
strcpy(ptr->buf, "abc");
ptr->xyz = 30;
}
// parent process
else
{
wait(NULL);
printf("ptr = %p, ptr->buf = %p\n", ptr, ptr->buf);
printf("%d\n",ptr->nread);
printf("%s\n",ptr->buf);
printf("%d\n",ptr->xyz);
}
return 0;
}
Sample Output (varies by system obviously)
ptr = 0x80000, ptr->buf = 0x8000c
20
abc
30
it is worth noting that this:
ptr->buf = ((char*)ptr) + sizeof(*ptr);
could be written as the following, using typed-pointer math:
ptr->buf = (char*)(ptr + 1);
The location where the cast is applied is important. The first applies it before we do any math, so we need to account for octet count. The second applies it after the pointer math, so simply adding one to to the typed pointer ptr will move us to the memory immediately past our ptr structure base.
Best of luck.

If you have a pointer from type T, an addition by one to the pointer will increment it by sizeof(T).
So you have to replace:
ptr->buf=ptr+sizeof(ptr->nread);
to
ptr->buf= ((char*)ptr )+sizeof(ptr->nread);
If you don't do that, your pointer will be incremented by sizeof(T)^2.

Related

POSIX unnamed semaphore in shared memory is not responding to post or wait

I have unnamed semaphores that I am placing in shared memory in one process following the method that I found here on SO
In P0:
/* addr is a pointer to the base of the shared memory area */
sem_t *sem = (sem_t*) addr;
void *usableSharedMemory = (char*) addr + sizeof(sem_t)
sem_init(sem, 1, 0);
In P1:
if ((addr = mmap(NULL, SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) {
exit(EXIT_FAILURE);
}
sem_t *my_sem = (sem_t*) addr;
...
sem_post(my_sem);
...
sem_wait(my_sem);
If I call sem_getvalue(my_sem) before and after a post or wait, the value of the semaphore does not change. I have prints like this to debug:
int v = 0;
v = sem_getvalue(rsem, &v);
printf("BEFORE-POST:%d\n", v);
sem_post(rsem);
v = sem_getvalue(rsem, &v);
printf("AFTER-POST:%d\n", v);
Before and after the call to sem_post, the semaphore value is zero (0).
I haven't used this kind of semaphore before, but I see a number of things one could trip over.
I'm not excited about the funky pointer math that the other SO post was suggesting, and I can't tell from here whether both processes are actually talking to the same chunk of shared memory.
A good idea in this case is to avoid the pointer math and use a struct to overlay the shared memory segment so you have a clear organization, and add a magic number so everybody can tell if they're getting a valid segment or some random bogus memory:
#define MYSHM_MAGIC 12345987 // can be anything random-ish
struct mysharedmem {
int magicvalue;
sem_t MySemaphore;
void *UsableMemory;
};
This structure overlays whatever your shared memory segment is and allows you to use consistent and more readable access methods, especially if you add additional variables that all sides agree on. The code that creates the segment should initialize it:
// in P1 ?
struct mysharedmem *myshm = mmap(NULL, SIZE, ...);
myshm->magic = MYSHM_MAGIC;
sem_init(&myshm->MySemaphore, 1, 0);
...
and then in the other process, once you obtain the shared address, actually ask the segment "did you come from the place I think?" by checking the magic number. The value of the number doesn't matter, only that both sides agree.
// In P0 ?
struct mysharedmem *myshm = addr; // obtained somehow
if (myshm->magic != MYSHM_MAGIC)
{
error - we don't see the special magic number
}
.. do stuff
How does P0 obtain a handle to the shared memory segment created by the P1?

shmctl throws "cannot allocate memory" in C

I have this code:
#define SHMSIZE 8388606
int main()
{
int shmid;
void *shmPtr;
char *shm;
if ((shmid = shmget(IPC_PRIVATE, sizeof(char) * SHMSIZE , IPC_CREAT | 0666)) < 0) {
perror("shmget");
exit(1);
}
if ((shmPtr = shmat(shmid, NULL, 0)) == (char *) -1) {
perror("shmat");
exit(1);
}
shm = (char *)shmPtr;
strncpy(shm, "0\n", 2);
struct shmid_ds shmid_ds;
int rtrn = shmctl(shmid, SHM_LOCK, &shmid_ds);
if(rtrn < 0) {
perror("shmctl");
exit(1);
}
else {
printf("Nailed it\n" );
}
return 0;
}
Running it, I get the error:
shmctl: Cannot allocate memory
Defining smaller value for SHMSIZE fixes the error, but what I found weird is that this error is thrown in the "shmctl" part. My reasoning tells me that this error is supposed to be thrown in the "shmget" part.
How does this code runs successfully through the shmget() call? Did I miss something important?
Read this, It may help you to solve your problem:
The caller can prevent or allow swapping of a shared memory segment
with the following cmd values:
SHM_LOCK (Linux-specific)
Prevent swapping of the shared memory segment. The caller
must fault in any pages that are required to be present
after locking is enabled. If a segment has been locked,
then the (nonstandard) SHM_LOCKED flag of the shm_perm.mode
field in the associated data structure retrieved by
IPC_STAT will be set.
SHM_UNLOCK (Linux-specific)
Unlock the segment, allowing it to be swapped out.
In kernels before 2.6.10, only a privileged process could employ
SHM_LOCK and SHM_UNLOCK. Since kernel 2.6.10, an unprivileged
process can employ these operations if its effective UID matches the
owner or creator UID of the segment, and (for SHM_LOCK) the amount of
memory to be locked falls within the RLIMIT_MEMLOCK resource limit
(see setrlimit(2)).
Try this:
int rtrn = shmctl(shmid, IPC_STAT, &shmid_ds);

Pointers within shared memory

I'm using shared memory (shm_open/mmap) to keep track of some state. In my shared memory I have the struct:
typedef struct fim_t {
uint64_t num_procs;
uint64_t num_numa;
int64_t *numa_nodes[MAX_FIM_NUMA];
int64_t procs[MAX_FIM_PROC];
}fim_t;
What I want to do is load process IDs in the procs array and then have the numa_nodes array point to procs array values so I can manipulate the value in one spot and have it change across all the references. My understanding is that setting the numa_nodes references to addresses of the procs array should not be a memory access violation because their addresses are both entirely within the shared memory segment. However I get a seg fault when I try to access the value which tells me that my previous statement must be false.
Here is example code:
int main(){
int fd;
int init_flag = 0;
if((fd = shm_open("fim", O_RDWR | O_CREAT | O_EXCL, S_IRWXU)) > 0){
printf("creating shared memory\n");
init_flag = 1;
} else {
printf("opening shared memory\n");
fd = shm_open("fim", O_RDWR, S_IRWXU);
}
if (-1 == fd) {
printf("fd is negative\n");
abort();
}
if ((1 == init_flag) && -1 == ftruncate(fd, sizeof(fim_t))){
printf("ftruncate failed %d\n", errno);
abort();
}
fim_t *fim = mmap(NULL, sizeof(fim_t), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if(MAP_FAILED == fim){
printf("mmap failed\n");
abort();
}
if(init_flag){
fim->num_procs = 1;
my_rank = 0;
for(int x=0;x<MAX_FIM_PROC;x++){
fim->procs[x] = 0;
}
fim->numa_nodes[0] = &(fim->procs[0]);
} else {
my_rank = __sync_fetch_and_add(&(fim->num_procs),1);
fim->procs[my_rank] = my_rank;
fim->numa_nodes[0] = &(fim->procs[my_rank]);
}
printf("my rank is: %"PRId64"\n",my_rank);
sleep(5);
printf("my numa val is %"PRId64"\n",*fim->numa_nodes[0]);
printf("rank %"PRId64" is going down\n", my_rank);
// SHUTDOWN
uint64_t active = __sync_sub_and_fetch(&(fim->num_procs),1);
printf("num active is now %"PRId64"\n", active);
close(fd);
shm_unlink("fim");
return 0;
}
What I expect/hope to happen would be that I run one process then immediately start another and the first process prints "my numa val is 1" (due to the second process setting the numa_node[0] value) and both exit cleanly. However, the second process runs fine, but in the first process seg faults (memory access) at the print statement for numa_node[0] (after the sleep).
So here's my question: Am I doing something wrong or is my approach unworkable? If it is unworkable, is there another way to achieve the result I'm looking for?
You haven't done anything to arrange for all users of the shared memory to map it at the same virtual address. Some *nix systems will do this by default, but most will not.
Either try to map your segment at a fixed address (and deal with failure - this may not succeed) - or store offsets in the shared memory, not actual pointers.
My understanding is that setting the numa_nodes references to addresses of the procs array should not be a memory access violation because their addresses are both entirely within the shared memory segment.
The problem is that different processes map the shared memory to different addresses.
fim_t *fim = mmap(NULL, sizeof(fim_t), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
fim will have different values in different processes. Print it out to check this.
This causes the pointers to the int64_t procs[MAX_FIM_PROC] elements to be different in different processes.
fim is <addr1> in process 1
fim is <addr2> in process 2
&fim->procs[0] will be different in two processes
&fim->procs[0] is <addr1> + <offset> in process 1
&fim->procs[0] is <addr2> + <offset> in process 2
Because these are different values, they cannot be shared between processes. Valid pointer in one process will not be valid in another process.
There are two possible solutions to this.
Force the shared memory to map to the same address in all processes. mmap has an option to accomplish this. Then you can share pointers to elements in the shared memory across processes.
Do not share pointers in shared memory. Share indexes instead.

Shared memory in C - getting NULL when I try to access my structure with a char array in it

I'm trying to wrap my mind around the concept of shared memory by making a multiprocessed application that will utilize shared memory and semaphores, but I'm running into an issue with getting the shared memory to work.
The program creates a child which shares a database with the parent. I have an array of structs formatted like this:
struct customer{
char accName[100];
float balance;
int inService;
}
and a shared memory segment initialized like this: (the array needs to be able to hold twenty customers max)
struct customer **shmPtr;
key_t key = 1234;
int shmID;
shmID = shmget(key, sizeof(struct customer)*20, IPC_CREAT | SHM_R | SHM_W);
shmPtr = shmat(shmID, NULL,0);
I add "customer" structs to the shared memory segment with the following code:
struct customer* newCustomer = malloc(sizeof(struct customer));
strcpy(newCustomer->accName, name);
newCustomer->balance = 100;
newCustomer->inService = 0;
shmPtr[numOfClients] = newCustomer;
numOfClients++;
And for some reason, whenever I try to print the information of the clients from shmPtr (called by the PARENT thread), I'm just getting null stuff. I print it out with the following line:
struct customer *currCust = shmPtr[i];
if(currCust == NULL)
{
break;
}
printf("NAME: %s --- Balance: %f - Session Status: %d\n", currCust->accName, currCust->balance, currCust->inService);
And just get a blank space for the %s, 0.0000 for the %f(despite initializing it to 100), and a 0 for %d.
Can anybody point me to what I'm doing wrong? Just when I thought I had memory operations figured out in C, this is throwing another wrench into my mind and I can't figure out what I'm doing wrong as I'm relatively new to the concept of shared memory. I'm guessing it might have something to do with malloc() as the parent can't read stuff that the child has malloc'd, but how would I go about initializing the "Customer" struct within the shared memory?

How does one keep an int and an array in shared memory in C?

I'm attempting to write a program in which children processes communicate with each other on Linux.
These processes are all created from the same program and as such they share code.
I need them to have access to two integer variables as well as an integer array.
I have no idea how shared memory works and every resource I've searched has done nothing but confuse me.
Any help would be greatly appreciated!
Edit: Here is an example of some code I've written so far just to share one int but it's probably wrong.
int segmentId;
int sharedInt;
const int shareSize = sizeof(int);
/* Allocate shared memory segment */
segmentId = shmget(IPC_PRIVATE, shareSize, S_IRUSR | S_IWUSR);
/* attach the shared memory segment */
sharedInt = (int) shmat(segmentId, NULL, 0);
/* Rest of code will go here */
/* detach shared memory segment */
shmdt(sharedInt);
/* remove shared memory segment */
shmctl(segmentId, IPC_RMID, NULL);
You are going to need to increase the size of your shared memory. How big an array do you need? Whatever value it is, you're going to need to select it before creating the shared memory segment - dynamic memory isn't going to work too well here.
When you attach to shared memory, you get a pointer to the start address. It will be sufficiently well aligned to be used for any purpose. So, you can create pointers to your two variables and array along these lines (cribbing some of the skeleton from your code example) - note the use of pointers to access the shared memory:
enum { ARRAY_SIZE = 1024 * 1024 };
int segmentId;
int *sharedInt1;
int *sharedInt2;
int *sharedArry;
const int shareSize = sizeof(int) * (2 + ARRAY_SIZE);
/* Allocate shared memory segment */
segmentId = shmget(IPC_PRIVATE, shareSize, S_IRUSR | S_IWUSR);
/* attach the shared memory segment */
sharedInt1 = (int *) shmat(segmentId, NULL, 0);
sharedInt2 = sharedInt1 + 1;
sharedArry = sharedInt1 + 2;
/* Rest of code will go here */
...fork your child processes...
...the children can use the three pointers to shared memory...
...worry about synchronization...
...you may need to use semaphores too - but they *are* complex...
...Note that pthreads and mutexes are no help with independent processes...
/* detach shared memory segment */
shmdt(sharedInt1);
/* remove shared memory segment */
shmctl(segmentId, IPC_RMID, NULL);
From your comment it seems you're using IPC_PRIVATE, and that definitely looks wrong ("private" kinds of suggest it's not meant for sharing, no?-). Try something like:
#include <sys/ipc.h>
#include <sys/shm.h>
...
int segid = shmget((key_t)0x0BADDOOD, shareSize, IPC_CREAT);
if (segid < 0) { /* insert error processing here! */ }
int *p = (int*) shmat(segid, 0, 0);
if (!p) { /* insert error processing here! */ }
This guide looks useful: http://www.cs.cf.ac.uk/Dave/C/node27.html. It includes some example programs.
There are also Linux man pages online.
Shared memory is just a segment of memory allocated by one process, with a unique id, and the other process will also make the allocation, with the same id, and the size of the memory is the sizeof the structure that you are using, so you would have a structure with 2 integers and an integer array.
Now they both have a pointer to the same memory, so the writes of one will overwrite whatever else was there, and the other has immediate access to it.

Resources