Saving array of structs to shared memory - c

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.

Related

Initializing a struct results in Segmentation Fault

I have a simple struct as follows:
typedef struct {
char *raw_headers;
char headers[128][512][512];
} HTTPrequest;
Now in the same file I have a function as follows:
void init_request(char *raw_headers) {
HTTPrequest request;
request.raw_headers = raw_headers;
}
This results in a Segmentation Fault when running the output binary.
I compile the file, as
$ gcc Server.c -o Builds/debug
And, I run the executable as,
$ ./Builds/debug
This is my original file as requested:
Parser.h
typedef struct {
char *raw_headers;
char headers[128][512][512];
} HTTPrequest;
void parser_init(char *raw_headers) {
char *token, *key_value = NULL;
token = strtok(raw_headers, "\r\n");
int line_counter = 1
HTTPrequest request;
request.raw_headers = raw_headers;
while (token) {
char *line = token;
if(line_counter != 1) {
}
token = strtok(NULL, "\r\n");
line_counter++;
}
}
Server.h
int socket_create() {
return socket(AF_INET, SOCK_STREAM, 0);
}
void infinite_loop(int socketFD) {
char buffer[1024];
memset(buffer, 0, sizeof(buffer));
printf("Starting infinite loop, don't worry, everything would be okay!\n");
do {
int connectionFD = accept(socketFD, (struct sockaddr*) NULL, NULL);
/*Accept is a blocking call! The following code wont execute until, accept() returns.*/
strcpy(buffer, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\nHello!");
write(connectionFD, buffer, sizeof(buffer));
char request[2048];
memset(&request, 0, sizeof(request));
read(connectionFD, &request, sizeof(request));
printf("Request received!\n");
// Init the parser.
parser_init(request);
close(connectionFD);
} while (true);
}
Server.c
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <stdbool.h>
#include "Parser.h"
#include "Server.h"
void main() {
struct sockaddr_in listeningAddr;
int socketFD = socket_create();
memset(&listeningAddr, 0, sizeof(listeningAddr));
listeningAddr.sin_family = AF_INET;
listeningAddr.sin_addr.s_addr = htonl(INADDR_ANY);
listeningAddr.sin_port = htons(8070);
bind(socketFD, (struct sockaddr*) &listeningAddr, sizeof(listeningAddr));
if(listen(socketFD, 5) == -1) {
printf("Woah there! I couldn't startup the server!");
}
infinite_loop(socketFD);
}
Also, the error message: Segmentation fault (core dumped)
The headers member (32MiB) makes your struct too big to fit on most default system-provided stacks (8MiB on Linux).
Make it smaller, and trivial MCVEs such as:
typedef struct {
char *raw_headers;
char headers/*[128]*/[512][512]; }
HTTPrequest;
void init_request(char *raw_headers) {
HTTPrequest request;
request.raw_headers = raw_headers;
}
int main()
{
init_request("hello, world");
}
will work, although, initializing an on-stack struct only to have it discarded by the function return is not very meaningful
(Initializer functions will usually take a pointer to the struct they're initializing and initialize the object through that pointer.)
Although your HTTPrequest is simple, it is over 32MB in size. You most probably encounter a stack overflow...
That's a typical case of Stack overflow!
The reason is that your struct HTTPrequest is too big, more than 32 MB. The 3D array has a size of 128 * 512 * 512 = 33554432 bytes, since it's of type char.
In any case, 3D arrays are barely used, and only in special cases. Reconsider your design and try to make that array a 2D instead of a 3D.

String concatenation maintaining destination length

I have a basic server-client program that I'm writing in C and I am stuck with an issue regarding strings and concatenation.
Basically I have some strings (in the example below just 2) that I have to put into a buffer which size is determined by:
total # of registered people * 33
Both the strings in the example have a length which is much less than the length of the buffer. I want to obtain something like this after the concatenation:
[0] [32]
people_list=Mark Amy\0;
where Mark(which is inserted secondly) is right at the start of the buffer (people_list) and Amy is 32 characters away from the start of Mark (I hope I've made myself clear).
This is because the client code was given to me and I cannot modify it. The client code takes the buffer and reads the first element, then jumps of 32 chars and reads again.
The output I get from the printf of the client is this:
connected to server
Registered people:
Mark
while I'd like this:
connected to server
Registered people:
Mark
Amy
The communication is implemented through sockets, which I have already checked, but if you wish to suggest some changes I would appreciate that.
Server code:
#include <stdio.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <errno.h>
#define LENGTH 32
struct person {
char name[LENGTH+1];
struct person *next;
struct person *prev;
};
struct person *p_head = NULL;
void addPerson(char* name_p){
struct person *new = (struct person*) malloc(sizeof(struct person));
strcpy(new->name, name_p);
new->name[LENGTH]='\0';
new->next=p_head;
new->prev=NULL;
p_head=new;
}
int main(){
int fd_ser;
int fd_c;
int N=100;
char buf[N];
int times=0;
char* path="tmp/sock";
struct sockaddr_un sa;
unlink(path);
sa.sun_family=AF_UNIX;
strncpy(sa.sun_path,(char*) path, sizeof(sa.sun_path));
if((fd_ser=socket(AF_UNIX,SOCK_STREAM,0))<0){ //socket
perror((const char*) (size_t) errno);
exit(EXIT_FAILURE);
}
if ( bind(fd_ser,(struct sockaddr *)&sa, sizeof(sa))<0){
perror("bind\n");
}
listen(fd_ser,10); //listen
struct sockaddr_un addr;
int addr_size= sizeof(struct sockaddr_un);
fd_c=0;
while( (fd_c=accept(fd_ser,(struct sockaddr*) &addr, (socklen_t*)&addr_size))<0){
printf("waiting for connections...\n");
sleep(2);
}
//initialize list of people
char* Amy="Amy";
char* Mark="Mark";
addPerson(Amy);
addPerson(Mark);
//now concat the name strings in a buffer to be sent to the client
char* people_list;
unsigned int list_len;
int value;
struct person* ptr=(struct person*) malloc(sizeof(struct person));
ptr=p_head;
int offset=0;
int i=0;
while(ptr!=NULL){
i++;
people_list=realloc(people_list,i*LENGTH); //every single name has to be LENGTH characters
strcpy(&people_list[offset],ptr->name);
ptr=ptr->next;
offset=offset+LENGTH;
}
people_list[i*LENGTH]='\0';
list_len=(i*LENGTH)+1;
value=write(fd_c, &(list_len), sizeof(unsigned int));
if(value==-1){
perror("write length");
return -1;
}
int toWrite=list_len;
char *toRead=people_list;
while(toWrite>0){
value=write(fd_c, toRead, toWrite);
if(value==-1){
perror("write data");
return -1;
}
toWrite=toWrite-value;
toRead=toRead+value;
if(toRead<=people_list + list_len) break;
}
close(fd_c);
close(fd_ser);
return 0;
}
Client code:
#include <stdio.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <assert.h>
#include <errno.h>
#define MAX_LENGTH 2048
#define LENGTH 32
int main(){
int fd_cli;
char* path="tmp/sock";
struct sockaddr_un sa;
sa.sun_family=AF_UNIX;
strncpy(sa.sun_path,(char*) path, sizeof(sa.sun_path));
if( (fd_cli = socket(AF_UNIX, SOCK_STREAM, 0))==-1){
perror((const char*) (size_t) errno);
return -1;
}
while( (connect(fd_cli,(struct sockaddr*)&sa, sizeof(struct sockaddr_un))) == -1 ) {
if ( errno == ENOENT ) { sleep(1); }
else perror("connect:");
errno=0;
sleep(1);
}
printf("connected to server\n");
int value;
unsigned int len_data;
value=read(fd_cli,&(len_data),sizeof(unsigned int));
if(value==-1){
perror("read length");
return -1;
}
char* buffer=malloc(len_data*sizeof(char));
int toRead=len_data;
char *toWrite=buffer;
while(toRead>0){
value=read(fd_cli, toWrite, toRead);
if(value==-1){
perror("read buffer");
return -1;
}
toRead=toRead-value;
toWrite=toWrite+value;
if(toWrite<=buffer + len_data) break;
}
int people_n = len_data / (LENGTH+1);
assert(people_n > 0); //proceeds only if there is at least one person registered
printf("Registered people:\n");
for(int i=0,p=0;i<people_n; ++i, p+=(LENGTH+1)) {
printf(" %s\n", &buffer[p]);
}
close(fd_cli);
return 0;
}
I really hope I've explained the problem clearly! Thank you for your help!
It's printing only the first user (Mark) because of this line:
int people_n = len_data / (LENGTH+1);
In this example, len_data = 65, LENGTH = 32. So when you are adding 1 to LENGTH, it will return 1 (65/33 = 1.96 => you get 1). Then it only prints the first user.
Consider using Valgrind. It will help you checking the use of the memory.

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.

Putting a Struct into Shared Memory

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.

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

Resources