LazyBear LazyBear - 2 months ago 5
C Question

Initialize a 1D or 2D array in shared memory

I am trying to initialize a 2D char array of strings into POSIX shared memory to be shared between 3 other processes. There are plenty of tutorials on how to use a pointer to share a single string or an integer between processes, but I could find no examples on how to initialize 1D or 2D arrays using

mmap()
. I have posted what I have so far below. It is the first program, which creates the shared memory object and initialize the array
char files[20][2][100]
with the value
files[0][0][0] = '\0'
.

What is the proper method to initialize and share an array in C?

For context, I coded a simple version of this project (at the suggestion of helpful S.O. gurus) which does not use shared memory and combines all 4 processes (separated by
/**********/
) into one. It's below.

I've asked a similar question involving structs in a previous post, but I was required to use a multidimensional array instead for my project. Any insights on that answer would be helpful HERE.

Thanks.

CODE SO FAR: (program initializes shared memory object and array)

#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <sys/mman.h>

int main (int argc, char *argv[])
{
char files [20][2][100];


/* the size of shared memory object */
int size = sizeof(files);

/* name of the shared memory object */
const char *name = "/PROJ4_SHARED_MEM";

/* shared memory file descriptor */
int shm_fd;

/* pointer to shared memory obect */
void *ptr;

/* create the shared memory object */
shm_fd = shm_open(name, O_CREAT | O_RDRW, 0666);

/* configure the size of the shared memory object */
ftruncate(shm_fd, size);

/* memory map the shared memory object */
ptr = mmap(0, size, PROT_WRITE, MAP_SHARED, shm_fd, 0);

/* save array to the shared memory object. */
/*****WHERE I LOSE IT*****/

return 0;
}


CONTEXT PROGRAM: (not POSIX)

#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>

int main (int argc, char *argv[])
{
char files [20][2][100];
files [0][0][0] = '\0';
char file_name[100];

char file_contents[100];
char search[100];

char answer;
int again = 0;
int counter;
int i = 0;

/**************************************************/

while (again == 0)
{
printf("Enter a filename: ");
scanf("%s", &file_name);
getchar();
printf("Enter file contents (string): ");
fgets (file_contents, 100, stdin);

for (i = 0; i < 20; i++)
{
if (files[i][0][0] == '\0')
{
strcpy(files[i][0],file_name);
strcpy(files[i][1],file_contents);
files [i+1][0][0] = '\0';
break;
}
else if (i == 19)
{
printf("ERROR: The directory is full.\n\n");
}
}

counter++;

printf("Save another file y/n ?: ");
scanf(" %c", &answer);

if (answer == 'n')
{
again = 1;
}

}
printf("\n\n");
again = 0;

/**************************************************/

printf("You have saved the following files:\n");
for (i = 0; i < 20; i++)
{
if (files[i][0][0] == '\0')
{
break;
}

printf("%s \n", files[i][0]);
}
printf("\n\n");

/**************************************************/

printf("Would you like to open a file? (y/n) ?: ");
scanf(" %c", &answer);
if (answer == 'n')
again = 1;

while (again == 0)
{
printf("Enter a filename: ");
scanf(" %s", &file_name);

for (i = 0; i < 20; i++)
{
if (strcmp(files[i][0], file_name) == 0)
{
printf("%s \n", files[i][1]);
break;
}
else if (i == 19)
{
printf("ERROR: The file was not found.\n\n");
}
}

printf("Search for another file (y/n) ?: ");
scanf(" %c", &answer);

if (answer == 'n')
{
again = 1;
}
printf("\n\n");
}


getchar();

return 0;
}

Answer

The code has a pointer to the shared memory therefore, like with any pointer, the code can access any specific item in the shared memory by indexing off that pointer.

#define INDEX_1_SIZE (20)
#define INDEX_2_SIZE (2)
#define INDEX_3_SIZE (100)
#define SIZE         (INDEX_1_SIZE * INDEX_2_SIZE * INDEX_3_SIZE)
ptr = mmap(0, SIZE, PROT_WRITE, MAP_SHARED, shm_fd, 0);

then:

int i, j, k; // loop counters

// initialize the shared memory to '\0'
for(i=0;i<INDEX_1_SIZE; i++_
{
    for(j=0; j<INDEX_2_SIZE; j++)
    {
        for(k=0;k<INDEX_3_SIZE; k++)
        {
            ptr[i][j][k] = '\0';
        }
    }
}

there are faster ways to perform the initialization to '\0' for instance:

memset( ptr, '\0', SIZE );

There is no need for the local variable: 'files'

However, since a number of different processes can access the shared memory at the same time,
the code needs a named semaphore that all the processes use so only one process is accessing the shared memory at any one time, to avoid 'race' conditions. here is a good discussion, with example usage, for semaphores http://www.cs.cf.ac.uk/Dave/C/node26.html the code should be checking for errors, something like this: ( of course, using your own variable names )

des_mutex = shm_open(MUTEX, O_CREAT | O_RDWR | O_TRUNC, mode);

if (des_mutex < 0)
{
    perror("failure on shm_open on des_mutex");
    exit(1);
}

if (ftruncate(des_mutex, sizeof(pthread_mutex_t)) == -1)
{
    perror("Error on ftruncate to sizeof pthread_cond_t\n");
    exit(-1);
}