Putting a Struct into Shared Memory - c

I have created two programs a server.c and a client.c. I have a struct that holds an age. I have got the programs working together to read the shared memory and to change the shared memory, however this only works when using one variable in the struct. As soon as i have more than one variable in the struct i get a segmentation fault.
Server.c
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
typedef struct People
{
int age;
int isDone;
} Person;
int main()
{
Person aaron;
Person *p_aaron;
int id;
int key = 5432;
p_aaron = &aaron;
(*p_aaron).age = 19;
(*p_aaron).isDone = 0;
if ((id = shmget(key,sizeof(aaron), IPC_CREAT | 0666)) < 0)
{
perror("SHMGET");
exit(1);
}
if((p_aaron = shmat(id, NULL, 0)) == (Person *) -1)
{
perror("SHMAT");
exit(1);
}
(*p_aaron).age = 19;
printf("Shared Memory Age: %d\n", (*p_aaron).age);
*p_aaron = aaron;
while ((*p_aaron).age == 19)
{
sleep(1);
}
printf("Shared Memory Age Turned To: %d", (*p_aaron).age);
return 0;
}
Client.c
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <string.h>
typedef struct People
{
int age;
} Person;
int main()
{
Person aaron;
Person *p_aaron;
int id;
int key = 5432;
p_aaron = &aaron;
id = shmget(key,sizeof(aaron), IPC_CREAT | 0644);
p_aaron = shmat(id, NULL, 0);
printf("%d", (*p_aaron).age);
(*p_aaron).age = 21;
return 0;
}
Error message from Server.c
SHMGET: Invalid argument
RUN FINISHED; exit value 1; real time: 0ms; user: 0ms; system: 0ms

You don't show any code that deletes the shared memory segment.
If you look at the POSIX specification for shmget(), you will see that the EINVAL error you are reporting can be given if:
[EINVAL]
A shared memory segment is to be created and the value of size is less than the system-imposed minimum or greater than the system-imposed maximum.
[EINVAL]
No shared memory segment is to be created and a shared memory segment exists for key but the size of the segment associated with it is less than size.
I think you may be running into the second case; you're trying to create a bigger shared memory segment than the one that already exists.
Modify your code to clean up behind itself (shmdt(), shmctl()).
Use ipcrm to remove the existing shared memory segment.
Also, as I noted in a comment, you should make sure that the client and server programs agree on the size of the structure in shared memory. Anything else is a recipe for disaster. You should put the structure definition into a header, and both your programs should use that header, and both should be recompiled when you change the definition of the header.

Related

how to make shared memory with specific array size?

I got a question about shared memory and segmentation fault.
I thought that it would be fine to use huge size of memory.
When I checked Shmmax, i found the huge memory can be allocated.
under data is the result of $ipcs -lm
------ Shared Memory Limits --------
max number of segments = 4096
max seg size (kbytes) = 18014398509465599
max total shared memory (kbytes) = 18014398442373116
min seg size (bytes) = 1
#include <stdio.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>
#define ARRAY_SIZE 40000000
int main(int argc, char *argv[]){
int shmid;
void *shared_memory = (void *)0;
shmid = shmget((key_t)1234, sizeof(float), IPC_CREAT|0666);
if (shmid == -1)
{
perror("shmget failed : ");
exit(0);
}
shared_memory = (float *)shmat(shmid, NULL, 0);
if (shared_memory == (void *)-1)
{
perror("shmat failed : ");
exit(0);
}
static float *testx;
testx = (float *)shared_memory;
int k = 0;
for(k;k<400;k++){
testx[k] = 1.12;
}
for(k;k<40000000;k++){
testx[k] = 1.12;
}
}
the program can run the first for loop which has small amount of size
the problem, however, is the second loop with 40,000,000 size
any suggestion what should i edit to run this code?
The reason for your SEGFAULT is that you haven't created enough size segment with shmget.
The argument you passed to shmget as size is sizeof(float) which is just enough to store 1 float.
What you need to do is call shmget like this -
shmget((key_t)1234, sizeof(float)*40000000, IPC_CREAT|0666);
Then you can use all the memory correctly.
The reason that the smaller loop of 400 worked is because shmget creates segments that are multiple of PAGE_SIZE.
So even when you passed sizeof(float), it allocated atleast 1 page which was enough to hold 400 floats but not 40000000.
I hope that clears the confusion.

Share Memory Error: shmat return NULL ,errno(22:Invalid argument) in CentOS6.8

This is I waited two file for share memory ,one is written data to share memory,another is read data from share memory and printf datas; but there are something error.
shm_w.c
#include <stdio.h>
#include <sys/shm.h>
#include <string.h>
#define MAX_MEM 4096
int main()
{
int shmid;
int ret;
void* mem;
shmid=shmget(0x12367,MAX_MEM,IPC_CREAT | 0666 );
printf("shmid is = %d,pid=%d\n",shmid,getpid());
mem=shmat(shmid,(const void*)0,0);
if((int)mem==-1)
{
printf("attach faile.\n");
}
strcpy((char*)mem,"Hello,this is test memory.\n");
ret=shmdt(mem);
return 0;
}
shm_r.c
#include <errno.h>
#include <stdio.h>
#include <sys/shm.h>
#include <string.h>
#define MAX_MEM 4096
int main()
{
int shmid;
int ret;
void* mem;
shmid=shmget(0x12367,MAX_MEM,0);
mem=shmat(shmid,(const void*)0,0);
//printf("%s\n",(char*)mem);
if(mem==(void*)-1)
{
fprintf(stderr,"shmat return NULL ,errno(%d:%s)\n",errno,strerror(errno));
return 2;
}
printf("%s\n",(char*)mem);
shmdt(mem);
return 0;
}
When I in CentOS6.8 compile two .c files, the first time is ok.
Unfortunately, from now on ,I runed she_w.c was right too:
shmid is = 65537,pid=7116.
but when I run shm_r.c , it's appear error:
shmat return NULL ,errno(22:Invalid argument)
so I didn't know what happen it ? I try to solve it ,for example used ipcs -m ,but not appear the shmid.
And I cat /proc/7116/maps :
"No such file or directory"
who can tell me what happen ? And How can I find shmid in CentOS6.6
uname -r:
2.6.32-504.12.2.el6.x86_64
I also use cat /proc/sysvipc/shm | grep 65537, but not appear the shmid.
Bad luck!
Please tell me how to solve the problem if you know ,thanks!
I've downloaded and run your program and am unable to reproduce your [conflicting] results.
To list the active shm segments, use the ipcs command. If you needed to delete the one you created, you can use the ipcrm command.
There are two things I can think of that might be an issue for you, but I'm discounting them because you said that you ran successfully the first time.
The permissions on the segment because you created it incorrectly the first time. ipcs should show this.
When you were debugging your writer program, it may have done something incorrectly. This can be seen with ipcs. If a mistake was made, you can manually remove the segment with ipcrm and try your program again.
The other possibility is that your key collides with some other segment created by another program. This is unlikely for two reasons.
Your key is, in all probably unique. This can also be checked with ipcs
Most programs nowadays use a key of 0x00000000 which is the "private" mode. This is done when a program does a fork but does not do an execvp. The child just calls some function (e.g. child_worker). The parent and child communicate data back and forth using the "private" segment. Since the segment is private, you don't have to worry about having a unique key value.
Because you have two separate programs, using that mode would not work for your use case. You need a non-zero key value so your segment will persist from one program invocation to the next.
For simplicity, I combined both your programs into a single one. I also added an option to delete the shm segment.
Note: I did cosmetic cleanup only. I did not fix any bugs. So, this is just an FYI for what I tested on my system:
#include <stdio.h>
#include <sys/shm.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#define MAX_MEM 4096
#define XID 0x12367
int opt_cmd;
void
writer(void)
{
int shmid;
void *mem;
shmid = shmget(XID,MAX_MEM,IPC_CREAT | 0666);
printf("shmid is = %d,pid=%d\n",shmid,getpid());
mem = shmat(shmid,NULL,0);
if (mem == (void *) -1) {
printf("attach faile.\n");
}
strcpy((char *) mem,"Hello,this is test memory.\n");
shmdt(mem);
}
void
reader(void)
{
int shmid;
void *mem;
shmid = shmget(XID,MAX_MEM,0);
mem = shmat(shmid,NULL,0);
// printf("%s\n",(char*)mem);
if (mem == (void *) -1) {
fprintf(stderr,"shmat return NULL ,errno(%d:%s)\n",
errno,strerror(errno));
exit(2);
}
printf("%s\n",(char *) mem);
shmdt(mem);
}
void
clean(void)
{
int shmid;
shmid = shmget(XID,MAX_MEM,0);
shmctl(shmid,IPC_RMID,NULL);
}
// main -- main program
int
main(int argc,char **argv)
{
char *cp;
--argc;
++argv;
for (; argc > 0; --argc, ++argv) {
cp = *argv;
if (*cp != '-')
break;
switch (cp[1]) {
case 'd':
case 'w':
case 'r':
opt_cmd = cp[1];
break;
default:
break;
}
}
switch (opt_cmd) {
case 'w':
writer();
break;
case 'd':
clean();
break;
default:
reader();
break;
}
return 0;
}
For using the shmget () call, it is necessary to include <sys/ipc.h>.
shm_w.c
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>
#define MAX_MEM 4096
int main()
{
int shmid;
int ret;
void* mem;
shmid=shmget(0x12367,MAX_MEM,IPC_CREAT | 0666 );
printf("shmid is = %d,pid=%d\n",shmid,getpid());
mem=shmat(shmid,(const void*)0,0);
if(mem==(void *) -1)
{
printf("attach faile.\n");
}
strcpy((char*)mem,"Hello,this is test memory.\n");
ret=shmdt(mem);
return 0;
}
shm_r.c
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>
#include <errno.h>
#define MAX_MEM 4096
int main()
{
int shmid;
int ret;
void* mem;
shmid=shmget(0x12367,MAX_MEM,0);
mem=shmat(shmid,(const void*)0,0);
//printf("%s\n",(char*)mem);
if(mem==(void *) -1)
{
fprintf(stderr,"shmat return NULL ,errno(%d:%s)\n",errno,strerror(errno));
return 2;
}
printf("%s\n",(char*)mem);
shmdt(mem);
return 0;
}
$ gcc shm_w.c -o shm_w
$ gcc shm_r.c -o shm_r
$ ./shm_w
shmid is = 2293774,pid=5779
$ ./shm_r
Hello,this is test memory.
$ ./shm_r
Hello,this is test memory.
$ ./shm_r
Hello,this is test memory.

shm_open with mmap giving bus error only in one particular machine

I have the following simple program to create a shared memory. But this is giving
Bus error (core dumped)
This is happening only on one virtual machine and I tried the same code in mutiple VM and in every other machine this is working correctly. But only on one machine this issue is happening. Can anyone point me the issue. All my machines are running on 2.6.32-279.el6.x86_64 . Will this be a kernel issue or application issue?
#include<stdio.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdint.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
const char *shpath="/shm_path_for_data";
void *memPtr;
shm_unlink(shpath);
int fd=0;
fd = shm_open(shpath, O_RDWR|O_CREAT|O_EXCL, 0777);
if(fd<0) {
printf("shm_open failed\n");
return 1;
}
if((ftruncate(fd, getpagesize())) <0) {
printf("Ftruncate failed\n");
return 1;
}
memPtr = mmap(NULL, getpagesize(), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
if(memPtr == MAP_FAILED) {
return 1;
}
strcpy((char *)memPtr, "test input.Just copying something\n");
printf("mapped out: %s\n", (char *)memPtr);
}
You are copying 50 bytes
memcpy((char *)memPtr, "test input.Just copying something\n", 50);
/* BTW: ^ this cast is unneeded */
there are only 36 available, so you are reading beyond the string literal, which is undefined behavior, that's why it works on one machine and fails on another, that's how undefined behavior behaves.
Try
strcpy((char *) memPtr, "Test input. Just copying something\n");

Error when function is in a separate C file

I have a file API.c in which there are certain functions but no main function.
Inside one of the functions , I call shmat() and return :
#include<stdio.h>
#include<stdlib.h>
#include<sys/shm.h>
#include<unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/msg.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include<string.h>
#include<fcntl.h>
#include<errno.h>
#include<sys/stat.h>
#include<signal.h>
int array1[100]; // TO STORE THE SHMIDs
int array2[100]; // TO STORE THE RSHMIDs
char * array3[100]; //TO STORE THE SHMAT ADDRESSES
struct msgbuf
{
long type;
int key;
int rshmid;
int size;
void * addr;
int cmd;
int pid;
int shmid;
};
struct msgbuf my,rec;
int queue_id;
int rshmget(int key,int size)
{
queue_id = msgget(1234, IPC_CREAT | 0666);
printf("1\n");
my.type =1;
my.key=key;
my.size=size;
my.pid=getpid();
//SENDING REQUEST OF CLIENT TO SERVER
if (msgsnd (queue_id, &(my), sizeof (my), 0) == -1)
{
perror("msgsnd");
}
//SERVER CREATES SHARED MEMORY AND WRITES THE REPLY BACK
while(1)
{
if (msgrcv (queue_id, &(rec), sizeof (rec), getpid(), IPC_NOWAIT) != -1)
{
printf("GOT MSG\n");
break;
}
sleep(1);
}
//ARRAY 1 STORES THE ACTUAL SHMID OF SHARED MEMORY
//ARRAY 2 STORES RSHMID , WHICH IS THE ID RETURNED TO CLIENT
array1[key%100] = rec.shmid;
array2[key%100] = rec.rshmid;
return rec.rshmid;
}
void * rshmat(int rshmid, void* addr)
{
my.type =2;
my.rshmid = rshmid;
my.pid=getpid();
int i;
// IDENTIFYING THE SHMID CORRESPONDING TO THE RSHMIDS
for(i=0;i<100;i++)
{
if(array2[i]==rshmid)
break;
}
//ARRAY 3 STORES THE ADDRESSES CORRESPONDING TO EACH SHMAT
array3[i] = shmat(array1[i],NULL,0);
//INFORMING THE SERVER ABOUT THE ATTACHING
if (msgsnd (queue_id, &(my), sizeof (my), 0) == -1)
{
perror("msgsnd");
}
return array3[i];
}
In the file main.c , i am doing this:
int main()
{
queue_id = msgget(1234, IPC_CREAT | 0666);
int id1 = rshmget(521,200);
char * addr1 = (char *)rshmat(id1,NULL);
strcpy(addr1,"FIRST MESSAGE 123456");
}
When trying to executing the strcpy statement , segmentation fault occurs.
But if I keep the API functions and main in same file, strcpy executes successfully.
According to my understanding, shmat() will return a pointer in heap , so it should be accessible from main.
The fact that it works when code is in the same file might indicate that I am not linking the files properly , but other functions in the API.c file are able to return properly.
This is how I am compiling:
gcc -c API.c
gcc -c main.c
gcc main.o API.o -o client
I am using LINUX operating system.
In both files add prototypes for every function that you are using from other file with identifier extern in front of them.
e.g. in file main.c add extern void * rshmat(int shm_id);

Saving array of structs to shared memory

I'm trying to create a piece of shared memory that holds an array of structs. In my current code when i run it i get a segmentation fault. I think I may need to use memcpy but am severely stuck at the moment. Any help would be mch appreciated...
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/shm.h>
#include <unistd.h>
#include "header.h"
int main()
{
key_t key = 1234;
int shmid;
int i = 1;
struct companyInfo * pdata[5];
strcpy(pdata[0]->companyName,"AIB");
pdata[0]->sharePrice = 11.02;
strcpy(pdata[1]->companyName,"Bank Of Ireland");
pdata[1]->sharePrice = 10.02;
strcpy(pdata[2]->companyName,"Permanent TSB");
pdata[2]->sharePrice = 9.02;
strcpy(pdata[3]->companyName,"Bank Od Scotland");
pdata[3]->sharePrice = 8.02;
strcpy(pdata[4]->companyName,"Ulster Bank");
pdata[4]->sharePrice = 7.02;
int sizeOfCompanyInfo = sizeof(struct companyInfo);
int sizeMem = sizeOfCompanyInfo*5;
printf("Memory Size: %d\n", sizeMem);
shmid = shmget(key, sizeMem, 0644 | IPC_CREAT);
if(shmid == -1)
{
perror("shmget");
exit(1);
}
*pdata = (struct companyInfo*) shmat(shmid, (void*) 0, 0);
if(*pdata == (struct companyInfo*) -1)
{
perror("schmat error");
exit(1);
}
printf("name is %s and %f . \n",pdata[0]->companyName,pdata[0]->sharePrice);
exit(0);
}
the header.h file is as follows...
struct companyInfo
{
double sharePrice;
char companyName[100];
};
struct companyInfo * pdata[5];
contains an array of 5 uninitialized pointers. You need too allocate memory for each element in the array before using them:
for (int i = 0; i < 5; i++)
{
pdata[i] = malloc(sizeof(companyInfo));
}
or just declare an array of struct companyInfo as there does not appear to be any need for dynamic allocation:
struct companyInfo pdata[5];
pdata is a table of pointers so you need to create each struct companyInfo using malloc before being able to access them.

Resources