Find size of shared memory in C shmget - c

I was wondering if it is possible to obtain the size of a shared memory segment in C created from shmget without having the size of the segment as a part of the data? I'm trying to allocate a dynamic int array and need to find the size of the array in the child process.
Main process:
int sizeOfArray = 3;
int shm = shmget(IPC_PRIVATE, sizeof(int) * sizeOfArray, IPC_CREAT | 0666);
int *a = (int*) shmat(shm, NULL, 0);
a[0] = 0;
a[1] = 1;
a[2] = 2;
if (fork() == 0) {
char *args[3];
char shmID[11];
bzero(shmID, 11);
intToString(shm, shmID); // custom function that does what the name implies
args[0] = "slave";
args[1] = shmID;
args[2] = NULL;
execvp("slave", args);
return -1;
}
wait(NULL);
shmdt((void*) a);
shmctl(shm, IPC_RMID, NULL);
Child process (slave):
int shm = atoi(argv[1]);
int *ptr = (int*) shmat(shm, NULL, 0);
//TODO: find length of int array in shared memory
shmdt((void*) ptr);
return 0;

I found that if you use shmctl and the IPC_STAT flag, you can get the number of bytes allocated to the shared memory segment. Then you can just divide it by sizeof(int) to get the size of your array.
struct shmid_ds buf;
shmctl(shm, IPC_STAT, &buf);
int length = (int) buf.shm_segsz / sizeof(int);

Related

Passing by reference to an char array

This is my code.
int main(int argc, char *argv[]) {
int shmid;
char *shmc;
char *shmp;
pid_t pid;
shmid = shmget(IPC_PRIVATE, 3 * sizeof(char), 0666 | IPC_CREAT);
pid = fork();
if (pid == 0) {
shmc = (char *) shmat(shmid, NULL, 0);
shmc[0] = argv[1];
shmc[1] = argv[2];
shmc[2] = argv[3];
shmdt(shmc);
}else{
wait(NULL);
shmp =(char*) shmat(shmid,NULL, 0);
char *arg_vec[]={"./test", &shmp[1], &shmp[2],NULL};
execv("./test", arg_vec);
shmdt(shmp);
shmctl(shmid, IPC_RMID, NULL);
}
}
char *arg_vec[]={"./test", &shmp[1], &shmp[2],NULL};
In this line, I was trying to pass 2, 3 to another c program. But it passes 23 and 3. I wonder why and how can I fix the problem. Any help is appreciated. Thank you.
Function execv expects an array of pointers to strings. What you provide matches the type but not the logic behind:
char *arg_vec[]={"./test", &shmp[1], &shmp[2],NULL};
The addresses you use to initialize the parameter array are only pointers to single char, no strings.
In shmp all elements only have 1 byte and they are located at consecutive memory addresses. There is no place for some terminating 0 byte that would make your array a valid string.
You need to provide and initialize more space:
(Assuming you can ensure that numbers are only 1 digit)
shmid = shmget(IPC_PRIVATE, 3 * sizeof(char[2]), 0666 | IPC_CREAT);
char (*shmc)[2] = shmat(shmid, NULL, 0);
sprintf(shmc[0], "%d", number[0]);
...
char (*shmp)[2] = shmat(shmid, NULL, 0);
printf("%s, %s\n",shmp[1], shmp[2]);
char *arg_vec[]={"./test", shmp[1], shmp[2], NULL};
I leave it for you to ensure proper lengths and adapt in case you need larger numbers.

"Turn" initialized memory into shared memory with the SYSV API?

To create a new and empty shared memory segment I can do something like:
int shmid = shmget(IPC_PRIVATE, nbytes, IPC_CREAT | 0777);
int* new_array = (int*)shmat(shmid, NULL, 0);
Now suppose int* array owns a chunk of pre-existing data. Something like
int* array = malloc(2 * sizeof(int));
array[0] = 42;
array[1] = 24;
I'd like to turn this memory segment into a shared memory segment with the System V shared memory C API, on Linux.
How can I do this?
I've tried stuff like
int shmid = shmget(IPC_PRIVATE, nbytes, IPC_CREAT | 0777);
void* attach = shmat(shmid, array, SHM_RND);
and like
int shmid = shmget(IPC_PRIVATE, nbytes, IPC_CREAT | 0777);
array = (int*)shmat(shmid, 0, 0);
but either shmat returns (void*)-1 or int* array gets all its values set to zero.
Here's a full program that shows the failures:
/*
gcc shm0.c -o shm0 && ./shm0
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#define NELEMENTS 3
void array_show(int* array, size_t nelements){
puts("");
for(int i=0; i < nelements; ++i)
printf("%d\n", *(array + i));
}
int main(){
const size_t nbytes = NELEMENTS * sizeof(int);
int* array;
posix_memalign((void**)&array, 16*16*16, nbytes);
int shmid = shmget(IPC_PRIVATE, nbytes, IPC_CREAT | 0777);
// 1st try...
void* attach = shmat(shmid, array, SHM_RND);
if(attach == (void*)-1)
puts("shmat() failed!");
// 2nd try...
memset(array, 1, nbytes); // Fill array with dummy data!
array_show(array, NELEMENTS);
array = (int*)shmat(shmid, 0, 0);
array_show(array, NELEMENTS);
if(array[1] == 0)
puts("array was reset!");
// 3rd try
array = (int*)shmat(shmid, array, 0); // Now accessing, say, array[0] yields a segfault!
shmdt(array);
}
Why SYSV shared memory and not mmap()? Because I'm using the X11 MIT shared memory extension, and, according to the docs:
... to be able to use this extension, your system must provide the SYSV shared memory primitives. There is not an mmap-based version of this extension. To use shared memory on Sun systems, you must have built your kernel with SYSV shared memory enabled ...
(If someone knows a way around this, or how to do this using shm_open(), I'd be delighted to hear about it.)

Child process can't access shared memory in C

I have a program where I want to set up a pointer to a struct as shared memory. I think I've set up the shared memory correctly in the main method; then I call a function to initialize the struct, and fork. However, the child process can't access the shared memory; the parent process works as expected, which isn't that big of a surprise. I know for sure that the child process executes and works, but it cannot access the shared memory, so the function doesn't really do much besides print out printf statements.
struct OverSharedData{
struct SharedData ** rep;
int rop;
};
void initialize( struct OverSharedData * bill){
bill->rep = (struct SharedData**)malloc(sizeof(struct SharedData*)*consumerthreads);
int on =0;
for (on=0; on<consumerthreads; on++) {
*(bill->rep+on) = (struct SharedData *)malloc(sizeof(struct SharedData));
init(*(bill->rep + on), on); //
}}
int main(int argc, const char * argv[])
{
databases(argv[1]); /* Takes care of setting up the database*/
categories(argv[2]); /*Takes care of setting up the book categories*/
bookorders = argv[3];
key_t key = ftok("garbage.txt", 71);
int eyedee = shmget(key, sizeof(struct OverSharedData ),
IPC_CREAT | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
if (eyedee == -1)
{
perror("shmget");
exit(1);
}
struct OverSharedData *remp = (struct OverSharedData *) shmat(eyedee, 0, 0);
if (remp == (void *) -1)
{
perror("shmat");
exit(1);
}
initialize(remp);
struct SharedData * d = *(remp->rep + 0);
printf("Hallo\n");
shmctl(eyedee, IPC_RMID, 0);
pid_t forkk = fork();
if (forkk==0) {
/*THIS DOES NOT WORK*/
printf("Entered consumer check: %d\n", remp->rop);
int z = 0;
pthread_t Consumer_Threads[consumerthreads];
for (z=0; z<consumerthreads; z++) {
remp->rop = z;
d = *(remp->rep + z);
d->da = z;
pthread_create((Consumer_Threads+z), 0, Consumer, d);
}
for (z = 0; z<consumerthreads; z++) {
pthread_join(Consumer_Threads[z], NULL);
}
shmdt(remp);
}
else{
/*THIS WORKS*/
printf("Entered Producer: %d\n",remp->rop);
pthread_t Produc;
pthread_create(&Produc, 0, Producer, remp);
pthread_join(Produc, NULL);
printf("Hey guys: %d\n", remp->rop);
shmdt(remp);
}
My guess is that I didn't initialize the struct correctly, but I'm not all too clear what I'm doing wrong. I left out some of the other initializing code but I figured since I can't even access the int in the OverSharedData struct, it's more of a matter where I can't access the struct in the first place.
The problem is that your shared data (the single OverSharedData object) contains pointers to non-shared data. You need to allocate all the data that you want shared in the shared memory segment, rather than with malloc. Something like:
static void *shared_available;
static size_t shared_left;
void init_shared(size_t size) {
key_t key = ftok("garbage.txt", 71);
int eyedee = shmget(key, size,
IPC_CREAT | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
if (eyedee == -1) {
perror("shmget");
exit(1); }
shared_available = shmat(eyedee, 0, 0);
if (shared_available == (void *) -1) {
perror("shmat");
exit(1); }
shared_left = size;
}
void *alloc_shared(size_t size) {
void *rv = shared_available;
if (size > shared_left) {
fprintf(stderr, "Ran out of shared memory!\n");
exit(1); }
shared_available = (char *)rv + size;
shared_left -= size;
return rv;
}
OverSharedData *initialize() {
init_shared(sizeof(struct OverSharedData) +
sizeof(struct SharedData *) * consumerthreads +
sizeof(struct SharedData) * consumerthreads)
OverSharedData *bill = alloc_shared(sizeof(OverSharedData));
bill->rep = alloc_shared(sizeof(struct SharedData*)*consumerthreads);
for (int on=0; on<consumerthreads; on++) {
bill->rep[on] = alloc_shared(sizeof(struct SharedData));
init(&bill->rep[on], on); }
}
The above will still have problems if the init routine tries to store pointers to non-shared memory into the SharedData struct (you don't show the definition of either, so we can't say).
If you want to be able to more flexibly allocate and manage shared memory across processes, you really need to use a general purpose shared memory allocator/manager, such as this

accessing a structure member in a shared memory "in C"

I am trying to write a code that shares a structure type, but im getting segmentation error when tryign to write in a structure member in the shared memory, the shared memory is between a parent and child process. as im showing in the code, im just tryin to access the struct member for now, so i can use semaphore later for synch.
Thanx in advance.
typedef struct file
{
char *shmPtr;
} file_entry;
int main (void)
{
int shmid;
int n;
file_entry *entries;
if (fork() == 0) {
/*wait for a while*/
if ((shmid = shmget(20441, sizeof(file_entry), 0666)) == -1) {
printf("shmget");
exit(2);
}
entries = (file_entry*) shmat(shmid, 0, 0);
if (entries->shmPtr == (char *) -1) {
printf("problem2");
exit(2);
}
printf("\nChild Reading ....\n\n");
printf("%s\n", entries->shmPtr[0]);
printf("%s\n", entries->shmPtr[1]);
putchar('\n');
printf("\nDone\n\n");
} else {
if ((shmid = shmget(20441, sizeof(file_entry), IPC_CREAT | 0666)) == -1) {
printf("problem3");
exit(2);
}
entries = (file_entry *) shmat(shmid, 0, 0);
if (entries->shmPtr == (char *) -1) {
printf("problem4");
exit(2);
}
printf("done attachment"); /*the parent prints this statment, then segmentation fault*/
entries->shmPtr[0]='a';
entries->shmPtr[1]='b';
putchar('\n');
wait();
shmdt(&shmid);
}
exit(0);
}
shmat returns a pointer to the shared memory area. In your code, after the call to shmat, entries points to the shared region. You are then treating the first few bytes of that shared area as a pointer to char (shmPtr). The value of shmPtr is uninitialized, and it points to some random location. Then you try to write to it and get a segfault.
Edit:
As Richard suggested, you could get rid of the struct and just use a char *. However, I'm guessing the reason you are using a struct and not just a char * is that you are planning to add some extra fields to the struct in the future. If that's the case, you can use a flexible array member:
typedef struct file
{
int flag;
int blah;
char shmPtr[];
} file_entry;
and the allocation becomes
shmget(20441, sizeof(file_entry) + bufsize, IPC_CREAT | 0666)
Of course, if the buffer size is fixed, you could just hardcode it:
typedef struct file
{
int flag;
int blah;
char shmPtr[BUFSIZE];
} file_entry;
/* ... */
shmget(20441, sizeof(file_entry), IPC_CREAT | 0666)

Passing data structure to another process using shared memory and saving to file

I am sending data(name, ph number & address) to another process using shared memory. I have to print data in the second process and store them to a file. I have tried this code but I am not receiving data in second process. Can someone help me with this.
Thank you.
address.c
typedef struct
{
char lname[25];
char fname[20];
char address[20];
char phonenumber[20];
}addressbook;
addressbook a;
char *shared_memory;
int main()
{
int select;
int segment_id;
char* shared_memory;
int segment_size;
key_t shm_key;
const int shared_segment_size = 0x6500;
shm_key = ftok("/home/madan/programs/shm_tok",'C');
if(shm_key < 0) {
printf("failed to create the key %s\n",strerror(errno));
}
/* Allocate a shared memory segment. */
segment_id = shmget (shm_key, shared_segment_size,
IPC_CREAT | IPC_EXCL | S_IRUSR | S_IWUSR);
if(segment_id < 0) {
printf("error geting the segment id %s\n",strerror(errno));
}
printf("segment ID:%d\n", segment_id);
/* Attach the shared memory segment. */
shared_memory = (char*) shmat (segment_id, 0, 0);
printf ("shared memory attached at address %p\n", shared_memory);
printf("enter lastname:\n");
gets(a.lname);
printf("enter firstname:\n");
gets(a.fname);
printf("enter address:\n");
gets(a.address);
printf("enter phone number:\n");
gets(a.phonenumber);
memcpy(shared_memory, &a, sizeof(a));
printf("data:%s\n", shared_memory);
system("./address-insert");
/* Detach the shared memory segment. */
shmdt (shared_memory);
/
* Deallocate the shared memory segment.*/
shmctl (segment_id, IPC_RMID, 0);
}
address-insert.c
typedef struct
{
char lname[20];
char fname[20];
char address[20];
char phonenumber[20];
}addressbook;
addressbook a;
int main ()
{
int segment_id;
char* shared_memory;
FILE *fp;
char *name;
int segment_size;
key_t shm_key;
shm_key = ftok("/home/madan/programs/shm_tok",'D');
const int shared_segment_size = 0x6500;
/* Allocate a shared memory segment. */
segment_id = shmget (shm_key, shared_segment_size,
S_IRUSR | S_IWUSR);
if(segment_id < 0) {
printf("error:[%s]",strerror(errno));
}
printf("segment id %d\n",segment_id);
/* Attach the shared memory segment. */
shared_memory = (char*) shmat (segment_id, 0, 0);
if(shared_memory == NULL) {
printf("failed to attach the shared memory %s",strerror(errno));
}
printf ("shared memory2 attached at address %p\n", shared_memory);
printf ("name=%s\n", shared_memory);
memcpy(&a, shared_memory, sizeof(a));
printf("name: %s\n", a.fname);
printf("address:%s\n", a.address);
printf("phone number=%s\n", a.phonenumber);
fp = fopen("filename","a+");
fwrite(a, 1, strlen(a),fp);
fclose(fp);
/* Detach the shared memory segment. */
shmdt (shared_memory);
return 0;
}
Why do you require two processes? If you want to do this you will also need a semaphone - so that the other process can be sure that all the data is in the shared memory. Perhaps a pipe (UNIX or otherwise) would be simpler.
In memcpy(&a, shared_memory, sizeof(a)); instead of passing the size of the variable a, try passing the size of structure address i.e. sizeof(addressbook).

Resources